I'm using the ViewPager from the compatibility library. I have succussfully got it displaying several views which I can page through.
However, I'm having a hard time figuring out how to update the ViewPager with a new set of Views.
I've tried all sorts of things like calling mAdapter.notifyDataSetChanged()
, mViewPager.invalidate()
even creating a brand new adapter each time I want to use a new List of data.
Nothing has helped, the textviews remain unchanged from the original data.
Update: I made a little test project and I've almost been able to update the views. I'll paste the class below.
What doesn't appear to update however is the 2nd view, the 'B' remains, it should display 'Y' after pressing the update button.
public class ViewPagerBugActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
Button updateButton = (Button) findViewById(R.id.update_button);
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateViewPager();
}
});
}
private void updateViewPager() {
data.clear();
data.add("X");
data.add("Y");
data.add("Z");
myViewPager.getAdapter().notifyDataSetChanged();
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
Just in case anyone are using FragmentStatePagerAdapter based adapter(which will let ViewPager create minimum pages needed for display purpose, at most 2 for my case), @rui.araujo's answer of overwriting getItemPosition in your adapter will not cause significant waste, but it still can be improved.
In pseudo code:
Instead of returning
POSITION_NONE
and creating all fragments again, you can do as I suggested here: Update ViewPager dynamically?I leave here my own solution, which is a workaround because seems as the problem is the
FragmentPagerAdapter
doesn't clean the previousfragments
, you can be added to theViewPager
, in the Fragment Manager. So, I create a method to execute before add theFragmentPagerAdapter
:(In my case I never add more than 3 fragments, but you can use for example
getFragmentManager().getBackStackEntryCount()
and check all thefragments
.I know it is a workaround because it will stop work if the way the framework is creating the tags change.
(currently
"android:switcher:" + containerId + ":" + i
)Then the way to use it is after getting the container:
I am just posting this answer in case anyone else finds it useful. For doing the exact same thing, I simply took the source code of the ViewPager and PagerAdapter from the compatibility library and compiled it within my code (You need to sort out all the errors and imports yourself, but it definitely can be done).
Then, in the CustomViewPager, create a method called updateViewAt(int position). The view itself can be gotten from ArrayList mItems defined in the ViewPager class (you need to set an Id for the views at instantiate item and compare this id with position in the updateViewAt() method). Then you can update the view as necessary.
This is a horrible problem and I'm happy to present an excellent solution; simple, efficient, and effective !
See below, the code shows using a flag to indicate when to return POSITION_NONE
This is for all those like me, which need to update the Viewpager from a service (or other background thread) and none of the proposals have worked: After a bit of logchecking i realized, that the notifyDataSetChanged() method never returns. getItemPosition(Object object) is called an all ends there without further processing. Then i found in the docs of the parent PagerAdapter class (is not in the docs of the subclasses), "Data set changes must occur on the main thread and must end with a call to notifyDataSetChanged() ". So, the working solution in this case was (using FragmentStatePagerAdapter and getItemPosition(Object object) set to return POSITION_NONE) :
and then the call to notifyDataSetChanged() :