【问题标题】:How to retain ViewPager state inside a RecyclerView?如何在 RecyclerView 中保留 ViewPager 状态?
【发布时间】:2015-12-14 19:04:59
【问题描述】:

我的 RecyclerView 包含许多 ViewPager。每个 ViewPager 中的页面数是恒定的 (2)。我使用 PagerAdapter。

当我滚动我的 RecyclerView 时,ViewPager 会失去它们的状态并重置到第一页。我想知道如何为每个 ViewPager 保留最后选择的页面。

例如: 1)我在第一个 ViewPager 中滚动到第 2 页 2)我将 RecyclerView 滚动到底部,我的第一个 ViewPager 走出屏幕。 3)我滚动到 ViewPager 的顶部,所以我的第一个 ViewPager 再次加载。 4) 我的第一个 ViewPager 设置为第 1 页,而不是我最后滚动到的第 2 页。

RecyclerView 中有“n”个 ViewPager,我想保留它们的所有状态,无论由于 RecyclerView 是否重新加载。

如何解决这个逻辑?

【问题讨论】:

  • 如果您使用的是FragmentPagerAdapter,请使用状态感知适配器fx,请使用FragmentStatePagerAdapter
  • 不,我没有使用 FragmentPagerAdapter。我正在使用 PagerAdapter。不确定基本 PagerAdapter 是否有任何状态感知适配器。
  • 您可以将当前项目保存在PagerAdaptersaveState() 方法中,然后在restoreState() 中恢复它? (胡乱猜测,不知道这些方法能不能用)

标签: android android-viewpager android-recyclerview


【解决方案1】:

好吧,我为此写了一点蛮力解决方案。

我的PagerAdapter

public class ViewPagerAdapter extends PagerAdapter {

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        FrameLayout layout = new FrameLayout(container.getContext());
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        layout.setLayoutParams(layoutParams);

        layout.setBackgroundColor(position % 2 == 0 ? Color.RED : Color.BLUE);
        container.addView(layout);
        return layout;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
    }

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

(所以我们视觉上的“状态”在颜色上有所不同——第一个位置是红色,第二个位置是蓝色)

RecyclerViewAdapter 看起来像:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public HashMap<Integer, Integer> viewPageStates = new HashMap<>();

    public RecyclerViewAdapter() {}

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        return new ViewHolder(new ViewWithViewPager(viewGroup.getContext()));
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((ViewWithViewPager) holder.itemView).refreshState(position, viewPageStates.containsKey(position)? viewPageStates.get(position) : 0);
    }

    @Override
    public int getItemCount() {
        return 42;
    }

    @Override
    public void onViewRecycled(RecyclerView.ViewHolder holder) {
        ViewWithViewPager recycledViewPager = ((ViewWithViewPager)holder.itemView);
        viewPageStates.put(recycledViewPager.viewPosition, recycledViewPager.viewPager.getCurrentItem());
        super.onViewRecycled(holder);
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public ViewHolder(View itemView) {
            super(itemView);
        }
    }
}   

即每次我们绑定RecyclerViewViewHolder 时,我们都会传递viewpager 的当前状态(来自viewPageStates)及其在RecyclerView 数据集中的顺序。 一旦它被回收 - 我们读取它的当前页面状态并将其保存到 HashMap viewPageStates

最后一块 - ViewWithViewPager

public class ViewWithViewPager extends FrameLayout {

    ViewPager viewPager;
    int viewPosition = 0;

    public ViewWithViewPager(Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.item_layout, this);
        viewPager = (ViewPager) findViewById(R.id.viewPager);
        viewPager.setAdapter(new ViewPagerAdapter());
    }

    public void refreshState(int position, int selectedPage) {
        viewPosition = position;
        viewPager.setCurrentItem(selectedPage);
    }
}

即在refreshState 中,我们将 ViewPager 设置为正确的页面

【讨论】:

  • 您可以在 RecyclerView 中使用public void onViewRecycled(VH holder) 方法,当适配器创建的视图被回收时调用它。您可以在调用此方法时保存当前状态,不再需要 ViewWithViewPager :)
  • @atabouraya 公平点进行优化,改进了我的答案,谢谢!
  • 感谢康斯坦丁和 atabouraya。 onViewRecycled() 是我需要的。它帮助我解决了问题。
  • 这可能会带来一些问题,就像您想在列表中删除或插入项目一样。因为您将 recyclerview 位置作为键存储在 viewPageStates 中,所以每次插入或删除项目(或任何类型的项目顺序更改)时,您都需要更新 HashMap 中的键以使其与 recyclerview 本身保持同步。
  • 如果您的项目具有唯一的 ID,那么我认为更好的方法是使用这些 ID 作为 viewPageStates HashMap 中的键。这样您就不必每次在 recyclerview 中发生项目订单更改时都更新密钥。如果您的项目数少于几百,您也可以使用 SparseIntArray 而不是 HashMap 以获得更好的性能。
【解决方案2】:

OP 的解决方案。

  • 在您的内部覆盖 onViewRecycled(ViewHolder viewHolder){} RecyclerView.Adapter
  • 使用 HashMap 来存储 ViewPager 在 RecyclerView 和对应的当前选中页面 ViewPager。
  • 在 onViewRecycled() 方法中输入上面的值 创建了 HashMap。
  • 在 onBindViewHolder() 方法中加载保存的状态/页面 ViewPager 从 HashMap 使用位置并设置保存 使用 setCurrentItem() 方法将状态/页面添加到 ViewPager。

感谢 Konstantin 和 atabouraya。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    相关资源
    最近更新 更多