I had a view with a netstedScroll and a recyclerView that I was using to display my data so my fragment was like that:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.core.widget.NestedScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:itemCount="10"
tools:listitem="#layout/row_header" />
</androidx.core.widget.NestedScrollView>
The recyclerview had a custom adapter that I used is to get a recyclerview same as an expandable view (I didn't use the native one) and everything was great until my last update, so we decided to add more KPIs and informations on the recyclerview became at the very bottom of the view:
The problem now is when the user clicks on negociating, the list of products doesn't show automatically if he doesn't scroll manually, and feels like there isn't any changes.
I want just on click of this part, to scroll, the problem is that I'm using a custom adapter:
private fun bindHeaderViewHolder(holder: RecyclerView.ViewHolder, position: Int, viewType: ViewType) {
val dataIndex = viewType.dataIndex
val headerViewHolder = holder as SectionHeaderViewHolder
when(position){
0 -> headerViewHolder.sectionTitle.text = "Sold out"
1 -> headerViewHolder.sectionTitle.text = "Rent"
2 -> headerViewHolder.sectionTitle.text = "On stock"
3 -> headerViewHolder.sectionTitle.text = "Negociating"
}
if (isExpanded(position)) {
headerViewHolder.expandIcon.setImageResource(R.drawable.ic_expand_less_black_24dp)
if(position == 3){
Log.e("position delivered", "I'm here")
}
} else {
headerViewHolder.expandIcon.setImageResource(R.drawable.ic_expand_more_black_24dp)
}
Can someone help me or suggest a solution for that please?
You can connect a listener to the adapter and called it when needed indicating the clicked position within the adapter:
class YourAdapter(val changeItemListener: (Int) -> Unit) : RecyclerView.Adapter<YourViewHolder>() {
...
changeItemListener(position)
...
}
After that you can from Activity or fragment create the adapter with the listener and within the listener you can call scrollToPosition or whatever you may need.
viewModelAdapter = YourAdapter(changeItemListener = {position -> your_recyclerview.smoothScrollToPosition(position)})
Related
My ReycyclerView is not showing data, but once i removed .setHasFixedSize(true), then only it will display. However, this causes another problem. On the first time entering the fragment, it works fine, my actionBar is displaying, but on the second time, it somehow overlaps or pushes away my ActionBar.
I guess .sethasFixedSize is necessary but if i keep it, it will not display data. What causes this?
First time entering:
Second time entering (before notifyItemInserted()):
After notifyItemInserted():
fragment.xml:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cartItemRv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
fragment.kt:
onCreateView() {
binding.cartItemRv.layoutManager = LinearLayoutManager(context)
binding.cartItemRv.setHasFixedSize(true)
cartAdapter = CartAdapter(productNameList, productVariationList, cartItemList)
binding.cartItemRv.adapter = cartAdapter
//code for retrieving data from Firebase
cartAdapter.notifyItemInserted(productNameList.size-1)
}
Adapter:
class CartAdapter(...: RecyclerView.Adapter<CartAdapter.CartViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.cart_item, parent, false)
return CartViewHolder(itemView, mListener)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
...
}
override fun getItemCount(): Int {
return productNameList.size
}
inner class CartViewHolder(itemView: View, listener: onItemClickListener) : RecyclerView.ViewHolder(itemView){
...
}
}
about sethasFixedSize , if the size of your the RecyclerView depends on the adapter's content you have to set the sethasFixedSize vlaue : false
otherwise set it true .
// for depending on adapter
mRecyclerView.setHasFixedSize(false);
// for doesn't depend on adapter
mRecyclerView.setHasFixedSize(true);
add NestedScrollView on the parent of RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cartItemRv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>
</androidx.core.widget.NestedScrollView>
</RelativeLayout>
Then set RecyclerView
cartItemRv.setHasFixedSize(false);
I'm a Kotlin newbie learning how to create simple recyclerview apps. My code is supposed to list the integers 1..10 in vertically stacked cells. However, it only lists the first item. I've consulted several tutorials and reviewed my code several times(after long breaks), but I can't see anything wrong in my code.
I got the bright idea early today to print Log statements. Examining them, I note that my onBindViewHolder function is only called once. What blunder am I making?
Here is my log output:
D/QuoteAdapter: value is: 1
D/QuoteAdapter: index is: 0
D/QuoteAdapter: Size is: 10
my activity:
class MainActivity : AppCompatActivity() {
lateinit var mRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mRecyclerView = findViewById(R.id.recyclerView)
mRecyclerView.layoutManager = LinearLayoutManager(this)
mRecyclerView.adapter = QuoteAdapter()
//mRecyclerView.setHasFixedSize(true)
}
}
my adapter:
class QuoteAdapter : RecyclerView.Adapter<QuoteViewHolder>() {
private val listOfInts = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
private val TAG = "QuoteAdapter"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item_row, parent, false)
return QuoteViewHolder(view)
}
override fun getItemCount(): Int {
Log.d(TAG, "Size is: ${listOfInts.size.toString()}")
return listOfInts.size
}
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
val item = listOfInts[position]
Log.d(TAG, "value is: ${item.toString()}")
Log.d(TAG, "index is: ${position.toString()}")
holder.listTitle.text = item.toString()
}
}
my viewholder:
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val listTitle = itemView.findViewById(R.id.itemString) as TextView
}
my layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
my main layout is shown below:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
In your "my layout" try this:
<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/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Notice layout_height of LinearLayout has changed to wrap_content
Also doubt you need the android:orientation="vertical" on your ViewHolders item xml unless you will add more than just 1 TextView in the future.
Like Zain says, you can just use a TextView on its own in a layout file, which will also fix the problem (so long as its height is wrap_content!)
There are actually a few included with Android - type android.R.layout. and you'll see a few things, like simple_list_item_1 which is just a styled TextView (you can ctrl+click the reference or whatever to look at the file). Can be nice if you just want to make a quick thing!
The ID of the TextView in android.R.layout.simple_list_item_1 is #android:id/text1 - note the android prefix, because its part of the android resources, not your app's. Which means you have to reference the ID in the same way as the layout, with android at the front: android.R.id.text1
You can get rid of the LinearLayout in the list item layout, and only keep the TextView.
So, replace your list item layout with:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
I have a ViewPager2 which I'm using with a RecyclerView Adapter. I'm binding the children of each viewpager item via the ViewHolder and everything is working okay.
I have a bunch of elements in this ViewPager item XML, some RadioButton components and a Button. I want this button to move it to the next item.
I know how to do that externally, assigning a sibling to the ViewPager2 and then setting a click-listener in the activity and then comparing currentItem of the ViewPager with the adapter's total item count.
I want the "Move to next" button inside the ViewPager as I want to change it based on the inputs supplied to the RadioButton components.
I'm currently stuck at failing getting the ViewPager item's button in the activity to set the click-listener. Is there any workaround to get the child element via the adapter itself?
Here's the activity_quiz.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.quiz.view.QuizActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/quiz_list"
android:layout_width="match_parent"
android:layout_height="600dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="16dp"
tools:listitem="#layout/quiz_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
The quiz_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:padding="#dimen/bigSpacing"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/question"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choices">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_1"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_2"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_3"/>
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/choice_4"/>
</RadioGroup>
<Button
android:id="#+id/result_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Next question" />
</LinearLayout>
The MyAdapter class (kotlin)
class MyAdapter: RecyclerView.Adapter<MyAdapter.MyViewHolder> {
override fun onCreateViewHolder(...) {
...
}
override fun getItemCount(): Int {
...
}
override fun onBindViewHolder(...) {
...
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
fun bind(someData: SomeData) {
itemView.question.text = somedata.question
itemView.choice_1.text = somedata.choice_1
itemView.choice_2.text = somedata.choice_2
itemView.choice_3.text = somedata.choice_3
itemView.choice_4.text = somedata.choice_4
val answerKey = someData.answerKey
var rightOrWrong = ""
itemView.choices.setOnCheckedChangeListener {_, checkedID ->
val checkedIndex: Int = itemView.choices.indexOfChild(itemView.choices.findViewById(checkedID))
if(checkedIndex + 1 == answerKey.toInt()) {
rightOrWrong = "Correct! Next Question."
} else {
rightOrWrong = "Incorrect :/ Next Question."
}
itemView.result_button.text = rightOrWrong
}
}
}
And the QuizActivity.kt file
class QuizActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quiz)
val myAdapter = MyAdapter()
quiz_list.adapter = myAdapter
// quiz_list.result_button.setOnClickListener {
// No idea how to get the children correctly, as this one throws errors
// }
}
}
First, why do you want to use RecyclerView.Adapter<MyAdapter.MyViewHolder> as an adapter for viewpager2? There is FragmentStateAdapter a built-in class implementation of PagerAdapter that uses a Fragment to manage each page, which is recommended for viewpager2.
Second, you are not inflating the views in MyViewHolder, I don't know if you left them intentionally for this post.
You cant access child views like this quiz_list.result_button.setOnClickListener {} while using something like RecyclerView, Viewpagger. You can access them in the ViewHolder after you inflate them, you can set any listener there aswell
I have a problem with RecyclerView directly inside of layout with bottomsheetbehaviour. The problem is that when bottom sheet is expanded and content is scrolled down, when I go to scroll back up it causes Bottom Sheet to start collapsing, instead of RecyclerView first being scrolled back to top.
Here's a video to demonstrate the problem. As you can see the problem appears when I scroll down on expanded bottom sheet. It immediately start to collapse instead of "waiting" for RecyclerView to scroll to top first.
Here is my layout code
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/scheduleRoot"
android:layout_height="match_parent"
tools:context=".schedule.ScheduleFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scheduleSheet"
app:behavior_peekHeight="300dp"
android:elevation="16dp"
android:clickable="false"
android:focusable="false"
android:background="#drawable/bg_bottom_sheet"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scheduleRecyclerView"
android:clickable="true"
android:focusable="true"
android:layout_marginTop="8dp"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Any help is appreciated!
I just encountered same problem, but I fixed it by adding this to onCreate:
androidx.core.view.ViewCompat.setNestedScrollingEnabled(recyclerview, false);
Add
android:nestedScrollingEnabled="true"
in the root layout of BottomSheetDialogFragment.
I had similar issue: Maybe the solution to my problem will give you some ideas. My bottom sheet was expanded to full height with recycler view in it; the bottom sheet was collapsing on user-drag, even though the first item in recycler view wasn't visible yet.
So, what I did:
You can enable/disable bottom sheet dragging by "isDraggable" = true/false
Add OnScrollListener for recycler view.
Override onScrolled and check layoutManager.findFirstVisibleItemPosition() in it
If first item is visible - update bottom sheet behavior.isDraggable = true, i also added small delay before setting behavior.isDraggable = true, because bottom sheet was collapsing too fast, but you might not need it
Maybe it's not optimal but it was fitting my needs and maybe will help you.
Your recyclerview item has overighted the scrolling state, so this error generates. The layout you provided does not have enough data to determine the cause. You change the item is a unique view to check
I played with this for a long time and tried way too many solutions. For me, this worked best:
val layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.layoutManager = layoutManager
binding.recyclerView.setOnScrollChangeListener { _, _, _, _, _ ->
if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
bottomSheetBehavior.isDraggable = layoutManager.findFirstCompletelyVisibleItemPosition() == 0
} else {
bottomSheetBehavior.isDraggable = true
}
}
The key to the solution is is controlling users ability drag the bottom sheet while the recyclerview is partially scrolled. The method only allows scrolling again once the top most cell is fully visible.
Its not ideal as the user may want to grab the very top of the bottom sheet (assuming its not part of the recycler view) and dismiss the bottom sheet regardless of its scroll position. Im just accepting.
Whatever you do, do not try these, as they just disable any recycling functionality and all cells are loaded at instantiation having a really bad impact on performance:
wrap_content
or:
binding.recyclerView.isNestedScrollingEnabled = false
Enable the scroll state of BottomSheet to allow scroll if recyclerview 0th item is visible.
activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#A8A7A7"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="80dp"
app:layout_behavior="com.asadmukhtar.recyclerviewinsidebottomsheet.LockableBottomSheetBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="Drag Me"
android:textColor="#000"
android:textSize="20sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_items"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
LockableBottomSheet file that used for handling allow dragging option or not.
class LockableBottomSheetBehavior<V : View?> : BottomSheetBehavior<V> {
private var mAllowUserDragging = true
constructor()
constructor(context: Context, attrs: AttributeSet?) : super(
context,
attrs
)
fun setAllowUserDragging(allowUserDragging: Boolean) {
mAllowUserDragging = allowUserDragging
}
override fun onInterceptTouchEvent(
parent: CoordinatorLayout,
child: V,
event: MotionEvent
): Boolean {
return if (!mAllowUserDragging) {
false
} else super.onInterceptTouchEvent(parent, child, event)
}
}
MainActivity.java
var bottomSheetBehavior: LockableBottomSheetBehavior<*>? = null
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
setUpBottomSheetBehaviour()
binding.rvItems.layoutManager = LinearLayoutManager(this)
binding.rvItems.adapter = RecyclerViewAdapter(this)
binding.rvItems.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
val firstPosition = (binding.rvItems.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition()
updateBottomSheetLockState(firstPosition == 0)
}
})
}
fun updateBottomSheetLockState(allow: Boolean) {
bottomSheetBehavior?.setAllowUserDragging(allow)
}
fun updateBottomSheetState(state: Int) {
if (bottomSheetBehavior != null) {
bottomSheetBehavior?.state = state
}
}
private fun setUpBottomSheetBehaviour() {
val bottomSheetBehavior: BottomSheetBehavior<LinearLayout> =
BottomSheetBehavior.from(binding.parent)
this.bottomSheetBehavior = bottomSheetBehavior as LockableBottomSheetBehavior<*>
updateBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED)
}
Your implementation might need more coding and with the provided code we might not able to give you good feedback.
Try this documentation
https://material.io/develop/android/components/bottom-sheet-behavior/
Plus I found this another implementation.
https://www.youtube.com/watch?v=WeaylHAwIIk
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:
I don't think this is a clipping problem, since the bottom item is fully there if I scroll down:
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?
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.
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.