I have a ListView in a Fragment and I want to update the data in the ListView when I return from another Activity. I have overwritten the onResume() method in the Fragment, modify the data in the Adapter and call notifyDataSetChanged() on the Adpater but somehow the ListView is not being updated. I suspect that there is something wrong with my Adapter, but I just can't seem to find the error.
Here's code of my Adpater:
class ManualExceptionsListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private TextView mManualExceptions;
SwitchCompat mSwitch;
TextView name;
final Context context = getActivity();
final SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
int a;
int ifUse = 0;
ManualExceptionsListAdapter(LayoutInflater inflater) {
mInflater = inflater;
}
@Override
public int getCount() {
return (mPermanentManualException.size()+mContactsExceptionNumber.size());
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public int getItemViewType(int position) {
if (position < (mContactsExceptionNumber.size())) {
a = 0;
if(position == (mContactsExceptionNumber.size()-1)){
ifUse = 1;
}
return a;
} else {
a = 1;
return a;
}
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
final int pos;
if(mContactsExceptionNumber.size()>0) {
pos = i - (mContactsExceptionNumber.size());
}else{
pos = 0;
}
int pos2 = 0;
int type = getItemViewType(i);
if(ifUse == 1){
if(mContactsExceptionNumber.size()>0) {
pos2 = i - (mContactsExceptionNumber.size());
Exceptions.index = pos2;
}
}
View v = view;
if (view == null) {
switch (type) {
case 0:
v = mInflater.inflate(R.layout.contacts_exception_row, null);
name = (TextView) v.findViewById(R.id.contact_name);
name.setText(mContactsExceptionNames.get(i));
break;
case 1:
v = mInflater.inflate(R.layout.manual_exception_row, null);
mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number);
mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch);
mManualExceptions.setText(mPermanentManualException.get(pos2));
mSwitch.setTag(i);
try {
if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) {
mSwitch.setChecked(true);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}else{
switch (type) {
case 0:
v = mInflater.inflate(R.layout.contacts_exception_row, null);
name = (TextView) v.findViewById(R.id.contact_name);
name.setText(mContactsExceptionNames.get(i));
break;
case 1:
v = mInflater.inflate(R.layout.manual_exception_row, null);
mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number);
mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch);
mManualExceptions.setText(mPermanentManualException.get(pos2));
mSwitch.setTag(i);
try {
if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) {
mSwitch.setChecked(true);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
try {
mSwitch.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
isTouched = true;
return false;
}
});
} catch (NullPointerException e) {
e.printStackTrace();
}
try {
mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (isTouched) {
if (b) {
if (!mManualExceptionList.contains((mPermanentManualException.get(pos)))) {
mManualExceptionList.add((mPermanentManualException.get(pos)));
}
mSharedPreferences.edit().putString("ManualExceptions", TextUtils.
join(",", mManualExceptionList)).apply();
} else {
try {
mManualExceptionList.remove((mPermanentManualException.get(pos)));
mSharedPreferences.edit().putString("ManualExceptions", TextUtils.
join(",", mManualExceptionList)).apply();
} catch (Exception e) {
e.printStackTrace();
}
}
Log.d("RejectCall", "Permanent " + TextUtils.join(",", mPermanentManualException));
Log.d("RejectCall", TextUtils.join(",", mManualExceptionList));
}
}
});
} catch (NullPointerException e) {
e.printStackTrace();
}
return v;
}
}
There are multiple issues with you
Adapterimplementation. Too many for me to give you advice on how to fix it. I am just going to explain how you can efficiently implement anAdapterand you can then apply this to yourAdapter.Suffice it to say that you should optimally switch to using the new
RecyclerViewwhich has many major improvements over the oldListView. You can find theRecyclerViewdocumentation here and Googles guide on how to use it here.If you want to display data in a
ListViewyou should first create the layout for each of the items in theListView. For this example I will use this layout:To bundle the data we want to display in each item of the
ListViewwe write a new class which just contains the data in private fields and getters and setters to get and set that data. Such classes are usually called view models. A view model for a layout like above might look something like this:Each instance of such a view model would represent one item in the
ListView. When one of the items enters the visible area of theListViewit has to be bound to aViewin theListView(That is something we have to implement ingetView()of theAdapter). As long as the item is visible the model will stay bound to this oneView, but as soon as theViewhas exited the visible area of theListViewit will be recycled and bound to a different view model which is just entering the visible area. This is called view recycling and it is done to minimise the memory footprint of theListViewand to increase overall scroll performance and fluidity.Viewsare very expensive objects, especially inflatingViewsandfindViewById()cost a lot of performance and the main point of view recycling is that you have to inflate only a small number ofViewsonce which can then be reused and therefore you avoid the expensive inflating andfindViewById()later on.Most of what I explained above happens automatically. What you as a developer have to do is inflate the correct
ViewsingetView()or reuse them if there already is one available and then bind the correct view model to theView. I know that most of this seems rather complicated and confusing if you first hear about it, but it gets much simpler and more obvious once we start to look at the code.So now we have the layout of the view items in the
ListViewand the view model to go along with it. What we need to do now is write another class which are usually called view holders. These view holders are essentially container classes around the views in theListView. Each view holder contains oneViewassociated with an item in theListViewand they also take care of binding the data of the view model to theView. Without further ado here is a view holder to go along with the view model from above:Now we are almost finished. The only thing that is missing now is to plug all this together in the
Adapter:And that is all you need. This is pretty much a best practice implementation of an
Adapter. If you are dealing with two or more different types of views you need to write a view model and view holder class for each type. You can write an interface calledViewModelwhich would look something like this:Every view model of yours should implement this interface. You can then use
List<ViewModel>in theAdapterwhich can contain all different kinds of view models.As soon as all your view models implement this interface you can do this:
And this
Listwhich now contains multiple different types of view models can then be passed to theAdapter. TheAdapterwould then look something like this:You can also unify your view holders by writing an abstract class. Such an abstract class would look something like this:
If you use this abstract class as base class for your view holders you would write them like this:
Although this part isn't really required. The most important part when dealing with multiple different types of items in the
ListViewis that all the models implement a common interface so you can safely put them in the sameList.Anyway, this whole thing looks a lot simpler and cleaner than your
Adapter, doesn't it? This way you have perfect separation between the data in theListViewand theViewswhich display the data and it's a lot more maintainable. You can easily implement animations in the view holder without having to concern yourself with the view recycling and a lot of requirements become a lot simpler to implement. TheRecyclerViewtakes all this to the next level of course. It works in much the same way, but has several major improvements over theListView, I really suggest you take a look at it.One thing I completely forgot: You can expose the internal
Listof view models with a getter so you can modify the view models from the outside. Add methods like this to theAdapter:Then you can modify the view models in the
Adapterlike this:And when you are finished modifying the data you can update the
ListViewby callingnotifyDataSetChanged()on theAdapter.