Recently, two RecyclerView nested layout was used in the project, that is, the item of RecyclerView is also RecyclerView, which encountered two typical problems: 1. When the direction of item is vertical, the parent RecyclerView will shift when it is loaded for the first time; 2. When the direction of item is horizontal, after the parent RecyclerView slides up and down, the position of the child RecyclerView will be restored. This paper mainly solves the above two problems. Let’s take a look at these two questions:





Before the repair. GIF

It can be obviously seen that when the Recyclerview of item is vertical, “Title1” disappears when the page is opened. When the Recyclerview of item is horizontal, we slide Inner Title1-X and Inner Title2-X a certain distance, then slide the parent Recyclerview up and down. Inner Title1-X and Inner Title2-x are restored.

First, solve the vertical nesting problem, this is relatively simple, mainly caused by the Recyclerview to grab the focus, we just need to let the parent layout to get the focus can be solved, the complete code is as follows:

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="vertical" android:paddingLeft="14dp" android:paddingRight="14dp" android:focusableInTouchMode="true" android:focusable="true" > <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="20dp" android:paddingBottom="20dp" android:layout_gravity="center_vertical" android:layout_marginRight="30dp" android:gravity="center" android:maxLines="1" android:textColor="#333333" android:textSize="18sp" tools:text="2017-06"/>  <android.support.v7.widget.RecyclerView android:id="@+id/rv_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fff" /> </LinearLayout>Copy the code

The key code is this:

android:focusableInTouchMode="true"
android:focusable="true"Copy the code

I have submitted the full source code to GitHub with the layout: item_vertical.xml, and the repository address will be provided later.

2.2 Solve the problem of horizontal nesting and parent Recyclerview sliding position restore. The solution of this problem is clear: 1. Set up the sliding monitoring of Recyclerview, at the end of each sliding, record the sliding position, including position and offset; 2. Call LinearLayoutManager. ScrollToPositionWithOffset slide () method to the last position of the record. Therefore, the key point to solve the problem is to calculate the sliding position and offset. The key code is as follows:

@Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState) { case RecyclerView.SCROLL_STATE_IDLE: int offset = recyclerView.computeHorizontalScrollOffset(); mEntity.scrollPosition = mLayoutManager.findFirstVisibleItemPosition() < 0 ? mEntity.scrollPosition : mLayoutManager.findFirstVisibleItemPosition() + 1; if (mItemWidth <= 0) { View item = mLayoutManager.findViewByPosition(mEntity.scrollPosition); if (item ! = null) { RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) item.getLayoutParams(); mItemWidth = item.getWidth(); mItemMargin = layoutParams.rightMargin; }} if (offset > 0 && mItemWidth > 0) {//offset % mItemWidth: Margin of all items (0 to position) // Use the width of the current item - all margin-current position slip distance, to get offset. mEntity.scrollOffset = mItemWidth - offset % mItemWidth + mEntity.scrollPosition * mItemMargin; } break; }}Copy the code

The calculation of position is relatively simple and can be directly called:

LinearLayoutManager.findFirstVisibleItemPosition() + 1Copy the code

The calculation of offset is a little more complicated, and the calculation formula is as follows:

// mentity. scrollPosition * mItemMargin: Margin of all items (0 to position) // Use the width of the current item - all margin-current position slip distance, to get offset. mEntity.scrollOffset = mItemWidth - offset % mItemWidth + mEntity.scrollPosition * mItemMargin;Copy the code

So far, the position and offset of the slide have been obtained. Next, you only need to call the following code to restore the position of the last slide and solve the position restore problem.

layoutManager.scrollToPositionWithOffset(item.scrollPosition, item.scrollOffset);Copy the code

The complete code for horizontalAdapter.class is as follows:

public class HorizontalAdapter extends BaseQuickAdapter<Entity, BaseViewHolder> { public static final String TAG = HorizontalAdapter.class.getSimpleName(); public HorizontalAdapter(List<Entity> data) { super(R.layout.item_horizontal, data); } @Override protected void convert(BaseViewHolder helper, Entity item) { helper.setText(R.id.tv_title, item.title); final RecyclerView recyclerView = helper.getView(R.id.rv_item); final LinearLayoutManager layoutManager = new LinearLayoutManager(mContext); layoutManager.setOrientation(OrientationHelper.HORIZONTAL); recyclerView.setLayoutManager(layoutManager); InnerAdapter innerAdapter = new InnerAdapter(item.innerEntities); recyclerView.setAdapter(innerAdapter); if (item.scrollOffset > 0) { layoutManager.scrollToPositionWithOffset(item.scrollPosition, item.scrollOffset); } recyclerView.addOnScrollListener(new MyOnScrollListener(item, layoutManager)); } private class MyOnScrollListener extends RecyclerView.OnScrollListener { private LinearLayoutManager mLayoutManager; private Entity mEntity; private int mItemWidth; private int mItemMargin; public MyOnScrollListener(Entity shopItem, LinearLayoutManager layoutManager) { mLayoutManager = layoutManager; mEntity = shopItem; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState) { case RecyclerView.SCROLL_STATE_IDLE: int offset = recyclerView.computeHorizontalScrollOffset(); mEntity.scrollPosition = mLayoutManager.findFirstVisibleItemPosition() < 0 ? mEntity.scrollPosition : mLayoutManager.findFirstVisibleItemPosition() + 1; if (mItemWidth <= 0) { View item = mLayoutManager.findViewByPosition(mEntity.scrollPosition); if (item ! = null) { RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) item.getLayoutParams(); mItemWidth = item.getWidth(); mItemMargin = layoutParams.rightMargin; }} if (offset > 0 && mItemWidth > 0) {//offset % mItemWidth: Margin of all items (0 to position) // Use the width of the current item - all margin-current position slip distance, to get offset. mEntity.scrollOffset = mItemWidth - offset % mItemWidth + mEntity.scrollPosition * mItemMargin; } break; } } } private class InnerAdapter extends BaseQuickAdapter<Entity.InnerEntity, BaseViewHolder> { public InnerAdapter(List<Entity.InnerEntity> datas) { super(R.layout.item_horizontal_inner, datas); } @Override protected void convert(final BaseViewHolder helper, final Entity.InnerEntity item) { helper.setText(R.id.title, item.innerTitle); ((ImageView) helper.getView(R.id.iv)).setImageResource(item.innerImageId); }}}Copy the code

Take a look at the solution:





After repair. GIF

Third, due to time relationship (is lazy), don’t want to write adapter duplicate code, so direct use of BaseRecyclerViewAdapterHelper, thank the authors provide a good framework, if it is to write their own adapter code, Simply implement the convert code in onBindViewHolder().

Complete Project Download

If you like, please start it. If you have any questions, please feedback in the issue or comments.