动态更新 ViewPager? [英] Update ViewPager dynamically?

查看:36
本文介绍了动态更新 ViewPager?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法更新 ViewPager 中的内容.

FragmentPagerAdapter 类中方法 instantiateItem() 和 getItem() 的正确用法是什么?

我只使用 getItem() 来实例化并返回我的片段:

@Override公共片段 getItem(int position) {返回新的 MyFragment(上下文,参数);}

效果很好.除了我不能改变内容.

所以我发现了这个:ViewPager PagerAdapter 未更新视图<块引用>

我的方法是对instantiateItem() 方法中的任何实例化视图使用setTag() 方法"

现在我想实现instantiateItem()来做到这一点.但是我不知道我要返回什么(类型是Object)和getItem(int position)有什么关系?

我阅读了参考:

<块引用>
  • public abstract Fragment getItem (int position)

    返回与指定位置关联的 Fragment.

  • public Object instantiateItem(ViewGroup 容器,int 位置)

    为给定位置创建页面.适配器负责将视图添加到此处给定的容器中,尽管它只必须确保在它从 finishUpdate(ViewGroup) 返回时完成.参数

    container 将在其中显示页面的包含视图.position 要实例化的页面位置.

    退货

    返回一个代表新页面的对象.这不需要是一个视图,但可以是页面的其他容器.

但我还是不明白.

