63

I have an Activity with a ViewPager. I also have a Dialog that will retrieve a list with some items that the user will choose. Now, how can I update the Fragment where the Items are supposed to display as new views attached to this Fragment?

Here is my ViewPager's adapter:

public static class MyAdapter extends FragmentPagerAdapter {
    public MyAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return 1;
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        switch (position) {
        case 0:
            return new Fragment1();

        default:
            return null;
        }
    }
}

Now, here is the Fragment:

public class Fragment1 extends Fragment{

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onActivityCreated(savedInstanceState);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
}

@Override
public ViewGroup onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    ViewGroup view = (ViewGroup)inflater.inflate(R.layout.screen1, container, false);
    }
    return view;
}

}

This is the method that I want to use to add Items to my Fragment

private void addItem() {

    // Instantiate a new "row" view.

    final ViewGroup[] newViews = new ViewGroup[selectedItems.length];


    for(int i = 0; i < selectedItems.length; i++){
        newViews[i] = (ViewGroup) LayoutInflater.from(this).inflate(
                    R.layout.animated_layout_item, mContainerView, false);

        // Set the text in each new row.
        ((TextView) newViews[i].findViewById(android.R.id.text1)).setText(selectedItems[i]);

        // Because mContainerView has android:animateLayoutChanges set to true,
        // adding this view is automatically animated.

            mContainerView.addView(newViews[i], 0);


        // Set a click listener for the "X" button in the row that will remove the row.
        OnClickListener listener = new MyAddItemListener(newViews[i], mContainerView, this);
        newViews[i].findViewById(R.id.delete_button).setOnClickListener(listener);

    }


}
Luis Lavieri
  • 4,064
  • 6
  • 39
  • 69

15 Answers15

139

Update Fragment from ViewPager

You need to implement getItemPosition(Object obj) method.

This method is called when you call

notifyDataSetChanged()

on your ViewPagerAdaper. Implicitly this method returns POSITION_UNCHANGED value that means something like this: "Fragment is where it should be so don't change anything."

So if you need to update Fragment you can do it with:

  • Always return POSITION_NONE from getItemPosition() method. It which means: "Fragment must be always recreated"
  • You can create some update() method that will update your Fragment(fragment will handle updates itself)

Example of second approach:

public interface Updateable {
   public void update();
}


public class MyFragment extends Fragment implements Updateable {

   ...

   public void update() {
     // do your stuff
   }
}

And in FragmentPagerAdapter you'll do something like this:

@Override
public int getItemPosition(Object object) {
   MyFragment f = (MyFragment ) object;
   if (f != null) {
      f.update();
   }
  return super.getItemPosition(object);
}

And if you'll choose first approach it can looks like:

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

Note: It's worth to think a about which approach you'll pick up.

MinaHany
  • 1,015
  • 4
  • 16
  • 32
