How to Add a Recyclerview Inside Another Recyclerview

How to add a recyclerView inside another recyclerView

I would like to suggest to use a single RecyclerView and populate your list items dynamically. I've added a github project to describe how this can be done. You might have a look. While the other solutions will work just fine, I would like to suggest, this is a much faster and efficient way of showing multiple lists in a RecyclerView.

The idea is to add logic in your onCreateViewHolder and onBindViewHolder method so that you can inflate proper view for the exact positions in your RecyclerView.

I've added a sample project along with that wiki too. You might clone and check what it does. For convenience, I am posting the adapter that I have used.

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

private static final int FOOTER_VIEW = 1;
private static final int FIRST_LIST_ITEM_VIEW = 2;
private static final int FIRST_LIST_HEADER_VIEW = 3;
private static final int SECOND_LIST_ITEM_VIEW = 4;
private static final int SECOND_LIST_HEADER_VIEW = 5;

private ArrayList<ListObject> firstList = new ArrayList<ListObject>();
private ArrayList<ListObject> secondList = new ArrayList<ListObject>();

public DynamicListAdapter() {
}

public void setFirstList(ArrayList<ListObject> firstList) {
this.firstList = firstList;
}

public void setSecondList(ArrayList<ListObject> secondList) {
this.secondList = secondList;
}

public class ViewHolder extends RecyclerView.ViewHolder {
// List items of first list
private TextView mTextDescription1;
private TextView mListItemTitle1;

// List items of second list
private TextView mTextDescription2;
private TextView mListItemTitle2;

// Element of footer view
private TextView footerTextView;

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

// Get the view of the elements of first list
mTextDescription1 = (TextView) itemView.findViewById(R.id.description1);
mListItemTitle1 = (TextView) itemView.findViewById(R.id.title1);

// Get the view of the elements of second list
mTextDescription2 = (TextView) itemView.findViewById(R.id.description2);
mListItemTitle2 = (TextView) itemView.findViewById(R.id.title2);

// Get the view of the footer elements
footerTextView = (TextView) itemView.findViewById(R.id.footer);
}

public void bindViewSecondList(int pos) {

if (firstList == null) pos = pos - 1;
else {
if (firstList.size() == 0) pos = pos - 1;
else pos = pos - firstList.size() - 2;
}

final String description = secondList.get(pos).getDescription();
final String title = secondList.get(pos).getTitle();

mTextDescription2.setText(description);
mListItemTitle2.setText(title);
}

public void bindViewFirstList(int pos) {

// Decrease pos by 1 as there is a header view now.
pos = pos - 1;

final String description = firstList.get(pos).getDescription();
final String title = firstList.get(pos).getTitle();

mTextDescription1.setText(description);
mListItemTitle1.setText(title);
}

public void bindViewFooter(int pos) {
footerTextView.setText("This is footer");
}
}

public class FooterViewHolder extends ViewHolder {
public FooterViewHolder(View itemView) {
super(itemView);
}
}

private class FirstListHeaderViewHolder extends ViewHolder {
public FirstListHeaderViewHolder(View itemView) {
super(itemView);
}
}

private class FirstListItemViewHolder extends ViewHolder {
public FirstListItemViewHolder(View itemView) {
super(itemView);
}
}

private class SecondListHeaderViewHolder extends ViewHolder {
public SecondListHeaderViewHolder(View itemView) {
super(itemView);
}
}

private class SecondListItemViewHolder extends ViewHolder {
public SecondListItemViewHolder(View itemView) {
super(itemView);
}
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View v;

if (viewType == FOOTER_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent, false);
FooterViewHolder vh = new FooterViewHolder(v);
return vh;

} else if (viewType == FIRST_LIST_ITEM_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_first_list, parent, false);
FirstListItemViewHolder vh = new FirstListItemViewHolder(v);
return vh;

} else if (viewType == FIRST_LIST_HEADER_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_first_list_header, parent, false);
FirstListHeaderViewHolder vh = new FirstListHeaderViewHolder(v);
return vh;

} else if (viewType == SECOND_LIST_HEADER_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_second_list_header, parent, false);
SecondListHeaderViewHolder vh = new SecondListHeaderViewHolder(v);
return vh;

} else {
// SECOND_LIST_ITEM_VIEW
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_second_list, parent, false);
SecondListItemViewHolder vh = new SecondListItemViewHolder(v);
return vh;
}
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

