9

I've got a CoordinatorLayout which contains a CollapsingToolbarLayout and a RecyclerView. Everything looks the way it's supposed to, except that when I try to scroll to the last item programmatically, it doesn't go all the way to the bottom. Instead, it does this:

enter image description here

I don't think this is a clipping problem, since the bottom item is fully there if I scroll down:

enter image description here

Here's the main layout:

<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="150dp" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary" app:expandedTitleMarginStart="16dp" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:toolbarId="@+id/toolbar"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:contentInsetStart="72dp" app:popupTheme="@style/AppTheme.PopupOverlay"/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="@dimen/recyclerview_bottom_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> </android.support.design.widget.CoordinatorLayout> 

And here's the code that goes with the screencaps above:

class TestActivity : AppCompatActivity() { private val itemNames = listOf("top item", "next item", "yada", "yada yada", "yada yada yada", "second last item", "last item") private val selectedPosition = itemNames.size - 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.recyclerview_with_collapsing_toolbar) setSupportActionBar(toolbar) supportActionBar?.setTitle(R.string.some_title) val recyclerView = findViewById<RecyclerView>(R.id.recycler_view) recyclerView.setHasFixedSize(true) val layoutManager = LinearLayoutManager(this) recyclerView.layoutManager = layoutManager recyclerView.adapter = MyAdapter() // try to scroll to the initial selected position recyclerView.scrollToPosition(selectedPosition) // layoutManager.scrollToPosition(selectedPosition) // layoutManager.scrollToPositionWithOffset(selectedPosition, resources.getDimensionPixelOffset(R.dimen.item_height)) // recyclerView.post { // recyclerView.smoothScrollToPosition(selectedPosition) // } } inner class MyAdapter: RecyclerView.Adapter<MyViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, itemType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false) return MyViewHolder(view) } override fun getItemCount(): Int { return itemNames.size } override fun onBindViewHolder(vh: MyViewHolder, position: Int) { vh.words.text = itemNames[position] if (selectedPosition == position) { vh.parent.setBackgroundColor(Color.MAGENTA) } else { vh.parent.setBackgroundColor(Color.BLACK) } } } inner class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { val parent = itemView val words: TextView = itemView.findViewById(R.id.some_text) } } 

Additional notes:

  • If I get rid of the CollapsingToolbarLayout then it does show the entire last item.

  • I've left some of my other attempts in the code above (commented out). None of them worked.

  • This example just involves a short static list and always scrolls to the same item, but the code I'm actually working on is a bit more complicated.

  • The designer really wants everything to look exactly as designed, and I'm not free to change the visual design.

How can I scroll to the last item in a RecyclerView that's inside a layout with a collapsing toolbar?

1
  • 1
    @Rapunazel I think you have to add an image view before the toolbar in the collapsing toolbar. Commented Jan 23, 2019 at 14:48

2 Answers 2

9
+50

The problem is that getNestedScrollingParentForType(type) in NestedScrollingChildHelper#dispatchNestedScroll returns null for the non-touch scroll, so scrolling is not dispatched when it is done programmatically.

So we need to enable that before scrolling programmatically:

if (!recyclerView.hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) { recyclerView.startNestedScroll(View.SCROLL_AXIS_VERTICAL,ViewCompat.TYPE_NON_TOUCH); } // now smooth scroll your recycler view programmatically here. 
Sign up to request clarification or add additional context in comments.

11 Comments

I was really optimistic that this solution would work, but it didn't seem to have any effect at all. (Specifically, I inserted your snippet into my small program just before scrolling, and it didn't seem to make any difference.)
Let me add the complete code snippet for you. I have tried your layout code and it works.
Have created a repository to demonstrate this fix - github.com/arora-vishal/RecyclerScrollFix
I'm not sure why you wrote a completely different example instead of just using the code that I originally posted, but thanks for the effort. I've tried out your code, and it does scroll all the way to the bottom when a button is clicked. However, if you take that code out of the onClick and try to just preselect the bottom item initially (before any user interaction), it doesn't scroll all the way to the bottom. That's an interesting mystery! I'm giving you an upvote, but your solution (which apparently requires a click to work) still doesn't do what I want yet.
Click is just to demonstrate the code which is required to scroll the content all the way to bottom.
|
4

A fix for this problem would be to collapse the toolbar before scrolling to the given position. This can be done by adding app_bar_layout.setExpanded(false) before scrollToPosition.

2 Comments

That simple fix does indeed scroll almost to the bottom, but the last bit (the height of the remaining app bar) of the selected item is still hidden. However, if I combine your trick with a viewTreeObserver, that does finally show the entire selected item. I gave your answer an upvote, and if I don't get any better answer, it may eventually get the bounty. But I'll wait until the end of the week to see what other answers come in, and award the bounty to the best answer.
@RapunzelVanWinkle Perhaps you could try using scrollToPositionWithOffset() and use the height of your collapsed app bar as the offset. You can either hard-code that height or retreive it dynamically according to this answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.