Я использую ViewPager из библиотеке совместимости. Я succussfully получил его отображение нескольких представлений, которые я могу пролистать.

Тем не менее, я, имея трудное время, выясняя, как обновить ViewPager с новым набором представлений.

Я пробовал всякие вещи, как призвание mAdapter.notifyDataSetChanged(), mViewPager.признать недействительными() даже создавая абсолютно новый адаптер каждый раз, когда я хочу использовать новый список данных.

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.

Что не обновляется впрочем это 2-й вид, типа 'B' остается, это должно отображаться как 'Y' после нажатия на кнопку "обновить".

public class ViewPagerBugActivity extends Activity {

    private ViewPager myViewPager;
    private List data;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        data = new ArrayList();
        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 data;
        private Context ctx;

        public MyViewPagerAdapter(Context ctx, List 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) {
        }
    }
}
share|improve this question
1  
Please see stackoverflow.com/questions/12510404/… for a potential bug with the FragmentStatePagerAdapter. – samusarin Jan 21 '14 at 20:12
1  
Sorry to dredge this up. Your question really helped me so thanks! The one thing I don't get is how the reference to data in the PagerAdapter gets changed when you update it in the Activity – Ewanw Jul 13 '15 at 22:54
    
Make your adapter extend BaseAdapter Refer this: stackoverflow.com/questions/13664155/… – user5219444 Aug 12 '15 at 13:04
1  
Apparently if you set the adapter again to your view pager, then it resets. – EpicPandaForce Feb 11 '16 at 21:27
    
i have issue in pager stackoverflow.com/questions/41217237/… – chris Dec 21 '16 at 12:27

34 Answers 34

up vote 657 down vote accepted

There are several ways to achieve this.

Первый вариант проще, но чуть более неэффективным.

Переопределить getItemPosition в вашем PagerAdapter такой:

public int getItemPosition(Object object) {
    return POSITION_NONE;
}

Таким образом, когда вы звоните notifyDataSetChanged()вид пейджер будет удалить все представления и загрузить их все. Так как перезагрузка эффект получается.

Второй вариант, предложил Альваро Луис Бустаманте (ранее alvarolb), является setTag() метод в instantiateItem() при создании нового представления. Затем, вместо использования notifyDataSetChanged()можно использовать findViewWithTag() чтобы найти вид, который вы хотите обновить.

Второй подход является очень гибким и высоко производительным. Престижность alvarolb для оригинальных исследований.

share|improve this answer
4  
Better than my work around, seems to not have any side affects, thanks. – C0deAttack Sep 3 '11 at 21:39
3  
I have a similar problem. My viewPager will show the new searc results but doesn't default back to the first entry in the cursor. Does anyone know how to reset the position when updating the dataset? – mAndroid Sep 14 '11 at 5:16
3  
Doesn't work for me – Ixx Jun 2 '12 at 21:08
3  
What do you mean? – rui.araujo Jun 3 '12 at 2:55
7  
Better answer (without removing all items) -> stackoverflow.com/a/10852046/898056 – yhpark Jul 10 '13 at 18:52

I don't think there is any kind of bug in the PagerAdapter. The problem is that understanding how it works is a little complex. Looking at the solutions explained here, there is a misunderstanding and therefore a poor usage of instantiated views from my point of view.

The last few days I have been working with PagerAdapter and ViewPager, and I found the following:

The notifyDataSetChanged() method on the PagerAdapter will only notify the ViewPager that the underlying pages have changed. For example, if you have created/deleted pages dynamically (adding or removing items from your list) the ViewPager should take care of that. In this case I think that the ViewPager determines if a new view should be deleted or instantiated using the getItemPosition() and getCount() methods.

I think that ViewPager, after a notifyDataSetChanged() call takes it's child views and checks their position with the getItemPosition(). If for a child view this method returns POSITION_NONE, the ViewPager understands that the view has been deleted, calling the destroyItem(), and removing this view.