这是我的代码.我正在使用支持包 v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {私有 ViewPager 寻呼机;私有 MyFragmentAdapter 适配器;@覆盖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"};适配器 = 新的 MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);pager.setAdapter(适配器);((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {@覆盖public void onClick(View v) {重新加载();}});}私有无效重新加载(){String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};//adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);适配器.setData(数据);适配器.notifyDataSetChanged();pager.invalidate();//pager.setCurrentItem(0);}}

MyFragmentAdapter

class MyFragmentAdapter 扩展 FragmentPagerAdapter {私人 int slideCount;私有上下文上下文;私有字符串[] 数据;public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {超级(调频);this.slideCount = slideCount;this.context = 上下文;this.data = 数据;}@覆盖公共片段 getItem(int position) {返回新的 MyFragment(data[position], context);}@覆盖公共 int getCount() {返回幻灯片计数;}公共无效集数据(字符串 [] 数据){this.data = 数据;}@覆盖公共 int getItemPosition(对象对象){返回 POSITION_NONE;}}

我的片段

public final class MyFragment extends Fragment {私人字符串文本;公共 MyFragment(字符串文本,上下文上下文){this.text = 文字;}@覆盖公共视图 onCreateView(LayoutInflater inflater,ViewGroup 容器,Bundle savedInstanceState) {查看视图 = inflater.inflate(R.layout.slide, null);((TextView)view.findViewById(R.id.text)).setText(text);返回视图;}}

这里也有人有类似的问题,没有答案http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html

解决方案

在使用 FragmentPagerAdapter 或 FragmentStatePagerAdapter 时,最好单独处理 getItem() 而不要触摸 instantiateItem() 根本没有.PagerAdapter 上的 instantiateItem()-destroyItem()-isViewFromObject() 接口是一个较低级别的接口,FragmentPagerAdapter 使用它来实现更简单的getItem() 接口.

在进入这个之前,我应该澄清一下

<块引用>

如果要切换出正在显示的实际片段,则需要避免 FragmentPagerAdapter 并使用FragmentStatePagerAdapter.

此答案的早期版本错误地将 FragmentPagerAdapter 用作其示例 - 这是行不通的,因为 FragmentPagerAdapter 在第一次显示后永远不会销毁片段.

我不推荐您链接的帖子中提供的 setTag()findViewWithTag() 解决方法.正如您所发现的,使用 setTag()findViewWithTag() 不适用于片段,因此它不是一个很好的匹配.

正确的解决方案是覆盖getItemPosition().当调用 notifyDataSetChanged() 时,ViewPager 对其适配器中的所有项目调用 getItemPosition() 以查看它们是否需要移动到不同的位置或删除.

默认情况下,getItemPosition() 返回 POSITION_UNCHANGED,这意味着这个对象在它所在的地方很好,不要破坏或移除它."返回 POSITION_NONE 解决了这个问题,而是说这个对象不再是我正在显示的项目,删除它."因此,它具有删除和重新创建适配器中每个项目的效果.

这是一个完全合法的修复!此修复程序使 notifyDataSetChanged 表现得像一个没有视图回收的常规适配器.如果您实施此修复并且性能令人满意,那么您就可以参加比赛了.工作完成.

如果您需要更好的性能,可以使用更高级的 getItemPosition() 实现.这是一个寻呼机从字符串列表中创建片段的示例:

ViewPager pager =/* 获取我的 ViewPager */;//假设这里面确实有东西最终的 ArrayListtitles = new ArrayList();FragmentManager fm = getSupportFragmentManager();pager.setAdapter(new FragmentStatePagerAdapter(fm) {公共 int getCount() {返回 titles.size();}公共片段 getItem(int position) {MyFragment 片段 = new MyFragment();fragment.setTitle(titles.get(position));返回片段;}公共 int getItemPosition(对象项目){MyFragment 片段 = (MyFragment)item;字符串标题 = fragment.getTitle();int position = titles.indexOf(title);如果(位置> = 0){返回位置;} 别的 {返回 POSITION_NONE;}}});

使用此实现,只会显示显示新标题的片段.任何显示标题仍在列表中的片段将被移动到列表中的新位置,而标题根本不再在列表中的片段将被销毁.

如果片段没有被重新创建,但无论如何都需要更新怎么办?对活动片段的更新最好由片段本身处理.毕竟,这是拥有片段的优势——它是它自己的控制器.片段可以在 onCreate() 中向另一个对象添加侦听器或观察者,然后在 onDestroy() 中删除它,从而管理更新本身.您不必像在 ListView 或其他 AdapterView 类型的适配器中那样将所有更新代码放在 getItem() 中.

最后一件事 - 仅仅因为 FragmentPagerAdapter 不销毁片段并不意味着 getItemPosition 在 FragmentPagerAdapter 中完全无用.您仍然可以使用此回调在 ViewPager 中重新排序您的片段.但是,它永远不会将它们从 FragmentManager 中完全删除.

I can't update the content in ViewPager.

What is the 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. Except I can't change the content.

So I found this: ViewPager PagerAdapter not updating the View

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

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

I read 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 get it.

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

解决方案

When using FragmentPagerAdapter or FragmentStatePagerAdapter, it is best to deal solely with getItem() and not touch instantiateItem() at all. The instantiateItem()-destroyItem()-isViewFromObject() interface on PagerAdapter is a lower-level interface that FragmentPagerAdapter uses to implement the much simpler getItem() interface.

Before getting into this, I should clarify that

if you want to switch out the actual fragments that are being displayed, you need to avoid FragmentPagerAdapter and use FragmentStatePagerAdapter.

An earlier version of this answer made the mistake of using FragmentPagerAdapter for its example - that won't work because FragmentPagerAdapter never destroys a fragment after it's been displayed the first time.

I don't recommend the setTag() and findViewWithTag() workaround provided in the post you linked. As you've discovered, using setTag() and findViewWithTag() doesn't work with fragments, so it's not a good match.

The right solution is to override getItemPosition(). When notifyDataSetChanged() is called, ViewPager calls getItemPosition() on all the items in its adapter to see whether they need to be moved to a different position or removed.

By default, getItemPosition() returns POSITION_UNCHANGED, which means, "This object is fine where it is, don't destroy or remove it." Returning POSITION_NONE fixes the problem by instead saying, "This object is no longer an item I'm displaying, remove it." So it has the effect of removing and recreating every single item in your adapter.

This is a completely legitimate fix! This fix makes notifyDataSetChanged behave like a regular Adapter without view recycling. If you implement this fix and performance is satisfactory, you're off to the races. Job done.

If you need better performance, you can use a fancier getItemPosition() implementation. Here's an example for a pager creating fragments off of a list of strings:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

With this implementation, only fragments displaying new titles will get displayed. Any fragments displaying titles that are still in the list will instead be moved around to their new position in the list, and fragments with titles that are no longer in the list at all will be destroyed.

What if the fragment has not been recreated, but needs to be updated anyway? Updates to a living fragment are best handled by the fragment itself. That's the advantage of having a fragment, after all - it is its own controller. A fragment can add a listener or an observer to another object in onCreate(), and then remove it in onDestroy(), thus managing the updates itself. You don't have to put all the update code inside getItem() like you do in an adapter for a ListView or other AdapterView types.

One last thing - just because FragmentPagerAdapter doesn't destroy a fragment doesn't mean that getItemPosition is completely useless in a FragmentPagerAdapter. You can still use this callback to reorder your fragments in the ViewPager. It will never remove them completely from the FragmentManager, though.

这篇关于动态更新 ViewPager?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