Update ViewPager dynamically?

2018-12-31 04:47发布

I can't update the content in ViewPager.

A question: What is the relationship and correct usage of methods instantiateItem() and getItem() in FragmentPagerAdapter class?

I was using only getItem() to instantiate and return my fragments:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

This worked well (but as said I can't change the content).

So I found this: ViewPager PagerAdapter not updating the View

Particulary one in which it's talked about method instantiateItem():

"My approach is to use the setTag() method for any instantiated view in the instantiateItem() method"

So now I want to implement instantiateItem() in order to do that. But I don't know what I have to return there (return is Object) and what is the relation with getItem(int position)?

Currently I'm using getItem to instantiate the Fragment, is that wrong? But then do I have to put the fragments in instance variable, or not implement getItem() at all...? I just don't understand it.

I tried reading the reference:

  • public abstract Fragment getItem (int position)

    Return the Fragment associated with a specified position.

  • public Object instantiateItem (ViewGroup container, int position)

    Create the page for the given position. The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup). Parameters

    container The containing View in which the page will be shown. position The page position to be instantiated.

    Returns

    Returns an Object representing the new page. This does not need to be a View, but can be some other container of the page.

... but I still don't understand how are they related and what I have to do.

Here's my code. I'm using support package v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

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

        pager = (ViewPager)findViewById(R.id.slider);

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

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

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

Here is also somebody with a similar problem, no answers...

http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html

18条回答
流年柔荑漫光年
2楼-- · 2018-12-31 05:37

I had a similar problem but don't want to trust on the existing solutions (hard coded tag names etc.) and I couldn't make M-WaJeEh's solution work for me. Here is my solution:

I keep references to the fragments created in getItem in an array. This works fine as long as the activity is not destroyed due to configurationChange or lack of memory or whatever (--> when coming back to the activity, fragments return to their last state without 'getItem' being called again and thus without updating the array).

To avoid this problem I implemented instantiateItem(ViewGroup, int) and update my array there, like this:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

So, on the one hand I'm happy that I found a solution that works for me and wanted to share it with you, but I also wanted to ask whether somebody else tried something similar and whether there is any reason why I shouldn't do it like that? So far it works very good for me...

查看更多
无与为乐者.
3楼-- · 2018-12-31 05:39

Try destroyDrawingCache() on ViewPager after notifyDataSetChanged() in your code.

查看更多
有味是清欢
4楼-- · 2018-12-31 05:39

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<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> 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<PagerItem> 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<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

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

I hope it helps.

查看更多
无与为乐者.
5楼-- · 2018-12-31 05:41

I had been trying so many different approaches, none really sove my problem. Below are how I solve it with a mix of solutions provided by you all. Thanks everyone.

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

I only want to refresh page 0 after onResume().

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

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

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

In my FragmentsMain, there is public integer "page", which can tell me whether it is the page I want to refresh.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;
查看更多
皆成旧梦
6楼-- · 2018-12-31 05:41

This solution won't work for everyone, but in my case, every Fragment in my ViewPager is a different class, and only one of them ever exist at a time.

With this constraint, this solution is safe and should be safe to use in production.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

If you have multiple versions of the same fragment, you can use this same strategy to call methods on those fragments to determine if it is the fragment you wish to update.

查看更多
孤独总比滥情好
7楼-- · 2018-12-31 05:42

For some reason none of the answers worked for me so I had to override the restoreState method without calling super in my fragmentStatePagerAdapter. Code:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

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

}
查看更多
登录 后发表回答