RecyclerView 应该与 stackFromEnd 保持在底部
RecyclerView should stay at bottom with stackFromEnd
所以网络上基本上已经存在这个问题:如何让 RecyclerView
滚动到带有新项目的底部?几乎总能找到答案:添加 stackFromEnd=true
或 reverseLayout=true
。就我而言,我尝试了两者的所有可能组合,但都没有用。我也看到了这样的解决方案:
viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
//(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(viewAdapter.itemCount - 1, 0) or
//(layoutManager as? LinearLayoutManager)?.smoothScrollToPosition(this@ChatMessagesList, null, viewAdapter.itemCount - 1) or
//this@ChatMessagesList.smoothScrollToPosition(viewAdapter.itemCount - 1)
}
})
这对我也没有帮助。首先,我将描述我的设置,然后是预期行为和当前行为:
我有一个 xml 看起来像这样:
<merge 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:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/backgroundImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/image_image_description"
android:scaleType="centerCrop"
android:src="@drawable/chat_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/overlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#CCFFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_header.ChatHeader
android:id="@+id/chat_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_message_list.ChatMessagesList
android:id="@+id/chat_messages_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/chat_options_chooser"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chat_header" />
<app.jooy.messenger.ui.components.generic.chat.chat_options_chooser.ChatOptionsChooser
android:id="@+id/chat_options_chooser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/chat_emoji_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_emoji_bar.ChatEmojiBar
android:id="@+id/chat_emoji_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/chat_action_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_action_bar.ChatActionBar
android:id="@+id/chat_action_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorActionBarBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</merge>
重要的观点是 chat_messages_list RecyclerView
这是我的 RecyclerView
其实现如下(为简单起见删除了一些东西):
@ExperimentalCoroutinesApi
@ReactiveComponent
class ChatMessagesList @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ReactiveRecyclerView<ChatMessagesList.ViewState, ChatMessagesList.ViewAction, ChatMessagesList.ViewStructure>(
ViewState(),
context,
attrs,
defStyleAttr
) {
private val viewAdapter = ChatMessagesListAdapter()
init {
setHasFixedSize(true)
layoutManager = object : LinearLayoutManager(context) {
init {
stackFromEnd = true
}
}
viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
//(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(0, 0)
//(layoutManager as? LinearLayoutManager)?.smoothScrollToPosition(this@ChatMessagesList, null, viewAdapter.itemCount - 1)
//this@ChatMessagesList.smoothScrollToPosition(0)
}
})
registerTypes(viewAdapter)
adapter = viewAdapter
addItemDecoration(getDecoration())
setPadding(0, 6.toPx(), 0, 6.toPx())
clipToPadding = false
}
}
此外,我可以告诉您,我的适配器肯定会调用正确的 notifyItemRangeInserted
,...调用。
预期行为:
RecyclerView
应该从屏幕底部的第一个项目开始,如果添加了一个新项目,它应该总是添加到底部并滚动到。最后底部的新项目应该看起来像是从底部推入的。
当前行为:
项目从底部开始堆叠,直到 RecyclerView
内的可见 space 未满,一切都以精美的动画呈现,但一旦可见 space 已满,项目就会添加到底部但没有滚动到。正如某些人所期望的那样,我想要实现的功能是聊天之一。
进一步说明:
如果我将 this@ChatMessagesList.smoothScrollToPosition(viewAdapter.itemCount - 1)
添加到 onItemRangeInserted
回调,它会转到底部但没有动画,它只是跳到那里。
所以我自己想通了。我仍然不知道为什么某些策略有效而有些策略根本无效。
我在 LinearLayoutManager
上设置了 reverseLayout=true
(这意味着我必须在列表中反转我的项目)然后我添加了一个 onItemRangeInserted
侦听器,如下所示:
layoutManager = object : LinearLayoutManager(context) {
init {
reverseLayout = true
}
}
viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0) {
layoutManager?.scrollToPosition(0)
}
}
})
所以网络上基本上已经存在这个问题:如何让 RecyclerView
滚动到带有新项目的底部?几乎总能找到答案:添加 stackFromEnd=true
或 reverseLayout=true
。就我而言,我尝试了两者的所有可能组合,但都没有用。我也看到了这样的解决方案:
viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
//(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(viewAdapter.itemCount - 1, 0) or
//(layoutManager as? LinearLayoutManager)?.smoothScrollToPosition(this@ChatMessagesList, null, viewAdapter.itemCount - 1) or
//this@ChatMessagesList.smoothScrollToPosition(viewAdapter.itemCount - 1)
}
})
这对我也没有帮助。首先,我将描述我的设置,然后是预期行为和当前行为:
我有一个 xml 看起来像这样:
<merge 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:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/backgroundImage"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@string/image_image_description"
android:scaleType="centerCrop"
android:src="@drawable/chat_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/overlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#CCFFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_header.ChatHeader
android:id="@+id/chat_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_message_list.ChatMessagesList
android:id="@+id/chat_messages_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/chat_options_chooser"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/chat_header" />
<app.jooy.messenger.ui.components.generic.chat.chat_options_chooser.ChatOptionsChooser
android:id="@+id/chat_options_chooser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/chat_emoji_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_emoji_bar.ChatEmojiBar
android:id="@+id/chat_emoji_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/chat_action_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<app.jooy.messenger.ui.components.generic.chat.chat_action_bar.ChatActionBar
android:id="@+id/chat_action_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/colorActionBarBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</merge>
重要的观点是 chat_messages_list RecyclerView
这是我的 RecyclerView
其实现如下(为简单起见删除了一些东西):
@ExperimentalCoroutinesApi
@ReactiveComponent
class ChatMessagesList @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ReactiveRecyclerView<ChatMessagesList.ViewState, ChatMessagesList.ViewAction, ChatMessagesList.ViewStructure>(
ViewState(),
context,
attrs,
defStyleAttr
) {
private val viewAdapter = ChatMessagesListAdapter()
init {
setHasFixedSize(true)
layoutManager = object : LinearLayoutManager(context) {
init {
stackFromEnd = true
}
}
viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
//(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(0, 0)
//(layoutManager as? LinearLayoutManager)?.smoothScrollToPosition(this@ChatMessagesList, null, viewAdapter.itemCount - 1)
//this@ChatMessagesList.smoothScrollToPosition(0)
}
})
registerTypes(viewAdapter)
adapter = viewAdapter
addItemDecoration(getDecoration())
setPadding(0, 6.toPx(), 0, 6.toPx())
clipToPadding = false
}
}
此外,我可以告诉您,我的适配器肯定会调用正确的 notifyItemRangeInserted
,...调用。
预期行为:
RecyclerView
应该从屏幕底部的第一个项目开始,如果添加了一个新项目,它应该总是添加到底部并滚动到。最后底部的新项目应该看起来像是从底部推入的。
当前行为:
项目从底部开始堆叠,直到 RecyclerView
内的可见 space 未满,一切都以精美的动画呈现,但一旦可见 space 已满,项目就会添加到底部但没有滚动到。正如某些人所期望的那样,我想要实现的功能是聊天之一。
进一步说明:
如果我将 this@ChatMessagesList.smoothScrollToPosition(viewAdapter.itemCount - 1)
添加到 onItemRangeInserted
回调,它会转到底部但没有动画,它只是跳到那里。
所以我自己想通了。我仍然不知道为什么某些策略有效而有些策略根本无效。
我在 LinearLayoutManager
上设置了 reverseLayout=true
(这意味着我必须在列表中反转我的项目)然后我添加了一个 onItemRangeInserted
侦听器,如下所示:
layoutManager = object : LinearLayoutManager(context) {
init {
reverseLayout = true
}
}
viewAdapter.registerAdapterDataObserver(object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0) {
layoutManager?.scrollToPosition(0)
}
}
})