Таким образом, переопределяя getItemPosition() всегда возвращают POSITION_NONE совершенно неправильно, если вы только хотите обновить содержание страницы, потому что ранее созданные представления будут разрушены, а новые будут создаваться при каждом вызове notifyDatasetChanged(). Это может показаться не так уж неправы лишь в течение нескольких Виджет textviewы, но когда у вас есть комплекс представлений, как представлений списка заполняется из базы данных, это может быть реальная проблема и пустая трата ресурсов.

Поэтому существует несколько подходов к качественно изменить содержание смотреть без необходимости удалить и создать снова посмотреть. Это зависит от проблемы, которую вы хотите решить. Мой подход заключается в использовании setTag() способ для любой инстанцировать смотреть в instantiateItem() метод. Поэтому, когда вы хотите изменить данные или же аннулировать мнение о том, что вам нужно, вы можете позвонить в findViewWithTag() метод на ViewPager для извлечения ранее инстанцировать просматривать и изменять/использовать его, как вы хотите, без необходимости удалять/создавать новый вид каждый раз, когда вы хотите обновить какую-либо ценность.

Представьте, например, что у вас есть 100 страниц с 100 Виджет textviewS, и вы только хотите обновить одно значение периодически. С подходами объяснил прежде, это означает, что вы удаляете и инстанцирование 100 Виджет textviewS на каждое обновление. It does not make sense...

share|improve this answer
8  
+1 - Your solution not only works like a charm I ran in exactly the problem you described here when I had to update pages which contained a tabs with listviews, textviews and images. (OutOfErrorExceptions and such ...) Thank you! – schlingel Dec 15 '11 at 16:23
19  
Im glad that someone read and understand my comment =) – Alvaro Luis Bustamante Dec 15 '11 at 21:37
18  
This is a deep look of an engineer. Very good answer, thanks. – Sergii Rudchenko Mar 24 '12 at 21:28
12  
It would be perfect if you wrote an example. – Sami Eltamawy May 19 '15 at 11:31
5  
As someone says : an example woulb be appreciated! – Jey10 Jan 21 '16 at 14:25

Change the FragmentPagerAdapter to FragmentStatePagerAdapter.

Override getItemPosition() method and return POSITION_NONE.

Eventually, it will listen to the notifyDataSetChanged() on view pager.

share|improve this answer
    
This helped me, thanks! – Mark O'Sullivan Oct 26 '16 at 8:34
    
this code work fine – Carl Nov 2 '16 at 5:46
    
Didn't work for me – viper Jan 9 at 10:26
    
you won't believe how many upvotes i want to give you right now! :) – Amir.F Jan 26 at 8:56
    
Great answer dude....................... – Vicky May 4 at 11:17

The answer given by alvarolb is definitely the best way to do it. Building upon his answer, an easy way to implement this is to simply store out the active views by position:

SparseArray views = new SparseArray();

@Override
public Object instantiateItem(View container, int position) {
    View root = ;
    ((ViewPager) container).addView(root);
    views.put(position, root);
    return root;
}

@Override
public void destroyItem(View collection, int position, Object o) {
    View view = (View)o;
    ((ViewPager) collection).removeView(view);
    views.remove(position);
    view = null;
}

Then once by overriding the notifyDataSetChanged method you can refresh the views...

@Override
public void notifyDataSetChanged() {
    int key = 0;
    for(int i = 0; i < views.size(); i++) {
       key = views.keyAt(i);
       View view = views.get(key);
       
    }
    super.notifyDataSetChanged();
}

You can actually use similar code in instantiateItem and notifyDataSetChanged to refresh your view. In my code I use the exact same method.

share|improve this answer
7  
can you provide a full adapter example? is this being done using Fragments or not? – user1545072 Jan 30 '13 at 16:22
7  
?? what is the mean of it either we have to add he view here or nothing ? – Akarsh M Jul 22 '13 at 15:39
    
Worked perfectly! Thank you so much! :) – Gaurav B Dec 31 '15 at 21:09
    
This method update all the views. If you only want to update one view, then you can write your own notifyDataItemChanged method like this: public void notifyDataItemChanged(int pos) { TextView tv = (TextView) views.get(pos).findViewById(R.id.tv); tv.setText(list.get(pos)); super.notifyDataSetChanged(); } – Kai Wang Mar 8 '16 at 20:57
    