try {
if (holder instanceof SecondListItemViewHolder) {
SecondListItemViewHolder vh = (SecondListItemViewHolder) holder;
vh.bindViewSecondList(position);

} else if (holder instanceof FirstListHeaderViewHolder) {
FirstListHeaderViewHolder vh = (FirstListHeaderViewHolder) holder;

} else if (holder instanceof FirstListItemViewHolder) {
FirstListItemViewHolder vh = (FirstListItemViewHolder) holder;
vh.bindViewFirstList(position);

} else if (holder instanceof SecondListHeaderViewHolder) {
SecondListHeaderViewHolder vh = (SecondListHeaderViewHolder) holder;

} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
vh.bindViewFooter(position);
}
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public int getItemCount() {

int firstListSize = 0;
int secondListSize = 0;

if (secondList == null && firstList == null) return 0;

if (secondList != null)
secondListSize = secondList.size();
if (firstList != null)
firstListSize = firstList.size();

if (secondListSize > 0 && firstListSize > 0)
return 1 + firstListSize + 1 + secondListSize + 1; // first list header, first list size, second list header , second list size, footer
else if (secondListSize > 0 && firstListSize == 0)
return 1 + secondListSize + 1; // second list header, second list size, footer
else if (secondListSize == 0 && firstListSize > 0)
return 1 + firstListSize; // first list header , first list size
else return 0;
}

@Override
public int getItemViewType(int position) {

int firstListSize = 0;
int secondListSize = 0;

if (secondList == null && firstList == null)
return super.getItemViewType(position);

if (secondList != null)
secondListSize = secondList.size();
if (firstList != null)
firstListSize = firstList.size();

if (secondListSize > 0 && firstListSize > 0) {
if (position == 0) return FIRST_LIST_HEADER_VIEW;
else if (position == firstListSize + 1)
return SECOND_LIST_HEADER_VIEW;
else if (position == secondListSize + 1 + firstListSize + 1)
return FOOTER_VIEW;
else if (position > firstListSize + 1)
return SECOND_LIST_ITEM_VIEW;
else return FIRST_LIST_ITEM_VIEW;

} else if (secondListSize > 0 && firstListSize == 0) {
if (position == 0) return SECOND_LIST_HEADER_VIEW;
else if (position == secondListSize + 1) return FOOTER_VIEW;
else return SECOND_LIST_ITEM_VIEW;

} else if (secondListSize == 0 && firstListSize > 0) {
if (position == 0) return FIRST_LIST_HEADER_VIEW;
else return FIRST_LIST_ITEM_VIEW;
}

return super.getItemViewType(position);
}
}

There is another way of keeping your items in a single ArrayList of objects so that you can set an attribute tagging the items to indicate which item is from first list and which one belongs to second list. Then pass that ArrayList into your RecyclerView and then implement the logic inside adapter to populate them dynamically.

Hope that helps.

How can i create a RecyclerView inside of the another recyclerview's item?

Try this code

- MainRecyclerView Item

item_post.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<LinearLayout
android:id="@+id/llItemPost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/_5sdp">

<TextView
android:id="@+id/txtPostCname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Company Name"
android:textColor="@color/Blue"
android:textSize="@dimen/_21ssp"
android:textStyle="bold" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/_8sdp"
android:layout_marginLeft="@dimen/_8sdp"
android:layout_marginTop="@dimen/_8sdp"
android:text="Post:-"
android:textColor="@color/Blue"
android:textSize="@dimen/_17ssp"
android:textStyle="bold" />

<android.support.v7.widget.RecyclerView
android:id="@+id/rvPostSub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/_5sdp"
android:paddingBottom="@dimen/_10sdp">

</android.support.v7.widget.RecyclerView>

</LinearLayout>

</LinearLayout>

- MainRecyclerView Adapter

PostAdapter.class

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

private static final String TAG = "PostAdapter";

private PostSubAdapter postSubAdapter;
private ArrayList<String> arrayList;
private Context mContext;
private ArrayList<String> yourSubArrayList;