Simon Dorociak
  • 33,374
  • 10
  • 68
  • 106
  • Thanks for your answer. But, it is not updating it! Could you take a look at it in this [Gist](https://gist.github.com/lumalav/6167821) that I just created? I chose option 2. – Luis Lavieri Aug 06 '13 at 19:36
  • Also, when I try to call notifyDataSetChanged(), it tells me that is undefined for the type ViewPager – Luis Lavieri Aug 06 '13 at 19:42
  • 4
    @LuisLavieri you should call it like `yourPager.getAdapter().notifyDataSetChanged()` – Simon Dorociak Aug 06 '13 at 19:54
  • Thanks @Sajmon, but it is throwing a [Null Pointer Exception](https://gist.github.com/lumalav/6167821/raw/af7b13e10a4a994aaa73dc504ea8e88e011dde1e/NullPointerException) when I call it. :( What do you think it might be? – Luis Lavieri Aug 06 '13 at 20:03
  • @LuisLavieri you need to call it after you instantiated adapter and viewpager and added adapter to pager. – Simon Dorociak Aug 06 '13 at 20:09
  • Sure, I'm calling it in myDialogListener. But, after instantiated them. Like this: mAdapter = new MyAdapter(getSupportFragmentManager()); mPager = (ViewPager) findViewById(R.id.pager); mPager.setAdapter(mAdapter); – Luis Lavieri Aug 06 '13 at 20:12
  • The ViewPager works fine at first, but the NPE is thrown after I click "OK" in my Listener – Luis Lavieri Aug 06 '13 at 20:13
  • 2
    Beautiful. After hard work, it finally worked. Thanks buddy. The problem was my `mContainerView`; it wasn't instantiated properly. – Luis Lavieri Aug 07 '13 at 02:27
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/35039/discussion-between-luis-lavieri-and-sajmon) – Luis Lavieri Aug 07 '13 at 23:19
  • 2
    +1 for @Override public int getItemPosition(Object object) { return POSITION_NONE; } – Emre Koç Jun 06 '14 at 11:35
  • I am using the second method, but I my fragment is not updating... From debug, I can clearly see the the adapter for my fragment's listview is updated. The one issue remains is that the fragment in the viewpager is not updated Any idea? – Quinn Wei Jun 19 '14 at 03:43
  • @Sajmon I have 2 fragments in FragmentPagerAdapter. But I want to recreate only One fragment onResume() of the MainActivity. Any idea on how to recreate one fragment out of the two? – harishannam Jan 10 '15 at 20:56
  • It's a really elegant solution for me to update fragment, I select "update" solution since that I want to reuse fragment without create a new fragment instance every time. Thanks very very much :) – Weiyi Aug 04 '15 at 08:51
  • @Sajmon I follow your steps but has a strange problem: `getItemPosition()` called more than one times after `notifyDataSetChanged()` called. This cause `f.update()` called many times too, but it should be called once! Would you please give me some tip, thanks. – Weiyi Aug 05 '15 at 10:31
  • This works best - not returning `return super.getItemPosition(object)` but returning `POSITION_NONE ` Thank you – mewc Jun 13 '16 at 03:41
  • this is biggest problem of viewpager adapter – Anand Savjani Aug 28 '16 at 10:50
  • 1
    I have 5 fragment in FragmentPagerAdapter. I have tried both solution but none of worked for me as MyFragment called multiple times every time i swipe. Please help if anyone found solution. – piyush poriya Sep 06 '16 at 09:25
  • it's working great :) btw can you please explain, what does `return POSITION_NONE` do ? and why `getItemPosition` ? – mrid Aug 16 '17 at 12:07
  • Small suggestion: For getItemPosition in the FragmentPagerAdapter, perhaps better to cast to `Updateable` instead of `MyFragment`, i.e.: `MyFragment f = (MyFragment) object;` -> `Updateable f = (Updateable) object;`? – derezz Mar 27 '21 at 06:41
  • Whats the ViewPager2 equivalent? – mathematics-and-caffeine Jul 06 '22 at 20:01
33

I faced problem some times so that in this case always avoid FragmentPagerAdapter and use FragmentStatePagerAdapter.

It work for me. I hope it will work for you also.

Yogendra
  • 4,817
  • 1
  • 28
  • 21
15

I implement a app demo following Sajmon's answer. Just for anyone else who search this question.

You can access source code on GitHub

enter image description here

Weiyi
  • 1,843
  • 2
  • 22
  • 34
14

In your PagerAdapter override getItemPosition

@Override
public int getItemPosition(Object object) {
// POSITION_NONE makes it possible to reload the PagerAdapter
return POSITION_NONE;
}

After that add viewPager.getAdapter().notifyDataSetChanged(); this code like below

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            viewPager.getAdapter().notifyDataSetChanged();
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

should work.

madhu527
  • 4,644
  • 1
  • 28
  • 29
5

You can update the fragment in two different ways,

First way

like @Sajmon

You need to implement getItemPosition(Object obj) method.

This method is called when you call

notifyDataSetChanged()

You can find a example in Github and more information in this post.

enter image description here

Second way

My approach to update fragments within the viewpager is to use the setTag() method for any instantiated view in the instantiateItem() method. So when you want to change the data or invalidate the view that you need, you can call the findViewWithTag() method on the ViewPager to retrieve the previously instantiated view and modify/use it as you want without having to delete/create a new view each time you want to update some value.

@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;
}

You can find a example in Github or more information in this post:

enter image description here

Community
  • 1
  • 1
Cabezas
  • 9,329
  • 7
  • 67
  • 69
4

Very simple to override the method in the fragment:

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if(isVisibleToUser){
        actionView();
    }
    else{
        //no
    }      
}
pRaNaY
  • 24,642
  • 24
  • 96
  • 146
Issac Balaji
  • 1,441
  • 1
  • 13
  • 25
3

Accepted answer is not understand, That's why added easy solution. after long struggle find Working solution.

Just notify to your adapter, it's working amazing,

Reference code

 void setAdapter(int position) {
        PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(pagerAdapter);
        // when notify then set manually current position.
        viewPager.setCurrentItem(position);
        pagerAdapter.notifyDataSetChanged();
    }

call this code when you set adapter and when you want to refresh UI.

In Your fragment add this code for refresh

((YourActivityName) getContext()).setAdapter(selectedTabPosition);

Use instanceof for cast context.

This code share only helping purpose who find solution.

Yogesh Rathi
  • 6,331
  • 4
  • 51
  • 81
2