This code crashes if new pager size is less than old size – Anton Duzenko Mar 24 '16 at 15:41

After hours of frustration while trying all the above solutions to overcome this problem and also trying many solutions on other similar questions like this, this and this which all FAILED with me to solve this problem and to make the ViewPager to destroy the old Fragment and fill the pager with the new Fragments. I have solved the problem as following:

1) Make the ViewPager class to extends FragmentPagerAdapter as following:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Create an Item for the ViewPager that store the title and the fragment as following:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}

3) Make the constructor of the ViewPager take my FragmentManager instance to store it in my class as following:

private FragmentManager mFragmentManager;
private ArrayList mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4) Create a method to re-set the adapter data with the new data by deleting all the previous fragment from the fragmentManager itself directly to make the adapter to set the new fragment from the new list again as following:

public void setPagerItems(ArrayList pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5) From the container Activity or Fragment do not re-initialize the adapter with the new data. Set the new data through the method setPagerItems with the new data as following:

ArrayList pagerItems = new ArrayList();
pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

mPagerAdapter.setPagerItems(pagerItems);
mPagerAdapter.notifyDataSetChanged();

I hope it helps.

share|improve this answer

Had the same problem. For me it worked to override FragmentStatePagerAdapter

@Override
public Parcelable saveState() {
    return null;
}

@Override
public void restoreState(Parcelable state, ClassLoader loader) {

}

This work for me.

share|improve this answer
    
The only solution worked for me – Mithun Sarker Shuvro Aug 31 '16 at 11:20
    
This worked for me too : thank you !!! – Jonatha ANTOINE Feb 21 at 20:59
    
@Mario Mosca, ur just a hero (y) (y). Thank you. That's the only solution that worked for me. – FRK Apr 4 at 5:01

Two and half years after the OP posed his question, this issue is still, well, still an issue. It's obvious Google's priority on this isn't particularly high, so rather than find a fix, I found a workaround. The big breakthrough for me was finding out what the real cause of the problem was (see the accepted answer in this post ). Once it was apparent that the issue was that any active pages are not properly refreshed, my workaround was obvious:

In my Fragment (the pages):

  • I took all the code which populates the form out of onCreateView and put it in a function called PopulateForm which may be called from anywhere, rather than by the framework. This function attempts to get the current View using getView, and if that is null, it just returns. It's important that PopulateForm contains only the code that displays - all the other code which creates FocusChange listeners and the like is still in OnCreate
  • Create a boolean which can be used as a flag indicating the form must be reloaded. Mine is mbReloadForm
  • Override OnResume() to call PopulateForm() if mbReloadForm is set.

In my Activity, where I do the loading of the pages:

  • Go to page 0 before changing anything. I'm using FragmentStatePagerAdapter, so I know that two or three pages are affected at most. Changing to page 0 ensures I only ever have the problem on pages 0, 1 and 2.
  • Before clearing the old list, take it's size(). This way you know how many pages are affected by the bug. If > 3, reduce it to 3 - if you're using a a different PagerAdapter, you'll have to see how many pages you have to deal with (maybe all?)
  • Reload the data and call pageAdapter.notifyDataSetChanged()
  • Now, for each of the affected pages, see if the page is active by using pager.getChildAt(i) - this tells you if you have a view. If so, call pager.PopulateView(). If not, set the ReloadForm flag.

After this, when you reload a second set of pages, the bug will still cause some to display the old data. However, they will now be refreshed and you will see the new data - your users won't know the page was ever incorrect because this refreshing will happen before they see the page.

Hope this helps someone!

share|improve this answer

A much easier way: use a FragmentPagerAdapter, and wrap your paged views onto fragments. They do get updated

share|improve this answer
    
Can you please explain with code snippet? – Dhasneem Mar 27 '15 at 7:34

