Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scrollBy doesn't work properly in nested recyclerview

I have a vertically scrolling RecyclerView with horizontally scrolling inner RecyclerViews just like this.

expl

With this implementation, users can scroll each horizontal recyclerview synchronously. However, when a user scroll vertically to the parent recyclerView, a new horizontal recyclerview which has just attached on window doesn't display on same scroll x position. This is normal. Because it has just created.

So, I had tried to scroll to the scrolled position before it was displayed. Just like this:

Note: this is in adapter of the parent recyclerview whose orientation is vertical.

 @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        CellColumnViewHolder viewHolder = (CellColumnViewHolder) holder;
        if (m_nXPosition != 0) {
             // this doesn't work properly 
             viewHolder.m_jRecyclerView.scrollBy(m_nXPosition, 0);
        }
    }

enter image description here

As you can see, scrollBy doesn't effect for row 10, row 11, row 12 and row 13 After that, I debugged the code to be able find out find out what's happening. When I set scroll position using scrollBy, childCount() return zero for row 10, row 11, row 12 and row 13 So they don't scroll. But why ? and Why others work ?

  • How can I fix this ?
  • Is onViewAttachedToWindow right place to scroll new attached recyclervViews ?

Note: I have also test scrollToPosition(), it doesn't get any problem like this. But I can't use it at my case. Because users can scroll to the any x position which may not the exact position. So I need to set scroll position using x value instead of the position.

Edit: You can check The source code

like image 592
ziLk Avatar asked Jun 29 '17 08:06

ziLk


People also ask

How do I make my RecyclerView smooth?

Use the setHasFixedsize method If the height of our RecyclerView items is fixed then we should use the setHasFixedsize method in our XML of our card item. This will fix the height of our RecyclerView item and prevent it from increasing or decreasing the size of our Card Layout.

How can show all items in RecyclerView without scrolling?

It's pretty simple, simply set the RecyclerView 's height to wrap_content . That's right.

How do I stop refreshing RecyclerView data scroll to top position android?

Make a setDataList method in your adapter class. And set your updated list to adapter list. And then every time of calling API set that list to setDataList and call adapter. notifyDataSetChanged() method of your adapter class.

How do I make my recycler view scrollable?

To be able to scroll through a vertical list of items that is longer than the screen, you need to add a vertical scrollbar. Inside RecyclerView , add an android:scrollbars attribute set to vertical .


1 Answers

I found a solution that is use scrollToPositionWithOffset method instead using scrollBy. Even if both of two scroll another position, they have really different work process in back side.

For example: if you try to use scrollBy to scroll any pixel position and your recyclerView had not been set any adapter which means there is no any data to display and so it has no any items yet, then scrollBy doesn't work. RecyclerView uses its layoutManager's scrollBy method. So in my case, I am using LinearLayoutManager to the horizontal recyclerViews.

Lets see what it's doing :

int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (getChildCount() == 0 || dy == 0) {
            return 0;
        }
        mLayoutState.mRecycle = true;
        ensureLayoutState();
        final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
        final int absDy = Math.abs(dy);
        updateLayoutState(layoutDirection, absDy, true, state);
        final int consumed = mLayoutState.mScrollingOffset
                + fill(recycler, mLayoutState, state, false);
        if (consumed < 0) {
            if (DEBUG) {
                Log.d(TAG, "Don't have any more elements to scroll");
            }
            return 0;
        }
        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
        mOrientationHelper.offsetChildren(-scrolled);
        if (DEBUG) {
            Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
        }
        mLayoutState.mLastScrollDelta = scrolled;
        return scrolled;
    }

As you can see scrollBy ignores the scroll intentions if there is no any child at that time.

  if (getChildCount() == 0 || dy == 0) {
     return 0;
  }

On the other hand scrollToPosition can work perfectly even if there is no any set data yet.

According to the Pro RecyclerView slide, the below sample works perfectly. However you can not do that with scrollBy.

void onCreate(SavedInstanceState state) {
    ....
    mRecyclerView.scrollToPosition(selectedPosition);
    mRecyclerView.setAdapter(myAdapter);
}

As a result, I have changed little thing to use scrollToPositionWithOffset().

Before this implementation I was calculating the exact scroll x position as a pixel.

After that, when the scroll came idle state, calculating the first complete visible position to the first parameter of the scrollToPositionWithOffset().

For second parameter which is the offset, I am getting the value using view.getLeft() function which helps to get left position of this view relative to its parent.

enter image description here

And it works perfectly!!

like image 90
ziLk Avatar answered Oct 27 '22 08:10

ziLk