I'm confused how the actually ArrayAdapter works? As I was testing with ArrayAdapter and read about it that I have to call the notifyDataSetChanged(); on adapter or update the listView's adapter (as listView.setAdapter()) to update the record in ListView.
Now check this code.
public class MainActivity extends AppCompatActivity {
ArrayList<String> list = new ArrayList<>();
ExampleArrayAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView listView = (ListView)findViewById(R.id.listView);
adapter = new ExampleArrayAdapter(this,list);
listView.setAdapter(adapter);
// Here I'm adding record after the listView.setAdapter(adapter);
// it is working fine.
list.add("Good");
list.add("Bad");
}
public void addData(View view){
// but when I call this method from Button then it doesn't working.
list.add("New Data Added");
}
}
I don't think so there is any difference between these lines.
list.add("Good");
list.add("Bad");
and
list.add("New Data Added");
Both are adding record after the setAdapter();
Then why list.add("New Data Added"); is not working.
After onCreate() by activity lifecycle run onStart() and onResume(). draw is after onCreate(). Therefore 2 items are visible.
addData(View view) runs after view is visible. To refresh values you need at this place adapter.notifyDataSetChanged();
Related
I noticed two patterns for handling a List Adapter's notifyDatasetChanged(). I was wondering what differentiates the two and if one is better than the other.
First
Holding a local variable inside the Activity/Fragment for the dataset.
private List<Movie> movieList;
private SwipeListAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
movieList = new ArrayList<>();
adapter = new SwipeListAdapter(this, movieList);
And then when the local variable changes, call this in the Activity/Fragment:
adapter.notifyDataSetChanged();
Second
Passing an empty DatasetSet array to the adapter in the Fragment/Activity.
EmployeeAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAdapter = new EmployeeAdapter(this, new ArrayList<Employee>());
ListView employeeListView = (ListView) findViewById(R.id.employees);
employeeListView.setAdapter(mAdapter);
}
and when the data is modified or retrieved call this:
mAdapter.setEmployees(data);
which represents this method in the Adapter class itself:
public void setEmployees(List<Employee> data) {
employees.addAll(data);
notifyDataSetChanged();
}
The second option can be nice as it encapsulates the logic on how to properly add items to the adapter. If you do this in multiple places you will not forget to call notifyDataSetChanged() after updating the data as an example.
However, there is also a third option. You can add and remove items directly on the ArrayAdapter and by default this will call notifyDataSetChanged() internally.
The ArrayAdapter has the following methods to modify its elements and calling these methods will internally call notifyDataSetChanged() as long as you do not call setNotifyOnChange(false).
addAll(Collection<? extends T> collection)
addAll(T ... items)
insert(T object, int index)
remove(T object)
clear()
So for the second example you really don't need the setEmployees() method as you can do mAdapter.addAll(data).
Here is my scenario:
I have a MainActivity and a CustomListViewActivity. In my MainActivity, I have 1 button and 1 spinner. On click of the button, I pass the selected spinner value to the CustomListViewActivity via Bundle and using Intents.
Now, in my CustomListViewActivity, I have a ListView that uses ArrayAdapter for populating it. I send a ArrayList from MainActivity, say for example
items = [abc]
In my CustomListViewActivity, I receive the same and use it to populate my ListView. The first time I do this, my value gets populated. The second time I do the same, the value existing is now replaced with the new one and the ListView shows one item instead of showing two.
So basically the problem is that the ListView is not updating and not showing both the items. Instead it shows me a single item always.
Here are snippets of my code
MainActivity.java
//code inside button click
..
{
items.add(spinner1.getSelectedItem().toString());
Bundle bundle =new Bundle();
bundle.putStringArrayList("data",items);
Intent i = new Intent(MainActivity.this,MyListViewActivity.class);
i.putExtras(bundle);
startActivity(i);
finish();
}
protected void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
Log.i("App","onSave");
icicle.putStringArrayList("data",items);
}
#Override
protected void onRestoreInstanceState(Bundle icicle2) {
// TODO Auto-generated method stub
super.onRestoreInstanceState(icicle2);
Log.i("App","onRestore");
icicle2.putStringArrayList("data",items);
}
MyListViewActivity.java
private ArrayList<String> myItems;
private static String[] titles;
CustomListViewAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_main);
listView = (ListView) findViewById(R.id.list1);
rowItems = new ArrayList<RowItem>();
adapter = new CustomListViewAdapter(this,R.layout.list_item, rowItems);
listView.setAdapter(adapter);
b=getIntent().getExtras();
if(b!=null)
{
myItems=b.getStringArrayList("data");
titles= new String[myItems.size()];
titles = myItems.toArray(myItems);
}
...
int i=0;
while(i<titles.length) {
item = new RowItem(titles[i]);
//rowItems.add(item);
Log.i("ROW", ""+rowItems.size());
i++;
adapter.add(item);
listView.setAdapter(adapter);
}
adapter.setNotifyOnChange(true);
adapter.notifyDataSetChanged();
}
}
How to make the ListView maintain the current data as well reflect the added data?
Thanks in advance
EDIT : Forgot to mention one more thing. In my MyListViewActivity class I have a button above the ListView that on clicked takes me to my MainActivity so that I can add a new Item again. So when I go back to MainActivity and try and add a new Item , it's the new Item that get displayed rather than showing both the previous and the new one
You need to persist and restore your items list in MainActivity's onSaveInstanceState and onRestoreInstanceState methods.
You also probably don't need to close the MainActivity by calling finish() every time you start the ListView activity but that depends on your application.
Basically, the problem you are having is that items is being recreated with each new instance of MainActivity. Since you finish MainActivity, a new instance is used every time you access that activity (and I assume you thought that items would just keep getting items added to it).
I saw that there were many problems regarding notifyDataSetChanged(). I've looked through many of them and none of the solutions worked for me.
I am using ArrayList to set my list, and after my ArrayList is updated, i ran notifyDataSetChanged(). The new list is added onto the previous one.
lets say first list is a,b and new list is a,b,c. what i get in the end is a,b,a,b,c.
and each time updating this happens again with the new list.
I've tried other such as invalidate(), adapter clear(), refreshDrawableState() etc and nothing worked.
Thank you in advance.
Here is the simplified code, a note that changing MainActivity extends Activity to ListActivity crashes the program even after i change the code in .xml file.
public class MainActivity extends Activity
{
ArrayList<String> names = new ArrayList<String>();
ArrayAdapter arrayAdapter = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView lv1 = (ListView) findViewById(R.id.listview);
arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, names);
lv1.setAdapter(arrayAdapter);
}
//code here to edit the ArrayList names.
public void onResume()
{
super.onResume(); // Always call the superclass method first
//the activity need to be updated everytime it resumes.
arrayAdapter.notifyDataSetChanged();
}
}
Seems like you are not properly updating the array list. Please try logging the array list contents after updating.
ListView doesn't seem to refresh when notifyDataSetChanged(); is called, but does refresh when its adapter gets set again.
In my Activity onCreate I initialize my ListView and my adapter. Then I have this Hanler that checks for new values every second. listview.setAdapter(arrayAdapter); works but arrayAdapter.notifyDataSetChanged(); doesn't do anything.
Here is the code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.listView1);
arrayAdapter = new ArrayAdapter<Integer>(this,android.R.layout. simple_list_item_1, myIntegers);
lv.setAdapter(arrayAdapter);
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.what == DIS){
handler.sendEmptyMessageDelayed(DIS, 1000);
if(!refresh()){
handler.removeMessages(DIS);
}
}
}
};
public boolean refresh(){
if(ports.isEmpty()){
return false;
}else{
listview.setAdapter(arrayAdapter); //WORKS
arrayAdapter.notifyDataSetChanged(); // DOESN'T WORK
return true;
}
}
So I was wondering how to make that work with notifyDataSetChanged, because I read that it is the right way to do it, and anyway even if the setAdapter does work it makes my listview jump to beginning every time it refreshes it.
EDIT:
To clarify things, I am adding more values to myIntegers.
Try instead adding the values using the adapter.add() method. I dont think you can modify the array that was used to create the adapter and then expect it to hold an updated instance to update the views from. Best of Luck!
Could someone tell me what is wrong with this piece of code?
It is supposed to be a ListView from an XML file that is then referred to in Java.
Alas, it crashes my application every time it enters the Menu class.
public class Menu extends ListActivity {
String Name_for_classes[] = {"- 1-9 Tabels -", "- 10-19 Tabels -", "- 20-29 Tabels -" };
String Tabel_classes[] = {"First", "Second", "Third"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(this, R.layout.menu, Name_for_classes));
ListView list = getListView();
list.setTextFilterEnabled(true);
}
}
Okay, let's assume you have your ListView in an XML file called my_listview.xml.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_listview.xml);
ListView list = (ListView)findViewById(R.id.list);
ArrayAdapter<String> yourAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1, Name_for_classes);
list.setAdapter(yourAdapter);
}
I'm not sure what is in R.layout.menu, but guessing by the naming its the activity layout. This should be used with setContentView(R.layout.menu) in onCreate. The layout that is passed into the ArrayAdapter is the TextView you are using to populate the listview.
You have forgotten to call the setContentView in your onCreate method, so your listview is not referenced yet.
When you use setContentView, it is equivalent as sayin 'for this activity I want to use the template 'myTemplate.xml'
After that, you have to 'linked' your java ListView attribute to the listview declared in your template.