When changing list item layout RecyclerView scrolls to top - android

In my RecyclerView OnLongClicking an item I want to change that item's layout by setting some TextViews to View.GONE and others to View.VISIBLE. Everything works except that when I long press the item and the layout changes my RecyclerView scrolls to top and I can no longer see the LongPressed view if it was at the bottom.
This is ListAdapter that I wrote:
class AssetsListAdapter(
private val onAssetClickListener: OnAssetClickListener,
private val onAssetLongClickListener: OnAssetLongClickListener
) :
ListAdapter<Asset, AssetsListAdapter.ViewHolder>(
AssetDiffCallback()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(
inflater.inflate(
R.layout.list_item,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position), onAssetClickListener, onAssetLongClickListener)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(
asset: Asset,
onAssetClickListener: OnAssetClickListener,
onAssetLongClickListener: OnAssetLongClickListener
) {
itemView.item_name.text = asset.name
// Set icons relatively to category
when (asset.category) {
"Cash" -> itemView.item_image.setImageResource(R.drawable.ic_cash)
"Bank Account" -> itemView.item_image.setImageResource(R.drawable.ic_bank)
"Investment" -> itemView.item_image.setImageResource(R.drawable.ic_invest)
"Salary" -> itemView.item_image.setImageResource(R.drawable.ic_job)
}
itemView.setOnClickListener {
onAssetClickListener.onAssetClick(asset)
}
// On long click listeners pulls up quick action options
itemView.setOnLongClickListener {
view.item_end_text.visibility = View.GONE
view.quick_actions_layout.visibility = View.VISIBLE
onAssetLongClickListener.onAssetLongClick(asset, itemView)
true
}
}
}
class AssetDiffCallback : DiffUtil.ItemCallback<Asset>() {
override fun areItemsTheSame(oldItem: Asset, newItem: Asset): Boolean {
return oldItem.assetId == newItem.assetId
}
override fun areContentsTheSame(oldItem: Asset, newItem: Asset): Boolean {
return oldItem == newItem
}
}
interface OnAssetClickListener {
fun onAssetClick(asset: Asset)
}
interface OnAssetLongClickListener {
fun onAssetLongClick(asset: Asset, view: View)
}
}

Ok, so I found out that if you set View.GONE the whole item is redrawn and it resets the RecyclerView? Because setting it to View.INVISIBLE solves the issue.
itemView.setOnLongClickListener {
view.item_end_text.visibility = View.GONE
view.quick_actions_layout.visibility = View.VISIBLE
onAssetLongClickListener.onAssetLongClick(asset, itemView)
true
}
to
itemView.setOnLongClickListener {
view.item_end_text.visibility = View.INVISIBLE
view.quick_actions_layout.visibility = View.VISIBLE
onAssetLongClickListener.onAssetLongClick(asset, itemView)
true
}

Related

Automatically hide items in recyclerView