public PostAdapter(ArrayList<String> arrayList, ArrayList<String> yourSubArrayList, Context mContext) {
this.arrayList = arrayList;
this.yourSubArrayList = yourSubArrayList;
this.mContext = mContext;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_post, viewGroup, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int i) {

viewHolder.txtPostCname.setText(arrayList.get(i));

viewHolder.rvPostSub.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false));
postSubAdapter = new PostSubAdapter(yourSubArrayList, mContext);
viewHolder.rvPostSub.setAdapter(postSubAdapter);
postSubAdapter.notifyDataSetChanged();

}

@Override
public int getItemCount() {
return arrayList.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView rvPostSub;
TextView txtPostCname;

public ViewHolder(@NonNull View itemView) {
super(itemView);
txtPostCname = itemView.findViewById(R.id.txtPostCname);
rvPostSub = itemView.findViewById(R.id.rvPostSub);
}
}

@Override
public int getItemViewType(int position) {
return position;
}

@Override
public long getItemId(int position) {
return position;
}
}

- SubRecyclerView Item

item_post_sub.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/txtPost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/_12sdp"
android:layout_marginEnd="@dimen/_12sdp"
android:text="Post"
android:textColor="#000000"
android:textSize="@dimen/_13ssp" />

<View
android:visibility="visible"
android:id="@+id/viewDivider"
android:layout_width="match_parent"
android:layout_height="@dimen/_2sdp"
android:layout_marginLeft="@dimen/_8sdp"
android:layout_marginTop="@dimen/_5sdp"
android:layout_marginRight="@dimen/_8sdp"
android:layout_marginBottom="@dimen/_5sdp"
android:backgroundTint="@color/Blue"
android:background="@drawable/social_media_divider" />

</LinearLayout>

- SubRecyclerView Adapter

PostSubAdapter.class

public class PostSubAdapter extends RecyclerView.Adapter<PostSubAdapter.ViewHolder> {
private ArrayList<String> arrayList;
Context mContext;

public PostSubAdapter(ArrayList<String> arrayList, Context mContext) {
this.arrayList = arrayList;
this.mContext = mContext;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_post_sub, viewGroup, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
String post=arrayList.get(i).replace("\\n","\n");
viewHolder.txtPost.setText(post);
if(i==arrayList.size()-1)
{
viewHolder.viewDivider.setVisibility(View.GONE);
}
}

@Override
public int getItemCount() {
return arrayList.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {
TextView txtPost;
View viewDivider;
public ViewHolder(@NonNull View itemView) {
super(itemView);
txtPost=itemView.findViewById(R.id.txtPost);
viewDivider=itemView.findViewById(R.id.viewDivider);
}
}

@Override
public int getItemViewType(int position) {
return position;
}

@Override
public long getItemId(int position) {
return position;
}
}

I hope this can help You!

Thank You.

Scrolling a RecyclerView inside another RecyclerView automatically not working correctly

Handler is not an issue here its the Runnable. you are using and posting same Runnable each time thats why its getting piled up . You can not remove the previous call because you do not have a Tag or token to this delayed call . take a look at some of Handler's method like sendMessageDelayed these might help .

After giving it some thought i think you can move the Auto scroll part to SnapHelper. Not a full prove solution but i think it will work. You might have to put few checks in SnapHelper . Give it a try and let me know . i haven't tested it.

class AutoPagedSnapHelper(private var autoScrollInterval: Long) : PagerSnapHelper() {
private var recyclerView: RecyclerView? = null
private var currentPage = 0
private var isHold = false

private val autoScrollRunnable = Runnable {
recyclerView?.let {
if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_DRAGGING && !isHold) {
if (it.adapter != null) {
val lastPageIndex = (recyclerView?.adapter!!.itemCount - 1)
var nextIndex: Int
nextIndex = currentPage + 1
if (currentPage == lastPageIndex) {
nextIndex = 0
}
it.post {
val linearSmoothScroller = object : LinearSmoothScroller(recyclerView?.context) {
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
}
}
linearSmoothScroller.targetPosition = nextIndex
(recyclerView?.layoutManager as LinearLayoutManager).startSmoothScroll(linearSmoothScroller)
}
}
} else {
postNextPage()
}
}
}

