使用 ItemTouchHelper 拖动项目时 RecyclerView 滚动到顶部 [英] RecyclerView scrolls to top when dragging item with ItemTouchHelper

查看:56
本文介绍了使用 ItemTouchHelper 拖动项目时 RecyclerView 滚动到顶部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个奇怪的错误,每当我开始在其中拖动项目时,我的 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)}

解决方案

所以问题是我的 RecyclerviewViewPager 里面,它在 ConstraintLayout.视图寻呼机被垂直约束,高度设置为 0dp,但宽度设置为 match_parent.我需要的只是将其水平约束,宽度设置为 0dp,setHasFixedSize = trueRecyclerView

当为适配器调用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