I'm trying to figure out how to get access to the views in my recyclerView. I want to be able to expand items in the recyclerView. When one expands, all the others should collapse.
class CustomAdapter(private val mList: List<Card>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView) {
val textView: TextView = itemView.findViewById(R.id.questionView)
val categoryView: TextView = itemView.findViewById(R.id.category_view)
val solutionView: TextView = itemView.findViewById(R.id.solutionView)
val editButton: FloatingActionButton = itemView.findViewById(R.id.floatingActionButton)
}
// create new views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.items_view_design, parent, false)
return ViewHolder(view)
}
// binds the list items to a view
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val itemsViewModel = mList[position]
holder.textView.text = itemsViewModel.question
holder.categoryView.text = itemsViewModel.subjectCategory
holder.solutionView.text = itemsViewModel.solution
fun showHide(view:View) {
view.visibility = if (view.visibility == View.VISIBLE){
View.GONE
} else{
View.VISIBLE
}
}
fun show(view:View){
view.visibility =View.VISIBLE
}
fun hide(view:View){
view.visibility = View.GONE
}
holder.textView.setOnClickListener{
showHide(holder.solutionView)
showHide(holder.editButton)
}
//This is where I need help.
}'''
In pseudocode, what I want to say is:
for loop (items in mList)
items.solution = holder.solutionView
hide(solutionView)
But I can't figure out how to get access to holder.categoryView for an item that isn't the current item being bound.
As I understand you need a property to indicate that an item was chosen and use it to check whether to hide or show some elements of your views:
class CustomAdapter(private val mList: List<Card>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
private var selectedCard: Card? = null
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val itemsViewModel = mList[position]
// Your code
if (selectedCard != null && selectedCard != itemsViewModel) {
showHide(holder.solutionView)
showHide(holder.editButton)
}
holder.textView.setOnClickListener{
if (itemsViewModel == selectedCard) {
selectedCard = null
} else {
selectedCard = itemsViewModel
}
notifyDataSetChanged() // It's not optimal, but the easiest way
}
}
}
Thanks for your help. I followed the link posted by Umesh RecyclerView expand/collapse items
Worked for me! This is a Kotlin translation of the answer he linked to:
global variables:
// private var previousExpandedPosition = -1
// private var mExpandedPosition = -1
var isExpanded: Boolean = (position == mExpandedPosition)
holder.details.setVisibility(if (isExpanded) View.VISIBLE else View.GONE)
holder.itemView.isActivated = isExpanded
if (isExpanded) previousExpandedPosition = position
holder.itemView.setOnClickListener {
mExpandedPosition = if (isExpanded) -1 else position
notifyItemChanged(previousExpandedPosition)
notifyItemChanged(position)
}

How to properly implement RecyclerView.ItemAnimator using the Lottie animation library?

I have a case where a view within RecyclerView items needs to be animated using the Lottie library. Each recycler view item is clickable and contains a liking Lottie animation.
I defined a custom RecyclerView.ItemAnimator like this:
class SampleItemAnimator : DefaultItemAnimator() {
override fun animateChange(
oldHolder: RecyclerView.ViewHolder,
newHolder: RecyclerView.ViewHolder,
preInfo: ItemHolderInfo,
postInfo: ItemHolderInfo
): Boolean {
val holder = newHolder as BindingViewHolder<ItemSampleBinding>
val animator = lottieAnimatorListener {
dispatchAnimationFinished(holder)
holder.binding.sampleAnimation.removeAllAnimatorListeners()
}
holder.binding.sampleAnimation.addAnimatorListener(animator)
if (preInfo is SampleItemHolderInfo) {
if (preInfo.isItemLicked) {
holder.binding.sampleAnimation.playAnimation()
} else {
resetAnimation(holder.binding.sampleAnimation)
}
return true
}
return super.animateChange(oldHolder, newHolder, preInfo, postInfo)
}
private fun resetAnimation(lottieAnimationView: LottieAnimationView) {
lottieAnimationView.progress = 0f
lottieAnimationView.cancelAnimation()
}
override fun recordPreLayoutInformation(
state: RecyclerView.State,
viewHolder: RecyclerView.ViewHolder,
changeFlags: Int,
payloads: MutableList<Any>
): ItemHolderInfo {
if (changeFlags == FLAG_CHANGED) {
return produceItemHolderInfoOrElse(payloads.firstOrNull() as? Int) {
super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads)
}
}
return super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads)
}
private fun produceItemHolderInfoOrElse(value: Int?, action: () -> ItemHolderInfo) =
when (value) {
LIKE_ITEM -> SampleItemHolderInfo(true)
UNLIKE_ITEM -> SampleItemHolderInfo(false)
else -> action()
}
override fun canReuseUpdatedViewHolder(viewHolder: RecyclerView.ViewHolder) = true
override fun canReuseUpdatedViewHolder(
viewHolder: RecyclerView.ViewHolder,
payloads: MutableList<Any>
) = true
}
lottieAnimatorListener is just a function that creates Animator.AnimatorListener to tell RecyclerView when the animation is canceled or ended by calling dispatchAnimationFinished(holder).
Everything works except that sometimes the liking animation can randomly play on items with no likes, especially while scrolling RecyclerView too fast.
As far as I understand, it happens because the ItemAnimator re-uses the same view holders and either uses outdated ItemHolderInfo or does not notify the RecyclerView about the end of the animation correctly.
That is how I pass a payload to the adapter to tell what has changed using DiffUtil.Callback.
class SampleListDiffCallback : DiffCallback<SampleItem> {
override fun areContentsTheSame(oldItem: SampleItem, newItem: SampleItem) =
oldItem.markableItem == newItem.markableItem
override fun areItemsTheSame(oldItem: SampleItem, newItem: SampleItem) =
oldItem.identifier == newItem.identifier
override fun getChangePayload(
oldItem: SampleItem,
oldItemPosition: Int,
newItem: SampleItem,
newItemPosition: Int
): Any? = createPayload(oldItem.markableItem, newItem.markableItem)
private fun createPayload(
oldItem: MarkableItem,
newItem: MarkableItem
) = when {
! oldItem.isLiked && newItem.isLiked -> LIKE_ITEM
oldItem.isLiked && ! newItem.isLiked -> UNLIKE_ITEM
else -> null
}
}
That is how I define a ViewHolder using the FastAdapter library:
class SampleItem(
val markableItem: MarkableItem,
private val onClickItem: (Item) -> Unit
) : AbstractBindingItem<ItemSampleBinding>() {
override val type: Int = R.layout.item
override var identifier: Long = markableItem.item.hashCode().toLong()
override fun createBinding(inflater: LayoutInflater, parent: ViewGroup?) =
ItemSampleBinding.inflate(inflater, parent, false)
override fun bindView(binding: ItemSampleBinding, payloads: List<Any>) {
super.bindView(binding, payloads)
with(binding) {
itemName.text = markableItem.item.name
itemImage.setContent(markableItem.item, IMAGE_SIZE)
likeAnimation.progress = if(markableItem.isClicked) 1f else 0f
root.setThrottleClickListener { onClickItem(markableItem.item) }
}
}
}
UPD: The liking animation's duration is 2 seconds.
Does anybody know if there is any way to fix it?

How to change bg recyclerview specific position [ Kotlin ]

Q1
I make some recyclerview with cardview I want to click some position and change bg current position to black and another position to green in the adapter
Q2
I want to change something specific position in the adapter
class RecyclerAdapterBrand(private var imageList: List<Int>) :
RecyclerView.Adapter<RecyclerAdapterBrand.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.imgViewBrand)
val cardView: CardView = itemView.findViewById(R.id.cardViewBrand)
init {
// Set Recycler Click
itemView.setOnClickListener { v: View ->
val positionClick: Int = (adapterPosition + 1)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(
R.layout.recy_brand_layout,
parent,
false
)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.post {
holder.imageView.setImageResource(imageList[position])
}
}
override fun getItemCount(): Int {
return imageList.size
}
You can change the background of the clicked item easily.You have to create a variable called selectedposition and assigned to -1. Whenever you click the item you have to set the clicked position value to selectedPosition.And call notifyDataSetChanged() method on recyclerview's item ClickListener method. Then in OnBindViewHolder() method you have to check if the position & selected position are same, set the background color to green.otherwise set the background color to black.
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var selectedPosition=-1
val imageView: ImageView = itemView.findViewById(R.id.imgViewBrand)
val cardView: CardView = itemView.findViewById(R.id.cardViewBrand)
init {
// Set Recycler Click
itemView.setOnClickListener { v: View ->
selectedPosition=(adapterPosition + 1)
val positionClick: Int = (adapterPosition + 1)
notifyDataSetChanged()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(
R.layout.recy_brand_layout,
parent,
false
)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if(selectedPosition==position){
holder.itemView.setBackground(#green color)
}else{
holder.itemView.post {
holder.imageView.setImageResource(#black color)
}
}
}
override fun getItemCount(): Int {
return imageList.size
}
I have included those lines in your adapter class.I hope it will solve your problem.

RecyclerView doesn't display last item when multiple ViewHolders are used

I'm using androidx.recyclerview.widget.RecyclerView to display a list of items, separated by an other item as a "header" with some aggregated values.
When i put only one item in my list without adding the header, everything is ok and the item is displayed correctly. As soon as i add the header item, only the header is displayed and the one single item isn't shown.
When i add two items and the header, the header and one item are displayed. I don't know why the last item of my list is missing altough it exists in the adapters datasource.
My ListAdapter inherits from RecyclerView.Adapter<RecyclerView.ViewHolder> and uses two ViewHolders detected by a viewType property of my list items.
When loading the data, the onBindViewHolder method isn't called for the last item in my list, even tough the item is in the visible section of my screen.
Does anybody has a hint, why this happens?
class ListAdapter(val onClick: (position: Long) -> Unit,
val onLongClick: (Long) -> Unit,
val onShareClick: (id: Long?) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>(),
BindableAdapter<List<ListAdapterItem<*>>> {
var items: List<ListAdapterItem<*>> = emptyList()
private var actionMode: ActionMode? = null
var tracker: SelectionTracker<Long>? = null
init {
setHasStableIds(true)
}
override fun setData(data: List<ListAdapterItem<*>>) {
this.items = data // all items are set correctly here!!
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
return if (items.isEmpty()) EMPTY else items[position].viewType
}
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
override fun getItemId(position: Int): Long = position.toLong()
fun getItem(position: Long): ListViewModel.ListItem = item[position.toInt()].value as ListViewModel.ListItem
fun setActionMode(actionMode: ActionMode?) {
this.actionMode = actionMode
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
EMPTY -> EmptyViewHolder(parent)
HEADER -> HistoryGroupHeaderViewHolder(parent)
else -> HistoryViewHolder(parent)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is HistoryViewHolder) {
val item = items[position].value as ListViewModel.ListItem
tracker?.let {
holder.bind(item, it.isSelected(position.toLong()))
}
holder.itemView.setOnClickListener {
onClick(position.toLong())
}
holder.itemView.findViewById<AppCompatImageView>(R.id.history_item_share)?.setOnClickListener {
onShareClick(item.id)
}
}
else if (holder is HistoryGroupHeaderViewHolder) {
val header = items[position].value as ListViewModel.ListSectionHeader
holder.bind(header)
}
}
class HistoryViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListItem, isActivated: Boolean = false) {
binding.model = item
itemView.isActivated = isActivated
val imageView = itemView.findViewById<AppCompatImageView>(R.id.history_item_image)
if(itemView.isActivated) {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt(),
parent.context.resources.getDimension(R.dimen.spacing_small).toInt()
)
imageView.layoutParams = parameter
} else {
val parameter = imageView?.layoutParams as ConstraintLayout.LayoutParams
parameter.setMargins(0,0,0,0)
imageView.layoutParams = parameter
}
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
}
}
class HistoryGroupHeaderViewHolder(
private val parent: ViewGroup,
private val binding: at.app.databinding.ViewHistoryListGroupHeaderItemBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_history_list_group_header_item,
parent,
false
)
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ListViewModel.ListSectionHeader) {
binding.model = item
}
}
class EmptyViewHolder(
private val parent: ViewGroup, view: View = LayoutInflater.from(parent.context).inflate(
R.layout.view_history_empty_item,
parent,
false
)
) : RecyclerView.ViewHolder(view)
companion object {
const val EMPTY = 0
const val ITEM = 1
const val HEADER = 2
}
}
class MyItemDetailsLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
private val log = LoggerFactory.getLogger(ListAdapter::class.java)
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
val view = recyclerView.findChildViewUnder(e.x, e.y)
if (view != null) {
return try {
if(recyclerView.getChildViewHolder(view) is ListAdapter.HistoryViewHolder) {
(recyclerView.getChildViewHolder(view) as ListAdapter.HistoryViewHolder)
.getItemDetails()
} else {
null
}
} catch (ex: Exception) {
log.error("Error on getItemDetails. ", ex)
null
}
}
return null
}
}
data class ListAdapterItem<out T>(val value: T, val viewType: Int)
And this is my layout:
<?xml version="1.0" encoding="utf-8"?>
<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>
<import type="android.view.View" />
<variable
name="viewModel"
type="at.app.ui.viewmodel.ListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/list_app_bar"
layout="#layout/layout_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/history_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/transparent"
android:scrollbars="vertical"
app:data="#{viewModel.items}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/list_app_bar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
When i add two items and the header, the header and one item are
displayed.
problem is in your getItemCount method.
override fun getItemCount(): Int {
return if (items.isEmpty()) 1 else items.filter { it.viewType == ITEM }.size
}
If you want to show 1 header and 2 elements that means that there are must be 3 items in recyclerview, so getItemCount must return 3. But now it looks like getItemCount will return 2, thats why recycerlview doesn't even create third element.

RecyclerView scrolls to top when dragging item with ItemTouchHelper

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.
If you are using NestedScrollView just remove and instead of using other layouts use ConstraintLayout
Steps:
Use ConstrainLayout and RecylerView like this:
<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>
Note- you can also try to use:
viewBinding.recycler.setHasFixedSize(true)
If you are using notifyDataSetChanged() so just replace it by notifyItemMoved() in onMove() method

Categories

Resources