After a lot of searching for this problem, I found a really good solution that I think is the right way to go about this. Essentially, instantiateItem only gets called when the view is instantiated and never again unless the view is destroyed (this is what happens when you override the getItemPosition function to return POSITION_NONE). Instead, what you want to do is save the created views and either update them in the adapter, generate a get function so someone else can update it, or a set function which updates the adapter (my favorite).

So, in your MyViewPagerAdapter add a variable like:

private View updatableView;

an in your instantiateItem:

 public Object instantiateItem(View collection, int position) {
        updatableView = new TextView(ctx); //My change is here
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);
        return view;
    }

so, this way, you can create a function that will update your view:

public updateText(String txt)
{
    ((TextView)updatableView).setText(txt);
}

Hope this helps!

share|improve this answer

I had a similar problem in which I had four pages and one of the pages updated views on the other three. I was able to updated the widgets(SeekBars, TextViews, etc.) on the page adjacent to the current page. The last two pages would have uninitialized widgets when calling mTabsAdapter.getItem(position).

To solve my issue, I used setSelectedPage(index) before calling getItem(position). This would instantiate the page, allowing me to be able to alter values and widgets on each page.

After all of the updating I would use setSelectedPage(position) followed by notifyDataSetChanged().

You can see a slight flicker in the ListView on the main updating page, but nothing noticeable. I haven't tested it throughly, but it does solve my immediate problem.

share|improve this answer
    
Hi, i am interested in your code, i am facing similar issue, I am able to update adjacent fragments but the current fragment which is visible doesn't get updated. I am very confused with all answers here. how can i update views in current fragment which is shown by the VIewPager adapter – Pankaj Nimgade Jun 11 '15 at 14:23

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:

public int getItemPosition(Object object) {
    YourFragment f = (YourFragment) object;
    YourData d = f.data;
    logger.info("validate item position on page index: " + d.pageNo);

    int dataObjIdx = this.dataPages.indexOf(d);

    if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
        logger.info("data changed, discard this fragment.");
        return POSITION_NONE;
    }

    return POSITION_UNCHANGED;
}
share|improve this answer
    
inside ViewPager, it was quite easy to know item past position, the most perfect implementation is just return the new position in getItemPosition() when dataset changed, and ViewPager will know they are changed or not. sadly, ViewPager didn't do it. – VinceStyling Jul 25 '14 at 7:19

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.

share|improve this answer

All these solution did not help me. thus i found a working solution: You can setAdapter every time, but it isn't enough. you should do these before changing adapter:

FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
List fragments = fragmentManager.getFragments();
for (Fragment f : fragments) {
    transaction.remove(f);
}
transaction.commit();

and after this:

viewPager.setAdapter(adapter);
share|improve this answer

what worked for me was going viewPager.getAdapter().notifyDataSetChanged();

and in the adapter putting your code for updating the view inside getItemPosition like so

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

might not be the most correct way of going about it but it worked (the return POSITION_NONE trick caused a crash for me so wasnt an option)

share|improve this answer

You can update dynamically all fragments, you can see in three steps.

In your adapter:

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap();
}

// Returns total number of pages
@Override
public int getCount() {
    return NUM_ITEMS;
}

// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return FirstFragment.newInstance();
        case 1:
            return SecondFragment.newInstance();
        case 2:
            return ThirdFragment.newInstance();
        default:
            return null;
    }
}

// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
    return "Page " + position;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        mFragmentTags.put(position, tag);
    }
    return object;
}

public Fragment getFragment(int position) {
    Fragment fragment = null;
    String tag = mFragmentTags.get(position);
    if (tag != null) {
        fragment = mFragmentManager.findFragmentByTag(tag);
    }
    return fragment;
}}

Now in your activity:

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{

MyPagerAdapter mAdapterViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
    mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(mAdapterViewPager);
    viewPager.addOnPageChangeListener(this);
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {

    Fragment fragment = mAdapterViewPager.getFragment(position);
    if (fragment != null) {
        fragment.onResume();
    }
}

@Override
public void onPageScrollStateChanged(int state) {

}}

Finally in your fragment, something like that:

public class YourFragment extends Fragment {

// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {

    return new YourFragment();
}

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}