override fun attachToRecyclerView(recyclerView: RecyclerView?) {
super.attachToRecyclerView(recyclerView)
if (this.recyclerView === recyclerView) {
return
}
if (autoScrollInterval != 0L) {
this.recyclerView = recyclerView
this.recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) {
val itemPosition = (recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
if (itemPosition != -1) {
currentPage = itemPosition
postNextPage()
}
}
}
})
postNextPage()
recyclerView?.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onInterceptTouchEvent(rv: RecyclerView, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
isHold = true
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
isHold = false
}
}
return false
}

override fun onTouchEvent(rv: RecyclerView, event: MotionEvent) {}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})
}
}

fun postNextPage() {
recyclerView?.handler?.removeCallbacks(autoScrollRunnable)
recyclerView?.postDelayed(autoScrollRunnable, autoScrollInterval)
}
companion object {
private const val MILLISECONDS_PER_INCH = 75f //default is 25f (bigger = slower)
}
}

This should take care of auto change page. You do not have to use scrollListener in Adapter. Give it a try.

Vertical RecyclerView nested inside vertical RecyclerView


Approach No.1: Using two nested RecyclerViews

Github sample

Pros:

  • Views are recycled (i.e. Good performance)
  • Semi-Seamless scrolling (after update 3 & 4)

Cons:

  • The programmatically propagated scroll during the transition from the inner to the outer scroll when the inner far end item is reached is not that smooth/natural like the gesture.
  • Complex code.

Well, I won't address the performance issues of vertically nested RecyclerViews; But notice that:

  • The inner RecyclerView probably loses the ability of recycling views; because the shown rows of the outer recyclerView should load their items entirely. (Thankfully it's not a right assumption as per the below UPDATE 1)
  • I declared a single adapter instance in the ViewHolder not in the onBindViewHolder to have a better performance by not creating a new adapter instance for the inner RecyclerView each time views are recycled.

The demo app represents the months of the year as the outer RecyclerView, and the day numbers of each month as inner RecyclerView.

The outer RecyclerView registers OnScrollListener that each time it's scrolled, we do this check on the inner RV:

  • If outer scrolling up: check if the inner first item is shown.
  • If outer scrolling down: check if the inner last item is shown.
    outerRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
if (dy > 0) //scrolled to BOTTOM
outerAdapter.isOuterScrollingDown(true, dy);
else if (dy < 0) //scrolled to TOP
outerAdapter.isOuterScrollingDown(false, dy);
}
});

In the outer adapter:

    public void isOuterScrollingDown(boolean scrollDown, int value) {
if (scrollDown) {
boolean isLastItemShown = currentLastItem == mMonths.get(currentPosition).dayCount;
if (!isLastItemShown) onScrollListener.onScroll(-value);
enableOuterScroll(isLastItemShown);

} else {
boolean isFirstItemShown = currentFirstItem == 1;
if (!isFirstItemShown) onScrollListener.onScroll(-value);
enableOuterScroll(isFirstItemShown);
}
if (currentRV != null)
currentRV.smoothScrollBy(0, 10 * value);
}

If the relevant item is not shown, then we decide to disable the outer RV scrolling. This is handled by a listener with a callback that accepts a boolean passed to a customized LinearLayoutManager class to the outer RV.

Likewise in order to re-enable scrolling of the outer RV: the inner RecyclerView registers OnScrollListener to check if the inner first/last item is shown.

innerRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {

if (!recyclerView.canScrollVertically(1) // Is it not possible to scroll more to bottom (i.e. Last item shown)
&& newState == RecyclerView.SCROLL_STATE_IDLE) {
enableOuterScroll(true);

} else if (!recyclerView.canScrollVertically(-1) // Is it possible to scroll more to top (i.e. First item shown)
&& newState == RecyclerView.SCROLL_STATE_IDLE) {
enableOuterScroll(true);
}
}
});

Still, there are glitches because disabling/enabling scrolling; we can't pass the scroll order to the other RV, until the next scroll. This is manipulated slightly by reversing the initial outer RV scroll value; and using an arbitrary scroll value to the inner with currentRV.smoothScrollBy(0, 10 * initialScroll). I wish if someone can suggest any other alternative to this.

