9
votes

My RecyclerView contains many ViewPagers. The number of pages inside each ViewPager is constant (2). I use PagerAdapter.

When I scroll my RecyclerView, the ViewPagers lose their state and reset to the 1st page. I want to know how to retain the last selected page for each ViewPager.

For example: 1) I scroll to page 2 in my 1st ViewPager 2) I scroll the RecyclerView to the bottom and my 1st ViewPager goes out of screen. 3) I scroll to the top of the ViewPager and so my 1st ViewPager loads again. 4) My 1st ViewPager is set to 1st page instead of page 2 which I lastly scrolled to.

There are 'n' number of ViewPagers inside the RecyclerView and I want to retain all their states regardless of reloading due to RecyclerView.

How to solve this logic?

2
use state-aware adapter fx if you are using FragmentPagerAdapter use FragmentStatePagerAdapterSelvin
No I'm not using a FragmentPagerAdapter. I am using PagerAdapter. Not sure if there is any state aware adapter for the basic PagerAdapter.Sanju
You could maybe save the current item in the PagerAdapter's saveState() method, then restore it in restoreState()? (It's a wild guess, I don't know if these methods can be used for that)Marc Plano-Lesay

2 Answers

5
votes

Well, I wrote little bit brute-force solution for this.

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

(so we the visually "state" differs in it's color - it's red in first position and blue in the second position)

The RecyclerViewAdapter looks like:

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

I.e. every time we bind RecyclerView's ViewHolder, we pass the current state of the viewpager(from viewPageStates) and its order in RecyclerView's dataset. Once it's recycled - we read its current page state and saving it into HashMap viewPageStates.

The last piece - the 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);
    }
}

I.e. in refreshState we set ViewPager to the proper page

0
votes

Solution by OP.

  • Override onViewRecycled(ViewHolder viewHolder){} inside your RecyclerView.Adapter
  • Use a HashMap to store the position of the ViewPager in the RecyclerView and the corresponding current selected page of the ViewPager.
  • Inside the onViewRecycled() method input the values to the above created HashMap.
  • In the onBindViewHolder() method load the saved state/page of the ViewPager from the HashMap using the position and set the saved state/page to the ViewPager using the setCurrentItem() method.

Thanks to Konstantin and atabouraya.