@Override
public void onResume() {
    super.onResume();

    //to refresh your view
    refresh();

}}

You can see complete code here.

Thanks Alvaro Luis Bustamante.

share|improve this answer

Always returning POSITION_NONE is simple but a little inefficient way because that evoke instantiation of all page that have already instantiated.

I've created a library ArrayPagerAdapter to change items in PagerAdapters dynamically.

Internally, this library's adapters return POSITION_NONE on getItemPosiition() only when necessary.

You can change items dynamically like following by using this library.

@Override
protected void onCreate(Bundle savedInstanceState) {
        /** ... **/
    adapter = new MyStatePagerAdapter(getSupportFragmentManager()
                            , new String[]{"1", "2", "3"});
    ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter);
     adapter.add("4");
     adapter.remove(0);
}

class MyPagerAdapter extends ArrayViewPagerAdapter {

    public MyPagerAdapter(String[] data) {
        super(data);
    }

    @Override
    public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) {
        View v = inflater.inflate(R.layout.item_page, container, false);
        ((TextView) v.findViewById(R.id.item_txt)).setText(item);
        return v;
    }
}

Thils library also support pages created by Fragments.

share|improve this answer
    
i have tried it and it's good. – Edijae Crusar Sep 19 '16 at 14:29

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

public class ViewPagerAdapter extends PagerAdapter
{
    // Members
    private boolean mForceReinstantiateItem = false;

    // This is used to overcome terrible bug that Google isn't fixing
    // We know that getItemPosition() is called right after notifyDataSetChanged()
    // Therefore, the fix is to return POSITION_NONE right after the notifyDataSetChanged() was called - but only once
    @Override
    public int getItemPosition(Object object)
    {
        if (mForceReinstantiateItem)
        {
            mForceReinstantiateItem = false;
            return POSITION_NONE;
        }
        else
        {
            return super.getItemPosition(object);
        }
    }

    public void setData(ArrayList newContent)
    {
        mDisplayContent = newContent;
        mForceReinstantiateItem = true;
        notifyDataSetChanged();
    }

}
share|improve this answer
    
My proposed solution appears to work, however, after using for a while I noticed that the 'cached' offscreen views become stale if you update the data in the array. It seems therefore that ViewPager.notifyDataSetChanged() is totally FUBAR and the best solution (only when dealing with ViewPager) is to recreate the adapter when the data needs to change. Other views that require an adapter work just fine with notifyDataSetChanged() – Someone Somewhere Aug 7 '16 at 11:53

Thank rui.araujo and Alvaro Luis Bustamante. At first, I try to use rui.araujo's way, because it's easy. It works but when the data change, the page will redraw obviously. It is bad so I try to use Alvaro Luis Bustamante's way. It's perfect. Here is the code:

@Override
protected void onStart() {
    super.onStart();
}

private class TabPagerAdapter extends PagerAdapter {
    @Override
    public int getCount() {
        return 4;
    }

    @Override
    public boolean isViewFromObject(final View view, final Object object) {
        return view.equals(object);
    }

    @Override
    public void destroyItem(final View container, final int position, final Object object) {
        ((ViewPager) container).removeView((View) object);
    }

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View view = LayoutInflater.from(
                getBaseContext()).inflate(R.layout.activity_approval, null, false);
        container.addView(view);
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        view.setTag(position);
        new ShowContentListTask(listView, position).execute();
        return view;
    }
}

And when data change:

for (int i = 0; i < 4; i++) {
    View view = contentViewPager.findViewWithTag(i);
    if (view != null) {
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        new ShowContentListTask(listView, i).execute();
    }
}
share|improve this answer

I had the same issue and my solution is overriding of ViewPagerAdapter#getItemId(int position):

@Override
public long getItemId(int position) {
    return mPages.get(position).getId();
}

By default, this method returns item's position. I suppose that ViewPager checks if itemId was changed and recreates page only if it was. But not-overriden version returns the same position as itemId even if page is actually different, and ViewPager doesn't define that page is replaced one and needs to be recreated.

