So, if I create an AlertDialog like so:
AlertDialog.Builder b = new AlertDialog.Builder();
b.setItems(MyStringArray, MyListener);
b.create().show();
And then I want to update the items in the list, i.e. MyStringArray has changed to have more or fewer items. I can't seem to find a way to do this. So far, I've tried getting the ListView from the AlertDialog, but I can't seem to get .setAdapter to work. Is this the right approach, or is there a better way to do this?
I haven't tried this out myself, but from all the other apps I've built I'm pretty sure this will solve your problem.
Instead of using setItems, try using the setAdapter() method and pass in an ArrayAdapter that has been initialized with the data from your Array of String. Then, when you know that the data has changed, you can use getListView() to get your View object and from there call getAdapter() so that now you're working directly with the dataset. You can clear it, and re-initialize it if you like, or just add / remove the items as you like. From the adapter object, if you call notifyDataSetChanged() it should trigger a re-draw using the new data set that you just supplied to the adapter.
Hope that helps you out. Let me know if it doesn't.
DSC
If you are like me and you would like to use default adapter for example for multichoice items, then there is also a way.
Just as with any other adapter just update the string array object, get adapter from the dialog instance, cast it to appropriate adapter and invalidate it.
AlertDialog.Builder b = new AlertDialog.Builder();
b.setItems(MyStringArray, MyListener);
AlertDialog instance = b.create();
instance.show();
// Later when you need to update
MyStringArray[0] = "puf";
ListView list = instance.getListView();
// Now according to whether you used cursor or array for supplying items to the builder
// you have to cast adapter to either CursorAdapter or ArrayAdapter
ArrayAdapter adapter = (ArrayAdapter)list.getAdapter();
adapter.notifyDataSetChanged();
You can find out more here.
This is how I did it in Kotlin:
AlertDialog.Builder(context).apply {
val actions = arrayListOf("aa", "bb")
val onItemClickListener = DialogInterface.OnClickListener { dialog, which ->
// code
}
val adapter = ArrayAdapter(requireContext(),
android.R.layout.simple_list_item_1, actions)
setAdapter(adapter, onItemClickListener)
executeGetRequest() {
actions.add("cc")
adapter.notifyDataSetChanged()
}
}.show()
Related
I have a Toolbar and an item (add) which, when clicked, adds a view in listView below. However, the onOptionsItemSelected gives you the effect of a single click so it only adds one view, and in my case, I need multiple views, thus multiple clicks are required. How do I set up everything so that the item behaves as an onClickListener rather than a single click?
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.addButton){
final TextView noProject = (TextView) findViewById(R.id.NOPROJECT);
final ArrayList<String> listItems=new ArrayList<String>();
final ListAdapter addAdapter = new ArrayAdapter<String>(this,
R.layout.list_item, R.id.listFrame, listItems);
final ListView lv = (ListView) findViewById(R.id.lv);
lv.setAdapter(addAdapter);
noProject.setVisibility(View.GONE);
lv.setVisibility(View.VISIBLE);
listItems.add("New Project");
((ArrayAdapter) addAdapter).notifyDataSetChanged();
}
if (id == R.id.addPeople) {
return true;
}
return super.onOptionsItemSelected(item);
}
Android is always listening for menu item clicks. And on click your action will happen, so you'll need to click multiple times anyways if you want this add feature in the menu.
I usually setup my list adapter in onCreate or onCreateView. Once it's established you can do addAdapter.clear() and addAdapter.add(item). You shouldn't need to reference your listitems directly since the ArrayAdapter.add() method is setup to append to that list anyways and then if i'm not mistaken you can get rid of notifyDataSetChange() - I've never had to use this method with any of the default list adapters or the custom adapters I've written. .clear(), .add(), .insert(), and .remove() should be sufficient.
My listview is usually filled out using a for loop. If you want multiple views added then could you just setup a loop instead of waiting/requiring for more clicks?
Maybe I'm not fully understanding the usecase but a basic for loop seems like the answer here.
Edit:
//For Each Loop - "For each individualItem in itemHolder"
listadapter.clear();
for(ItemType individualItem : itemHolder){
listAdapter.add(individualItem.getText());
}
or you can do a traditional for loop
//"For i(index) starting at index 0, run until index < itemHolder.getItemCount() is false"
//for(initialize index variable : condition check : increment after each iteration)
for(int index =0; index<itemHolder.getItemCount(); index++)
{
listAdapter.add(itemHolder.getItemAt(index));
}
Something like that. I made up method names obviously it's going to depend on your data structures.
//first i have this method , below is my question
public void addrows(){
TableRow fila;
tabla = (TableLayout)findViewById(R.id.tabla);
TextView txtNombre;
for(int i = 0;i<id;i++){
String x[] = helper.leer();
layoutFila = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT);
caja= new CheckBox(this);
fila = new TableRow(this);
fila.setLayoutParams(layoutFila);
txtNombre = new TextView(this);
txtNombre.setId(i);
txtNombre.setTextSize(17);
txtNombre.setText(x[i]);
txtNombre.setGravity(Gravity.LEFT);
// txtNombre.setLayoutParams(layoutTexto);
caja.setText("");
caja.setId(i);
fila.addView(txtNombre);
fila.addView(caja);
tabla.addView(fila);
}
}
i know that when the oncreate() method start the checkboxes objects are created and then i assign an numerical id from 0 to wherever the for cycle stop , but later in the program i need to retrieve what checkboxes were clicked so first i need the id but eclipse wont let me put the numerical id, please help! and sorry for my English i'm a noob in android and the English language
this.CheckBox = (CheckBox)this.findViewById(R.id.?);
As You may read in View class documentation ID should be unique within a tree You search.
You set same id for TextView and Checkbox.
If You know You are going to access them all later after creation keep references to them in array instead of trying to retrieve them later using findViewById.
But even better solution would be to set onClick event listener for them and keep track of checking/unchecking them.
In #HalR's answer You may read how to set onCheckedChanged event listeners for Your checkboxes. Folowing his solution will have an ArrayList of checked checkboxes.
Next step, You have to increment values of correct TextView so You need to couple CheckBoxes and TextViews.
I think best for this would be to set Tag for CheckBox with value of TextView id.
So after user submits You iterate over List of checkboxes, getTag and use it in findViewById to get TextView and update its value.
Id (short for IDentifier) is an integer to uniquely identify elements, You can use it in findViewById to get view elements. You can read more about ID in this answer
Tag is used to associate View element with some extra data as You may read in getTag documentation. It takes as parameter Object type so You set as tag anything not only numbers. In Your case You could set as ChechBox's tag a TextView instead of its id and it will work too.
You are manually setting your id to the index of the row, which is something I don't think I'd do. I'd normally use setTag() to identify my object.
I think it would be easier to use a listener to detect when the checkboxes have been checked, and you can track the changes when the check happens.
use something like this:
In your Activity, create a ArrayList
ArrayList<CheckBox> checkedBoxes = new ArrayList<CheckBox>();
then in your creation:
caja= new CheckBox(this);
caja.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
int index = buttonView.getId();//pulling back the number you set originally, if that is what you like. I would get the tag I had set, and maybe do something here.
//do something in here!
if (buttonView.isChecked()) {
//including this checkbox in the list of checked boxes
checkedBoxes.addObject(buttonView);
} else {
//take it out of the list of checked boxes
checkedBoxes.removeObject(buttonView);
}
}
};
Some info on Id vs Tag
Id is a numeric value that identifies the view in the view hierarchy. If you are using things in your layout, like aligning one view with another, they look for and expect a view with a specific id. So in layout, you'll have android:id="#+id/bigBox" and that will create some number that it associates with bigBox. When you find that view, with findViewById() that is the number it is looking for. When you manually set those numbers, it seems like you are asking for trouble. If you set a view's id to 2, then you should be able to find it with myView = findViewById(2).
Tag is a nicely little object pointer that you can pass along with your view. Quite often it will be a row number:
Integer saveMe = new Integer(i);
checkBox.setTag(saveMe);
Or it can even be a pointer to your original data object that you used to create that row. If you had created each row using a contact, you could use
myRow.setTag(contact)
and later when you clicked on that row, you would just use
contact = (Contact)myRow.getTag()
and you would have your original contact back. Its way cleaner than keeping big arrays of your rows or checkboxes, or whatevers. Just use listeners that detect when you do something, that is a much better way.
Oh, and if you if you do have an onClick(View view) that is triggered by your CheckBox, that view IS your CheckBox.
CheckBox theBoxIJustChecked = (CheckBox)view;
You don't need to look it up with some id. It's right there.
If you want to go this way than you should just do the apposite operation i.e.:
for(int i = 0; i < n; ++i){
...
...(CheckBox))this.findViewById(i);
...
}
It should work for you
However be careful as if you have number of views with the same id inside the view-tree than findViewById(i) can return an unexpected result such as returning the first view in view-tree with given id (it can be not of CheckBox type which can lead to ClassCAstException)
Update in reply to comment
If you want to make some sort of logical connection CheckBox-TextView there are several options:
You can make a sort of function like the following (assuming that there is the limit of CheckBoxes and TextViews quantity):
Code:
private static int CHECK_BOX_MAX_NUMBER = 10000;
public void int getTextVieIdByCheckBoxId(int checkBoxId){
if(checkBoxId >= CHECK_BOX_MAX_NUMBER){
// you can throw an exception here for example
}
return CHECK_BOX_MAX_NUMBER + checkBoxId;
}
And then you should set id's to your TextViews with that function.
checkBox.setId(i);
textView.setId(getTextVieIdByCheckBoxId(i));
....
// add Views to your layout
....
(CheckBox)this.findViewById(i);
TextView)this.findViewById(getTextVieIdByCheckBoxId(i));
or
2.I think there is a little bit more accurate method:
Just use setTag() of CheckBox instances to set appropreate TextView inside in order to create interconnection. In thiscase you have to store all the created checkBoxes in some List or array:
List<CheckBox> checkBoxList = new ArrayList<CheckBox>();
for(int i = 0; i < n; ++i){
...
CheckBox checkBox = new CheckBox();
TextView textView = new TextView();
checkBox.setTag(textView);
checkBoxList.add(checkBox);
}
Then you can achieve what you want like this:
int textBoxListSize = checkBoxList.size();
for(int i = 0; i < textBoxListSize; ++i){
CheckBox checkBox = checkBoxList.get(i);
if(chechkBox.isChecked()){
TextView textView = (TextView)checkBox.getTag();
//do whatever with textView
}
}
Here you don't need to generate id's and worry about collisions which could accure
I am working in Android. I want to design a spinner of song categories.
This is my code:
public Spinner spinner_category_forSong;
String[] arr_Category={"Select","sad","dj","rock"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this,android.R.layout.simple_spinner_item,arr_Category);
spinner_category_forSong = (Spinner)findViewById(R.id.Spinner_category_forUpload);
spinner_category_forSong.setPrompt("Music Category :");
spinner_category_forSong.setAdapter(adapter);
But whenever I run my project, a null pointer exception is created in spinner_category_forSong.setPrompt("Music Category :"); and spinner_category_forSong.setAdapter(adapter);.
Please tell me what mistake I have made in this code.
From your exception it seems that findViewById(R.id.Spinner_category_forUpload) returns null. My guess is that you didn't call setContentView() prior to your method calls.
From the code sample your class structure is not clear. Are some lines inside onCreate()?
try after setting adapter spinner_category_forSong.setAdapter(adapter); before spinner_category_forSong.setPrompt("Music Category :");
I am trying to populate a Spinner in an Android dialog. When the user clicks a button, a dialog pops up and I load the layout from an XML file. Now I am trying to populate that Spinner from a SQL query. I have searched all over and cannot figure out what the problem is. I can loop through the Cursor and add each value to an ArrayAdapter and then use that as the list for the spinner but that doesn't come with the _id's from the database. I have done this before using a SimpleCursorAdapter and I have even copied and pasted my old code exactly and still it isn't working. Any help would be much appreciated.
My Dialog code:
private void displayNewInteractionDialog() {
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.mm_new_dialog);
dialog.setTitle(R.string.mm_new_dialog_title);
dialog.setCancelable(true);
final Spinner spinner = (Spinner) dialog.findViewById(R.id.mm_spinner);
DatabaseInterface db = new DatabaseInterface(this);
db.open();
Cursor c = db.getNames();
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_spinner_item,
c,
new String[] {DatabaseInterface.KEY_ID, DatabaseInterface.KEY_NAME},
new int[] {android.R.id.text1});
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
c.close();
db.close();
dialog.show();
}
Here is the code that returns a Cursor in my DatabaseInterface class:
public Cursor getNames() {
return db.query(DATABASE_TABLE_4, new String[] {DatabaseInterface.KEY_ID, DatabaseInterface.KEY_NAME}, null, null, null, null, null);
}
I know this will work I am just missing something apparently. I know that I could load the Spinner with the list of names from the ArrayAdapter that I stated above and also populate an array with the ID's from the query and when the user selects an item I could just grab the corresponding one from the array of ID's... But I know the SimpleCursorAdapter will work I just can't seem to figure it out and I'm not giving up until I do haha. Thanks in advance for any help.
Well looks like I am dumb haha... For some reason or another, probably from just trying numerous things and then forgetting to remove parts that I had added. I just removed c.close(); and now everything is fine. I remember adding that as I was trying to figure things out and I must've fixed the problem somewhere else and then didn't realize it because this line was still in there. Since I am not actually doing anything with the Cursor other than passing it to a function, it doesn't need to be closed. Anyhow all is good, I knew it was going to be something stupid. Like usual.
why I am not able to refresh/reload my ListView?
I' ve written an app, which read out html-websites, saves the information in a String-Array an present it in my ListView.
But every time I close and re-open the app the new content append to the existing content.
In the first run I get, e. g., "1 2 3 4" and in the second run "1 2 3 4 1 2 3 4" and then " 1 2 3 4 1 2 3 4 1 2 3 4" and so on.
I google a lot and find the methods to clear an ArrayAdapter (aa) and refill it with new data
-> aa.clear() / aa.setModifyDataChanged() / aa.remove(String Object) / aa.add(String)
but every time i call one Method my app does a force close and LogCat shows the exception: java.lang.UnsupportedOperationException.
Why? This really grind my gears. The whole saturday afternoon I try to fix it -without success...
Maybe somebody could help me!?
Here is my code-snippet
public class viewband extends ListActivity
{
private static final int AKTUALISIEREN = 0;
private static ArrayList<String> al = new ArrayList<String>();
static String[] TST;
static ArrayAdapter<String> ad;
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case AKTUALISIEREN:
getIt();
//here a ad.close does a force close
setListAdapter(ad);
ListView lv = getListView();
lv.setTextFilterEnabled(true);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getIt();
ad = new ArrayAdapter<String>(this, R.layout.list_item, TST);
setListAdapter(ad);
// here, when I try ad.clear() my app does a force close
ListView lv = getListView();
lv.setTextFilterEnabled(true);
}
public static void getIt ()
{
// Here I get the source-code of the html-site and parse my content
// All necessary Information I write in my String Array TST, declared above in a static way
}
I hope, somebody could help me...
Many thanks and a nice sunday.
You need to back your ArrayAdapter by an ArrayList or List rather than a fixed-size array - the size of a fixed-size array cannot change nor can it be cleared (without re-creating it, which defaults the whole purpose)
So set the content of your adapter to be al rather than TST (on a side note you really need to name your variables with sensible names). Then call al.clear(), al.add(String), to modify the dataset backing the adapter. Once the dataset has been changed call ad.notifyDataSetChanged - this will then cause the list adapter and list to update the display.
Also your adapter should not be static (nor should TST or al probably) -- this will lead to memory leaks since the Adapter holds onto a reference to the current Context. NEVER make anything with a context static (drawables, adapters etc). Unless you know why you want something to be static keep it as a normal variable.
clear TST all time you update your listview