使用 ItemTouchHelper 拖动项目时 RecyclerView 滚动到顶部 [英] RecyclerView scrolls to top when dragging item with ItemTouchHelper
问题描述
我有一个奇怪的错误,每当我开始在其中拖动项目时,我的 RecyclerView
就会滚动回顶部位置.如果有任何区别,它在 ViewPager
内.您可以在 .gif 附件中看到行为.
当 notifyItemMoved
被调用时,RecyclerView
视图似乎滚动到顶部,并且它滚动到第一个视图至少部分显示在屏幕上.>
查看
适配器
class AccountListAdapter(私有 val onAccountClickListener: OnAccountClickListener) :ListAdapter(AccountDiffCallback()){覆盖乐趣 onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val inflater = LayoutInflater.from(parent.context)返回 ViewHolder(充气机.inflate(R.layout.view_account_list_item,家长,错误的))}覆盖 fun getItemId(position: Int): Long {返回 getItem(position).accountId.toLong()}覆盖乐趣 onBindViewHolder(holder: ViewHolder, position: Int) {holder.bind(getItem(position), onAccountClickListener)}class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), OnItemDragged {有趣的绑定(帐户:帐户,onAccountClickListener:OnAccountClickListener){itemView.account_name.text = account.nameitemView.setOnClickListener {onAccountClickListener.onAccountClick(账户)}}覆盖乐趣 onItemSelected() {itemView.setBackgroundColor(ContextCompat.getColor(itemView.context,R.color.background_contrast))}覆盖乐趣 onItemClear() {itemView.setBackgroundColor(ContextCompat.getColor(itemView.context,R.color.background))}}类 AccountDiffCallback : DiffUtil.ItemCallback() {覆盖乐趣 areItemsTheSame(oldItem: Account, newItem: Account): Boolean {返回 oldItem.accountId == newItem.accountId}覆盖乐趣 areContentsTheSame(oldItem: Account, newItem: Account): Boolean {返回 (oldItem.balance == newItem.balance&&oldItem.annualReturn == newItem.annualReturn&&oldItem.name == newItem.name)}}接口 OnAccountClickListener {有趣的 onAccountClick(帐户:帐户)}接口 OnItemDragged {有趣的 onItemSelected()有趣的 onItemClear()}}
ItemTouchHelper
私人乐趣 setupListAdapter() {accountListAdapter = AccountListAdapter(this)accountListAdapter.setHasStableIds(true)account_recycler_view.adapter = accountListAdapteraccount_recycler_view.addItemDecoration(DividerItemDecoration(需要上下文(),DividerItemDecoration.VERTICAL))val accountTouchHelper = ItemTouchHelper(对象:ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP 或 ItemTouchHelper.DOWN,0){覆盖乐趣 onSelectedChanged(viewHolder: RecyclerView.ViewHolder?,动作状态:Int){如果(actionState == ItemTouchHelper.ACTION_STATE_DRAG){val accountViewHolder = viewHolder 作为 AccountListAdapter.ViewHolderaccountViewHolder.onItemSelected()}super.onSelectedChanged(viewHolder, actionState)}覆盖有趣的 clearView(回收器视图:回收器视图,viewHolder: RecyclerView.ViewHolder){val accountViewHolder = viewHolder 作为 AccountListAdapter.ViewHolderaccountViewHolder.onItemClear()super.clearView(recyclerView, viewHolder)}覆盖乐趣 onMove(回收器视图:回收器视图,viewHolder: RecyclerView.ViewHolder,目标:RecyclerView.ViewHolder): 布尔 {val fromPos: Int = viewHolder.adapterPositionval toPos: Int = target.adapterPositionCollections.swap(_accountList, fromPos, toPos)accountListAdapter.notifyItemMoved(fromPos, toPos)返回真}覆盖乐趣 onSwiped(viewHolder: RecyclerView.ViewHolder, 方向: Int) {}})accountTouchHelper.attachToRecyclerView(accounts_recycler_view)}
所以问题是我的 Recyclerview
在 ViewPager
里面,它在 ConstraintLayout代码>.视图寻呼机被垂直约束,高度设置为 0dp,但宽度设置为
match_parent
.我需要的只是将其水平约束,宽度设置为 0dp,setHasFixedSize = true
为 RecyclerView
当为适配器调用 notifyItemMoved
时,如果 RecyclerView
是灵活的,则所有项目都被重绘,默认情况下它会关注第一个项目.
I have this weird bug where my RecyclerView
scrolls back to top position whenever I start dragging an item in it. It's inside ViewPager
if that has any difference. You can see the behavior in .gif attached.
EDIT:
It seems that RecyclerView
view scrolls to top when notifyItemMoved
is called and it scrolls just as much for the first view to be at least partially displayed on screen.
View
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/accounts_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="none"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/view_account_list_item" />
Adapter
class AccountListAdapter(
private val onAccountClickListener: OnAccountClickListener) :
ListAdapter<Account, AccountListAdapter.ViewHolder>(
AccountDiffCallback()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(
inflater.inflate(
R.layout.view_account_list_item,
parent,
false
)
)
}
override fun getItemId(position: Int): Long {
return getItem(position).accountId.toLong()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position), onAccountClickListener)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), OnItemDragged {
fun bind(account: Account, onAccountClickListener: OnAccountClickListener) {
itemView.account_name.text = account.name
itemView.setOnClickListener {
onAccountClickListener.onAccountClick(account)
}
}
override fun onItemSelected() {
itemView.setBackgroundColor(
ContextCompat.getColor(
itemView.context,
R.color.background_contrast
)
)
}
override fun onItemClear() {
itemView.setBackgroundColor(
ContextCompat.getColor(
itemView.context,
R.color.background
)
)
}
}
class AccountDiffCallback : DiffUtil.ItemCallback<Account>() {
override fun areItemsTheSame(oldItem: Account, newItem: Account): Boolean {
return oldItem.accountId == newItem.accountId
}
override fun areContentsTheSame(oldItem: Account, newItem: Account): Boolean {
return (oldItem.balance == newItem.balance
&& oldItem.annualReturn == newItem.annualReturn
&& oldItem.name == newItem.name)
}
}
interface OnAccountClickListener {
fun onAccountClick(account: Account)
}
interface OnItemDragged {
fun onItemSelected()
fun onItemClear()
}}
ItemTouchHelper
private fun setupListAdapter() {
accountListAdapter = AccountListAdapter(this)
accountListAdapter.setHasStableIds(true)
accounts_recycler_view.adapter = accountListAdapter
accounts_recycler_view.addItemDecoration(
DividerItemDecoration(
requireContext(),
DividerItemDecoration.VERTICAL
)
)
val accountTouchHelper = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
0
) {
override fun onSelectedChanged(
viewHolder: RecyclerView.ViewHolder?,
actionState: Int
) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
val accountViewHolder = viewHolder as AccountListAdapter.ViewHolder
accountViewHolder.onItemSelected()
}
super.onSelectedChanged(viewHolder, actionState)
}
override fun clearView(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
) {
val accountViewHolder = viewHolder as AccountListAdapter.ViewHolder
accountViewHolder.onItemClear()
super.clearView(recyclerView, viewHolder)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val fromPos: Int = viewHolder.adapterPosition
val toPos: Int = target.adapterPosition
Collections.swap(_accountList, fromPos, toPos)
accountListAdapter.notifyItemMoved(fromPos, toPos)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
})
accountTouchHelper.attachToRecyclerView(accounts_recycler_view)
}
So the issues was that my Recyclerview
was inside ViewPager
which was inside a ConstraintLayout
. View pager was constrained vertically with height set to 0dp but width was set to match_parent
. All I needed was to constrain it horizontally with width set to 0dp and setHasFixedSize = true
to RecyclerView
When calling notifyItemMoved
for adapter, if RecyclerView
is flexible, all items are redrawn and by default it focuses on the first item.
这篇关于使用 ItemTouchHelper 拖动项目时 RecyclerView 滚动到顶部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!