To use this, long id is needed for each page. Normally it is expected to be unique, but i suggest, for this case, that it just should be different from the previous value for the same page. So, It is possible to use continuous counter in adapter or random integers (with wide distribution) here.

I think that it is more consistent way rather using of Tags of view mentioned as a solution in this topic. But probably not for all cases.

share|improve this answer
    
It is. I've checked it out: just POSITION_NONE is not enough. Thank you for your wonderful answer! :) – Slava Aug 1 at 11:35

I have tried all this solutions did not work for me because all my views are complex. So it is difficult to save the View and update the View. If you have a simple View that contains only a few TextView or something like this, it's easy and the solutions posted here works. But if you have complex View like mine, and need to change the entire Fragment, I think that's impossible to do. I have investigating the ViewPager code, and it seems that the ViewPager keeps the old fragments. It's easy to realize it : Search for some native application from your device that implements swipe and do the test changing the language from English to Arabic. In this case, it should change the order of fragments, because Arabic is a RTL language. But it did not work this way.

share|improve this answer

Instead of returning POSITION_NONE and creating all fragments again, you can do as I suggested here: Update ViewPager dynamically?

share|improve this answer

1.First you have to set the getItemposition method in your Pageradapter class 2.You have to read the Exact position of your View Pager 3.then send that position as data location of your new one 4.Write update button onclick listener inside the setonPageChange listener

that program code is little bit i modified to set the particular position element only

public class MyActivity extends Activity {

private ViewPager myViewPager;
private List data;
public int location=0;
public Button updateButton;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    data = new ArrayList();
    data.add("A");
    data.add("B");
    data.add("C");
    data.add("D");
    data.add("E");
    data.add("F");

    myViewPager = (ViewPager) findViewById(R.id.pager);
    myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

      updateButton = (Button) findViewById(R.id.update);

    myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i2) {
             //Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onPageSelected( int i) {
          // here you will get the position of selected page
            final int k = i;
             updateViewPager(k);

        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }
    });
}

private void updateViewPager(final int i) {  
    updateButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
            data.set(i, "Replaced "+i);         
            myViewPager.getAdapter().notifyDataSetChanged();
        }
    });

}

private class MyViewPagerAdapter extends PagerAdapter {

    private List data;
    private Context ctx;

    public MyViewPagerAdapter(Context ctx, List data) {
        this.ctx = ctx;
        this.data = data;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @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) {
    }
}
}
share|improve this answer

I think I've made a simple way to notify of data set changes:

First, change a bit the way the instantiateItem function works:

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View rootView = mInflater.inflate(...,container, false);
        rootView.setTag(position);
        updateView(rootView, position);
        container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mViewPager.setObjectForPosition(rootView, position);
        return rootView;
    }

for "updateView" , fill the view with all the data you wish to fill (setText,setBitmapImage,...) .

verify that destroyView works like this:

    @Override
    public void destroyItem(final ViewGroup container, final int position, final Object obj) {
        final View viewToRemove = (View) obj;
        mViewPager.removeView(viewToRemove);
    }

Now, suppose you need to change the data, do it, and then call the next function on the PagerAdapter :

    public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos,
            final NotifyLocation toPos) {
        final int offscreenPageLimit = viewPager.getOffscreenPageLimit();
        final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        if (fromPosInt <= toPosInt) {
            notifyDataSetChanged();
            for (int i = fromPosInt; i <= toPosInt; ++i) {
                final View pageView = viewPager.findViewWithTag(i);
                mPagerAdapter.updateView(pageView, i);
            }
        }
    }

public enum NotifyLocation {
    MOST_LEFT, CENTER, MOST_RIGHT
}

For example if you wish to notify all of the views that are being shown by the viewPager that something has changed, you can call:

notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT);

That's it.

share|improve this answer

For what it's worth, on KitKat+ it seems that adapter.notifyDataSetChanged() is enough to cause the new views to show up, provided that you've setOffscreenPageLimit sufficiently high. I'm able to get desired behavior by doing viewPager.setOffscreenPageLimit(2).

share|improve this answer

