#android #android-recyclerview #android-viewpager #itemtouchhelper
#Android #android-recyclerview #android-viewpager #itemtouchhelper
Вопрос:
У меня есть эта странная ошибка, из-за которой моя RecyclerView
прокрутка возвращается в верхнюю позицию всякий раз, когда я начинаю перетаскивать в нее элемент. Это внутри ViewPager
, если это имеет какое-либо значение. Вы можете увидеть поведение в .gif прилагается.
Редактировать:
Кажется, что RecyclerView
при вызове view прокручивается вверх notifyItemMoved
, и он прокручивается так же сильно, чтобы первый вид хотя бы частично отображался на экране.
Вид
<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" />
Адаптер
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
amp;amp; oldItem.annualReturn == newItem.annualReturn
amp;amp; 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)
}
Ответ №1:
Итак, проблема заключалась в том, что мой Recyclerview
был внутри ViewPager
, который был внутри ConstraintLayout
. Просмотр пейджера был ограничен по вертикали с высотой, установленной на 0dp, но ширина была установлена на match_parent
. Все, что мне нужно было ограничить его по горизонтали шириной, равной setHasFixedSize = true
0dp, и RecyclerView
При вызове notifyItemMoved
адаптера, если RecyclerView
он гибкий, все элементы перерисовываются и по умолчанию фокусируются на первом элементе.
Ответ №2:
Если вы используете NestedScrollView, просто удалите и вместо использования других макетов используйте ConstraintLayout
Шаги:
Используйте ConstrainLayout и RecylerView следующим образом:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@ id/rvDraggable"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_0"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="none"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Примечание — вы также можете попробовать использовать:
-
Привязка к просмотру.recycler.setHasFixedSize(true)
-
Если вы используете notifyDataSetChanged(), просто замените его на notifyItemMoved() в методе onMove()