动态更新 ViewPager?

新手上路,请多包涵

我无法更新 ViewPager 中的内容。

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

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

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

这运作良好。除了我不能改变内容。

所以我发现了这个: ViewPager PagerAdapter not updates the View

“我的方法是对 instanceItem() 方法中的任何实例化视图使用 setTag() 方法”

现在我想实现 instantiateItem() 来做到这一点。但我不知道我必须返回什么(类型是 Object)以及与 getItem(int position) 的关系是什么?

我阅读了 参考资料

  • 公共抽象片段 getItem(int 位置)

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

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

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

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

退货

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

但我还是不明白。

这是我的代码。我正在使用支持包 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);
    }
}

我的片段适配器

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

我的片段

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

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

原文由 User 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

使用 FragmentPagerAdapter 或 FragmentStatePagerAdapter 时,最好单独处理 getItem() 而完全不接触 instantiateItem() 。 The instantiateItem() - destroyItem() - isViewFromObject() interface on PagerAdapter is a lower-level interface that FragmentPagerAdapter uses to implement the much getItem() interface.

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

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

该答案的早期版本错误地将 FragmentPagerAdapter 用作其示例 - 这将不起作用,因为 FragmentPagerAdapter 在第一次显示后永远不会破坏片段。

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

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

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

这是一个完全合法的修复!此修复使 notifyDataSetChanged 的行为类似于常规适配器,无需视图回收。如果您实施此修复并且性能令人满意,那么您就可以参加比赛了。任务完成。

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

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

使用此实现,只会显示显示新标题的片段。任何显示仍在列表中的标题的片段都将被移动到它们在列表中的新位置,并且标题不再在列表中的片段将被销毁。

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

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

原文由 Bill Phillips 发布,翻译遵循 CC BY-SA 3.0 许可协议

使用 ViewPager2FragmentStateAdapter

ViewPager2 支持动态更新数据。

文档 中有一条关于如何使其工作的重要说明:

注意: DiffUtil 实用程序类依赖于通过 ID 识别项目。 如果您使用 ViewPager2 对可变集合进行分页,则还必须覆盖 getItemId()containsItem() (强调我的)

基于 ViewPager2 文档 和 Android 的 Github 示例项目,我们需要采取一些步骤:

  1. Set up FragmentStateAdapter and override the following methods: getItemCount , createFragment , getItemId , and containsItem (note: FragmentStatePagerAdapter 不受 ViewPager2 支持)

  2. 将适配器连接到 ViewPager2

  3. 调度列表更新到 ViewPager2DiffUtil (不需要使用 DiffUtil ,如示例项目所示)


例子:

 private val items: List<Int>
    get() = viewModel.items
private val viewPager: ViewPager2 = binding.viewPager

private val adapter = object : FragmentStateAdapter(this@Fragment) {
    override fun getItemCount() = items.size
    override fun createFragment(position: Int): Fragment = MyFragment()
    override fun getItemId(position: Int): Long = items[position].id
    override fun containsItem(itemId: Long): Boolean = items.any { it.id == itemId }
}
viewPager.adapter = adapter

private fun onDataChanged() {
    DiffUtil
        .calculateDiff(object : DiffUtil.Callback() {
            override fun getOldListSize(): Int = viewPager.adapter.itemCount
            override fun getNewListSize(): Int = viewModel.items.size
            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                viewPager.adapter?.getItemId(oldItemPosition) == viewModel.items[newItemPosition].id

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
                areItemsTheSame(oldItemPosition, newItemPosition)
        }, false)
        .dispatchUpdatesTo(viewPager.adapter!!)
}

原文由 jacoballenwood 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题