I'm using the following code to populate listview
Oncreate method:
SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.more_custom_row_view, new String[] {"title","desc"}, new int[] {R.id.text1,R.id.text2});
populateList();
setListAdapter(adapter);
static final ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
private void populateList() {
HashMap<String,String> temp = new HashMap<String,String>();
temp.put("title","Previous Searches");
temp.put("desc", "View your search history");
list.add(temp);
HashMap<String,String> temp1 = new HashMap<String,String>();
temp1.put("title","Settings");
temp1.put("desc", "Update your account settings");
list.add(temp1);
}
when i go to another activity and come back to this acitivity it duplicate list items
each time any one guide me what mistake am i doing?
The problem is that you are using a static list for your items and not clearing or reinstantiating it in your onCreate method.
The list will stay in memory as long as your program is running because it is defined static. If you are returning to the activity the items are again added to the list. You could use clear() to remove all items from the list in your populate list call before filling it again.
Simple, check adapter.isEmpty() before populating
This is a snippet from my own codebase.
private void populateWithAppPackages() {
//DON'T continue if the adapter is not empty; prevents duplicates
if(!mAdapter.isEmpty()){
return;
}
/* ... populate the list ... */
}
DON'T clear() the list adapter!!
clear() and repopulating list every time the activity or the fragment are visited is a very expensive set of operations. You would be better of intelligently populating the list as above.
removing final from the list resolved my problem.
Related
In Android, you can provide an ArrayList when creating an ArrayAdapter for a ListView. I need to update a number of items in the ArrayList.
The ususal way is to just call notifyDatasetChanged. What I prefer to do is reload the entire data into a new ArrayList from my database and then apply this new ArrayList to the existing ArrayAdapter but without creating a new adapter. Creating a new adapter will cause the ListView to go blank and start with position zero. This would also be obvious if the user were scrolling and I suddenly recreated a new adapter.
Is it possible to apply a completely new ArrayList to the existing adapter? The primary reason I want to do this is because it is very fast to just reload a new ArrayList with all the data than having to go through an existing ArrayList and inserting, deleting or updating existing items.
1) First of all. You need to use custom ArrayAdapter (or SimpleAdapter or RecycleViewAdapter)
2) Than create function in your custom adapter :
// Initialize your list;
private ArrayList<Model> arrayList;
.....
//Create constructor and past starting ArrayList
public MyAdapter (ArrayList<Model> array) {
this.arrayList = array;
}
......
public void updateMyData (ArrayList array) {
clear();
// Or you can use arrayList.addAll(array); - just add new items
this.arrayList = array;
notifyDataSetChange();
}
.....
3) In your Activity (or where you initialize adapter)
just use code like this:
MyAdapter myAdapter = new MyAdapter(startArratListData);
// And Than for updating data:
myAdapter.updateMyData(newArrayListData);
I'm attempting to get method to handle updates to the ListView when SetWeatherData is called. Nothing ever shows up in my listview below. Any ideas? _rootView points to the right root and ListView comes back not null. m_weatherdata has a couple string elements in it.
Note the initial set of data does not show up either. Just blank.
I'm thinking it should be easier to setup a generic method to update a ListView when the data changes using straight up code.
private ArrayList<String> m_weatherdata;
private void SetWeatherData ( ArrayList<String> _weather)
{
m_weatherdata = _weather;
UpdateWeatherUI();
return;
}
ArrayAdapter<String> m_adapter = null;
private void UpdateWeatherUI()
{
if ( m_adapter == null ) {
m_adapter = new ArrayAdapter<String>(
this.getContext(),
R.layout.list_item_forecast,
R.id.list_item_forecast_textview,
m_weatherdata);
View _rootview = this.getLayoutInflater(null).inflate(R.layout.fragment_main, null, false);
ListView _listview = (ListView) _rootview.findViewById(R.id.listview_forecast);
_listview.setAdapter(m_adapter);
}
else
{
m_adapter.notifyDataSetChanged();
}
}
You are assigning a new ArrayList to your dataset.
m_weatherdata = _weather;
Instead add items to the dataset. Like this
m_weatherdata.addAll(_weather);
private void SetWeatherData ( ArrayList<String> _weather)
{
m_weatherdata.addAll(_weather);//change here
UpdateWeatherUI();
return;
}
When you set an adapter there is an observer attached to the
underlying data. So notifyDatasetChanged() only works if you only
modify the data in it.
If you want to clear all data from your dataset before adding new items to it, use the clear() method of ArrayList
private void SetWeatherData ( ArrayList<String> _weather)
{
m_weatherdata.clear();//change here
m_weatherdata.addAll(_weather);//change here
UpdateWeatherUI();
return;
}
m_weatherdata = _weather; // updates the local variable with new set of data. but adapter doesn't know about the changes made as you have created the instance of the adapter with array list by the following line of code.
m_adapter = new ArrayAdapter<String>(this.getContext(), R.layout.list_item_forecast,R.id.list_item_forecast_textview,m_weatherdata);
In you want to updated the data either call
m_adapter.addAll(newsetofstringtobeadded);
or
Create new adapter
This will update your list.
I have an AlertDialog with a ListView set to multiple selection on it. It also has a Button on it.
The Button open another AlertDialog that if ok'ed will remove the selected items from the data set of the ListView, and then tell the adapter of the list view that the dataset has changed with the notifyDataSetChanged() method.
This all works fine except for one thing. The ListView does not update it's content until I interact with something. Then it updates to the correct data.
This is not a big problem, but I really would like the ListView to appear correct at once, and not just after the focus has changed.
Code:
Button remove = (Button) view.findViewById(R.id.btn_remove_questions_edit_rack);
final Context con = this;
remove.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Builder warnBuild = new Builder(con);
warnBuild.setMessage(R.string.question_deletion_warning);
warnBuild.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
SparseBooleanArray checked = list.getCheckedItemPositions();
for (String s : keys)
{
int i = keys.indexOf(s);
if (checked.get(i))
{
toRemove.add(map.get(s));
map.remove(s);
}
}
keys.clear();
keys.addAll(map.keySet());
((ArrayAdapter) list.getAdapter()).notifyDataSetChanged();
list.clearChoices(); //This makes sure the selection is cleared, if it isn't, some of the other items (those that now has the index of the selected items) will be selected when the View refreshes.
dialog.dismiss();
}
});
//Negative button here, not relevant.
}
});
Where map and keys are:
final HashMap<String, QualityQuestion> map = new HashMap<>();
//I add items to the map
final ArrayList<String> keys = new ArrayList<>(map.keySet());
And toRemove is where I store the items to be removed from the actual object they are on when the ok button on the original AlertDialog is pressed.
This is how I populate my ListView in the first place:
final ListView list = (ListView) view.findViewById(R.id.list_questions_edit_rack);
list.setAdapter(
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_activated_1,
keys));
I have tried things like list.invalidateViews(), list.invalidate and other things I found in questions similar to mine here on SO. But none of that made any difference. I suspect my problem to be different from theirs since my items clearly are updated, it just takes a change of focus on the original AlertDialog for the change to be visible.
How can I make the ListView show the changes in it's data source imidiatly insted of after a focus change?
By calling
((ArrayAdapter) list.getAdapter()).notifyDataSetChanged();
you get a fresh adapter which is almost certainly not identical to the anonymous adapter you used to populate your list in the first instance.
See also the documentation for ListView.getAdapter()
Returns the adapter currently in use in this ListView.
The returned adapter might not be the same adapter passed to setAdapter(ListAdapter) but might be a WrapperListAdapter.
From the point of view of this fresh adapter, the data set hasn't changed because the changes happened way before it was instantiated.
To solve your problem, make your list and your list adapter members of your activity class (or the scope where you want to keep them alive):
private ArrayList<String> keys;
private ArrayAdapter myAdapter;
private ListView list;
Then in your "onCreate()"
keys = ...; // initialization of ArrayList with the needed data
myAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_activated_1,
keys);
list = (ListView) view.findViewById(R.id.list_questions_edit_rack);
list.setAdapter(myAdapter);
This way, in your "OnClickListener" you can notify "myAdapter":
keys.addAll(map.keySet());
myAdapter.notifyDataSetChanged();
Hope this helps :)
You can tweak it, by granting focus to another view, and then requesting it back:
view.requestFocus();
You can also use:
view.requestFocusFromTouch();
In one of my activity I have EditText, Submit Button and a ListView. The data of ListView are retrived from database. To retrive data from database and adapt to ListView I used the following code.
private void loadList() {
mylist = new ArrayList<HashMap<String, Object>>();
mylist.clear();
List<Data> catDesc = dbhelper.getCatMasterDesc(id);
ArrayAdapter<Data> adapter = new SimpleAdapter(getApplicationContext(), R.layout.list, catDesc);
lv1 = (ListView) findViewById(R.id.masListView1);
lv1.setAdapter(adapter);
}
And i call this method every time whenever I update the database to show updated listview.
String str = category.getText().toString();
yes.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
dbhelper.UpdateMasterDesc(str);
loadList(); // see here.
}
});
This is the way I am updating my ListView. I think this is not a good way. If yes means please suggest me how can I update my ListView Whenever I update the database.
Thank You.
notifyDataSetchanged() is the answer as proposed by Raghunandan.
But before calling it, you need to update your object data which has been included in your list of catDesc.
Judging from your code I believe what
dbhelper.UpdateMasterDesc(str);
does is adding a new category? If yes, do a
catDesc.add(new Data(whatever you need to declare it with str))
before calling notifyDataSetChanged().
Is it possible to update a SimpleAdapter? I have a list of data and a footer that says "See Next Results" When that list item is clicked I capture the event and get new data. I then want to replace the data in the ListView with this new data but I can't figure out how to do it. Any Ideas? I don't want to use an ArrayAdapter, cause as far as I can see the items can only hold one string where I need it to hold multiple strings and ints.
Update: According to del116, you can indeed give SimpleAdapter a mutable map and then manually call the adapter's notifyDataSetChanged method when you need the list to update. However, my point below stands about the documentation of SimpleAdapter specifying that it is for static data; using it for mutable data is going counter to its design, so if you use this technique I would be sure to check on whether it continues to work in new Android releases as they emerge.
(Original commentary follows:)
If you look at the SimpleAdapter description it says it is "An easy adapter to map static data to views defined in an XML file." I've added the emphasis -- put simply, SimpleAdapater isn't built for use with data that changes; it handles static data only. If you can't use an ArrayAdapter because your data has more than a single bit of text, then you will either have to build your own custom ListAdapter, or put your data in a DB and use one of the CursorAdapters.
As a last resort, if you don't need much performance, you could update a ListView backed by a SimpleAdapter by building a whole new SimpleAdapter instance any time your data changes and telling the list view to use it via setListAdapter.
ListView lv= (ListView) findViewById(R.id.loglist);
ArrayList<Map<String, String>> list = buildData();
String[] from = { "time", "message" };
int[] to = { R.id.logtime, R.id.logmessage };
adapter = new SimpleAdapter(getApplicationContext(), list,R.layout.log_list_row, from,to);
lv.setAdapter(adapter);
Call this function each time to update the ListView. Keep in Mind that You have to update the list variable with new Data..
Hope this Helps..:)
SimpleAdapter is meant for static data, so your performance may vary. The best solution is probably to switch to a different type of adapter, such as ArrayAdapter, or make a new SimpleAdapter every time you change the dataset.
I was not able to get notifyDataSetChanged() to work on updating my SimpleAdapter, so instead I tried first removing all views that were attached to the parent layout using removeAllViews(), then adding the ListView, and that worked, allowing me to update the UI:
LinearLayout results = (LinearLayout)findViewById(R.id.results);
ListView lv = new ListView(this);
ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
SimpleAdapter adapter = new SimpleAdapter( this, list, R.layout.directory_row,
new String[] { "name", "dept" }, new int[] { R.id.name, R.id.dept } );
for (...) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", name);
map.put("dept", dept);
list.add(map);
}
lv.setAdapter(adapter);
results.removeAllViews();
results.addView(lv);