I actually use notifyDataSetChanged() on ViewPager and CirclePageIndicator and after that I call destroyDrawingCache() on ViewPager and it works.. None of the other solutions worked for me.

share|improve this answer
    
The original question is nearly 2 years old, I'm sure there are many differences in the Android API by now that the answers here may not be right for all versions of the Android API. – C0deAttack Jul 9 '13 at 9:28

if you want to solve Viewpager update problem check this project, it will solve your problem.

and more understand check that;

happy coding...

share|improve this answer

In my case every time that Fragment entered to @override onCreateView(...) i was restarting values so handle null or empty values like:

if(var == null){
    var = new Something();
}

if(list.isEmpty()){
    list = new ArrayList();
}

if(myAdapter == null){
    myAdapter = new CustomAdapter();
    recyclerView.setAdapter(myAdapter);
}

// etc...

Fragment class could enter to onCreateView every time we change and get back to the view from the UI at runtime.

share|improve this answer

I m using Tablayout with ViewPagerAdapter. For passing data between fragments or for communicating between fragments use below code which works perfectly fine and refresh the fragment when ever it appears. Inside button click of second fragment write below code.

b.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            String text=e1.getText().toString(); // get the text from EditText

            // move from one fragment to another fragment on button click
            TabLayout tablayout = (TabLayout) getActivity().findViewById(R.id.tab_layout); // here tab_layout is the id of TabLayout which is there in parent Activity/Fragment
            if (tablayout.getTabAt(1).isSelected()) { // here 1 is the index number of second fragment i-e current Fragment

                LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
                Intent i = new Intent("EDIT_TAG_REFRESH");
                i.putExtra("MyTextValue",text);
                lbm.sendBroadcast(i);

            }
            tablayout.getTabAt(0).select(); // here 0 is the index number of first fragment i-e to which fragment it has to moeve

        }
    });

below is the code which has to be written in first fragment (in my case) i-e in receiving Fragment.

MyReceiver r;
Context context;
String newValue;
public void refresh() {
    //your code in refresh.
    Log.i("Refresh", "YES");
}
public void onPause() {
    super.onPause();

    LocalBroadcastManager.getInstance(context).unregisterReceiver(r);
}
public void onResume() {
    super.onResume();
    r = new MyReceiver();
    LocalBroadcastManager.getInstance(getActivity()).registerReceiver(r,
            new IntentFilter("EDIT_TAG_REFRESH"));
} // this code has to be written before onCreateview()


 // below code can be written any where in the fragment
 private class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    PostRequestFragment.this.refresh();
        String action = intent.getAction();
        newValue=intent.getStringExtra("MyTextValue");
        t1.setText(newValue); // upon Referesh set the text
    }
}
share|improve this answer

I leave here my own solution, which is a workaround because seems as the problem is the FragmentPagerAdapter doesn't clean the previous fragments, you can be added to the ViewPager, in the Fragment Manager. So, I create a method to execute before add the FragmentPagerAdapter:

(In my case I never add more than 3 fragments, but you can use for example getFragmentManager().getBackStackEntryCount() and check all the fragments.

/**
 * this method is solving a bug in FragmentPagerAdapter which don't delete in the fragment manager any previous fragments in a ViewPager.
 *
 * @param containerId
 */
public void cleanBackStack(long containerId) {
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    for (int i = 0; i < 3; ++i) {
        String tag = "android:switcher:" + containerId + ":" + i;
        Fragment f = getFragmentManager().findFragmentByTag(tag);
        if (f != null) {
            transaction.remove(f);
        }
    }
    transaction.commit();
}

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:

ViewPager viewPager = (ViewPager) view.findViewById(R.id.view_pager);
cleanBackStack(viewPager.getId());
share|improve this answer

I found very interesting decision of this problem. Instead of using FragmentPagerAdapter, which keep in memory all fragments, we can use FragmentStatePagerAdapter (android.support.v4.app.FragmentStatePagerAdapter), that reload fragment each time, when we select it.

Realisations of both adapters are identical. So, we need just change "extend FragmentPagerAdapter" on "extend FragmentStatePagerAdapter"

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.