A very simple yet effective solution to update your fragment views. Use setUserVisibleHint() method. It will be called upon when the fragment is about to be visible to user. There you can update our view (setText() or setVisibility() etc.)

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if(isVisibleToUser){
       //Write your code to be executed when it is visible to user
    }
}

It worked in my case. Hope it helps!

tahsinRupam
  • 6,325
  • 1
  • 18
  • 34
Tahmid Rahman
  • 748
  • 1
  • 8
  • 20
2

If you use Kotlin, you can do the following:

1. On first, you should be create Interface and implemented him in your Fragment

interface RefreshData {
    fun refresh()
}

class YourFragment : Fragment(), RefreshData {
    ...
    override fun refresh() {
        //do what you want
    }
}

2. Next step is add OnPageChangeListener to your ViewPager

viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
    override fun onPageScrollStateChanged(state: Int) { }

    override fun onPageSelected(position: Int) {
        viewPagerAdapter.notifyDataSetChanged()
        viewPager.currentItem = position
    }

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { }
})

3. override getItemPosition in your Adapter

override fun getItemPosition(obj: Any): Int {
    if (obj is RefreshData) {
        obj.refresh()
    }
    return super.getItemPosition(obj)
}
Danil
  • 165
  • 7
2

i had 4 Fragments in the ViewPager, but i wanted to update just one Fragment "FavoriteFragment" at the position 0 of the viewPager, every time the user click on this fragment. but none of the codes above helped me reload just the one Fragment i want. so i tried this and it works for me:

in my FavoriteFragment i overrided onResume()

@Override
    public void onResume() {
        super.onResume();
        updateUI(); // code to update the UI in the fragment
    }

in the Activity hosting the viewPager override addOnPageChangeListener() and add this code

mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);

                if(position == 0) { // 0 = the first fragment in the ViewPager, in this case, the fragment i want to refresh its UI
                    FavoritesFragment fragment = (FavoritesFragment) mViewPager.getAdapter().instantiateItem(mViewPager, position); 
                    fragment.onResume(); // here i call the onResume of the fragment, where i have the method updateUI() to update its UI
                    mViewPager.getAdapter().notifyDataSetChanged();
                }
            }
        });

and it works perfect for me, to refresh/reload just the one fragment i want.

korchix
  • 1,445
  • 1
  • 19
  • 22
2

Alot of answers, but a quick and dirty solution that got the job done for me is having a method to init your pagerAdapter in your MainActivity:

public void initPagerAdapter(){
        pagerAdapter = new MainPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(pagerAdapter);
        tabLayout.setupWithViewPager(viewPager);
}

In my fragment where I want to refresh, I just get the MainActivity and call that method:

MainActivity activity = (MainActivity) getContext();
activity.initPagerAdapter();
DIRTY DAVE
  • 2,523
  • 2
  • 20
  • 83
1

Maybe your method for adding items into fragment should be public (placed in desired Fragment) and should have parameter the same type as selectedItems ..

That will make it visible from activity, which will have selectedItems array and voila..

p.s. better name it addItemsFromArray(typeOfSelectedItems[] pSelectedItems) cause name addItem() is quite undescriptive

Edit: stackoverflow just suggested similar topic :) Check here for detailed idea implementation.. :)

Community
  • 1
  • 1
Ewoks
  • 12,285
  • 8
  • 58
  • 67
0

1) Create a handler in the fragment that you want to update.

public static Handler sUpdateHandler;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ...

        sUpdateHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // call you update method here.
            }
        };
    }

2) In the Activity/Fragment/Dialog, wherever you want the update call to be fired, get the reference to that handler and send a message (telling your fragment to update)

// Check if the fragment is visible by checking if the handler is null or not.
Handler handler = TaskTabCompletedFragment.sUpdateHandler;
if (handler != null) {
    handler.obtainMessage().sendToTarget();
}
Astonvish32
  • 179
  • 2
  • 11
0

Because none of the above answers did the trick for me, here is my solution:

I combined the POSITION_NONE with loading on setUserVisibleHint(boolean isVisibleToUser) instead of onStart()

As seen here: https://stackoverflow.com/a/25676323/497366

In the Fragment:

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}

and in the FragmentStatePagerAdapter as seen in the top answer here from Simon Dorociak https://stackoverflow.com/a/18088509/497366:

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

Now the fragments reload the data into their views everytime they are shown to the user.

Björn Kechel
  • 7,933
  • 3
  • 54
  • 57
0

This worked for me:

Activity:

for (Fragment fragment : getSupportFragmentManager().getFragments()) {
    if (fragment.isVisible() && fragment instanceof MyFragment) {
        ((MyFragment) fragment).refreshContent();
        break;
    }
}

Fragment:

public void refreshContent() {
    //refresh content
}
Kris B
  • 3,436
  • 9
  • 64
  • 106