如何在RecyclerView中保留ViewPager的状态?

9
我的RecyclerView包含许多ViewPagers。每个ViewPager内部的页面数量是固定的(2)。我使用PagerAdapter。
当我滚动RecyclerView时,ViewPagers会失去它们的状态并重置为第1页。我想知道如何保留每个ViewPager的上次选择的页面。
例如: 1)我在我的第1个ViewPager中滚动到第2页 2)我滚动RecyclerView到底部,我的第1个ViewPager消失了。 3)我滚动到ViewPager的顶部,所以我的第1个ViewPager再次加载。 4)我的第1个ViewPager设置为第1页,而不是我最后滚动到的第2页。
RecyclerView内有'n'个ViewPagers,我想保留它们所有的状态,无论RecyclerView如何重新加载。
如何解决这个逻辑?

1
如果您正在使用FragmentPagerAdapter,请使用状态感知适配器fx,而如果您使用的是FragmentStatePagerAdapter,则使用它。 - Selvin
不,我没有使用FragmentPagerAdapter。我正在使用PagerAdapter。不确定基本PagerAdapter是否有任何状态感知适配器。 - Sanju
也许你可以在 PagerAdaptersaveState() 方法中保存当前项目,然后在 restoreState() 中恢复它?(这只是一个猜测,我不知道这些方法是否可用于此。) - Marc Plano-Lesay
2个回答

5

我为此编写了一个有点暴力的解决方案。

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

每次我们绑定 RecyclerView 的 ViewHolder 时,都会传递 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设置到正确的页面。


2
你可以在RecyclerView中使用public void onViewRecycled(VH holder)方法,当一个由适配器创建的视图被回收时,它就会被调用。在这个方法被调用时,你可以保存当前状态,因此你不再需要ViewWithViewPager :) - atabouraya
@atabouraya 说得对,针对优化做了改进,谢谢! - Konstantin Loginov
感谢Konstantin和atabouraya。onViewRecycled()正是我所需要的。它帮助我解决了问题。 - Sanju
这可能会带来问题,如果您想要删除或插入列表中的项目。因为您将recyclerview位置存储为viewPageStates中的键,每次插入或删除项目(或任何类型的项目顺序更改)时,您需要更新HashMap中的键以使其与recyclerview本身同步。 - Mustafa Berkay Mutlu
如果您的项目有唯一的ID,则我认为更好的方法是将这些ID用作viewPageStates HashMap中的键。这样,每次recyclerview中的项顺序发生变化时,您就不必更新键。此外,如果您的项目数量少于几百个,则可以使用SparseIntArray而不是HashMap<Integer,Integer>以获得更好的性能。 - Mustafa Berkay Mutlu

0

OP的解决方案。

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

感谢Konstantin和atabouraya。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接