UPDATE 1

  • The inner RecyclerView probably lose the ability of recycling views; because the shown rows of the outer recyclerView should load their items entirely.

Thankfully it's not the right assumption and the views are recycled by tracking the recycled list of items in the inner adapter using a List that hold the currently loaded items:

By assuming some month has a 1000 days "Feb as it's always oppressed :)", and scrolling up/down to notice the loaded list and make sure that onViewRecycled() get called.

public class InnerRecyclerAdapter extends RecyclerView.Adapter<InnerRecyclerAdapter.InnerViewHolder> {

private final ArrayList<Integer> currentLoadedPositions = new ArrayList<>();

@Override
public void onBindViewHolder(@NonNull InnerViewHolder holder, int position) {
holder.tvDay.setText(String.valueOf(position + 1));
currentLoadedPositions.add(position);
Log.d(LOG_TAG, "onViewRecycled: " + days + " " + currentLoadedPositions);
}

@Override
public void onViewRecycled(@NonNull InnerViewHolder holder) {
super.onViewRecycled(holder);
currentLoadedPositions.remove(Integer.valueOf(holder.getAdapterPosition()));
Log.d(LOG_TAG, "onViewRecycled: " + days + " " + currentLoadedPositions);
}

// Rest of code is trimmed

}

Logs:

onViewRecycled: 1000 [0]
onViewRecycled: 1000 [0, 1]
onViewRecycled: 1000 [0, 1, 2]
onViewRecycled: 1000 [0, 1, 2, 3]
onViewRecycled: 1000 [0, 1, 2, 3, 4]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5, 6]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5, 6, 7]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5, 6, 7, 8]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
onViewRecycled: 1000 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
onViewRecycled: 1000 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
onViewRecycled: 1000 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
onViewRecycled: 1000 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
onViewRecycled: 1000 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
onViewRecycled: 1000 [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
onViewRecycled: 1000 [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
onViewRecycled: 1000 [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
onViewRecycled: 1000 [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
onViewRecycled: 1000 [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
onViewRecycled: 1000 [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
onViewRecycled: 1000 [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
onViewRecycled: 1000 [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
onViewRecycled: 1000 [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
onViewRecycled: 1000 [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
onViewRecycled: 1000 [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
onViewRecycled: 1000 [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
onViewRecycled: 1000 [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
onViewRecycled: 1000 [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
onViewRecycled: 1000 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
onViewRecycled: 1000 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
onViewRecycled: 1000 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
onViewRecycled: 1000 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
onViewRecycled: 1000 [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
onViewRecycled: 1000 [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
onViewRecycled: 1000 [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
onViewRecycled: 1000 [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
onViewRecycled: 1000 [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
onViewRecycled: 1000 [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]

UPDATE 2

Still there are glitches because disabling/enabling scrolling; we can't pass the scroll order to the other RV, until the next scroll. This is manipulated slightly by reversing the initial outer RV scroll value; and using an arbitrary scroll value to the inner with currentRV.smoothScrollBy(0, 10 * initialScroll). I wish if someone can suggest any other alternative to this.

  • Using a greater arbitrary value (like 30) makes the grammatical scroll looks smoother >> currentRV.smoothScrollBy(0, 30 * initialScroll)

  • And scrolling the outer scroll without reversing the scroll, makes it also looks more natural in the same direction of the scroll:

if (scrollDown) {
boolean isLastItemShown = currentLastItem == mMonths.get(currentPosition).dayCount;
if (!isLastItemShown) onScrollListener.onScroll(value);
enableOuterScroll(isLastItemShown);

} else {
boolean isFirstItemShown = currentFirstItem == 1;
if (!isFirstItemShown) onScrollListener.onScroll(value);
enableOuterScroll(isFirstItemShown);
}

UPDATE 3

Issue: glitches during the transition from the outer to inner RecyclerView because the onScroll() of the outer gets called before deciding whether we can scroll the inner or not.

By using OnTouchListener to the outer RecyclerView and overriding onTouch() and return true to consume the event (so that onScrolled() won't get called) until we decide that the inner can take the scroll over.



Related Topics



Leave a reply



Submit