RecyclerView scrolls to top when dragging item with ItemTouchHelper - android

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

Related

Drag and Drop to reorder and move from one to another recycling view

I am building an app to be able to drag an item from one recycle view to another and I still have to keep the option to re-order inside a single recycler view.
So I have defined a Reorder Callback already
class ReorderHelperCallback(val adapter : ItemTouchHelperAdapter): ItemTouchHelper.Callback() {
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
return makeMovementFlags( dragFlags, 0)//swipeFlags )
}
override fun onMove(
recyclerView: RecyclerView,
source: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
adapter.onItemMove(source.getAdapterPosition(),
target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
//Not use for Drag N Drop
}
}
also an interface:
interface OnStartDragListener {
fun onStartDrag(viewHolder: RecyclerView.ViewHolder?)
}
and a touchhelper:
interface ItemTouchHelperAdapter {
fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
fun onItemDismiss(position: Int)
}
to allow the reorder to work, I had to update the Recycler view adapter as below:
class Adapter(
private var context: Context?,
val dragStartListener : OnStartDragListener
): RecyclerView.Adapter<Adapter.ViewHolder>(), ItemTouchHelperAdapter {
var arrayItems : ArrayList<Data?> = ArrayList()
fun setData(array : MutableList<Data?>){
array.toCollection(arrayItems)
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return arrayItems.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Adapter.ViewHolder {
val binding = DashboardTileLayoutBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding, dragStartListener)
}
override fun onBindViewHolder(holder: Adapter.ViewHolder, position: Int) {
holder.setData(arrayItems[position])
}
inner class ViewHolder(val binding: LayoutBinding,
val dragStartListener : OnStartDragListener? = null)
: RecyclerView.ViewHolder(binding.root) {
val tileLayout = binding.tileLayout
fun setData(data: Data?) {
....
tileLayout.setOnDragListener { view, dragEvent ->
when(dragEvent.action) {
ACTION_DRAG_STARTED -> {
dragStartListener?.onStartDrag(this)
true
}
else -> false
}
}
}
}
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
Collections.swap(arrayItems, fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
return true
}
override fun onItemDismiss(position: Int) {
TODO("Not yet implemented")
}
}
and the fragment which contain the rv, I have updated the adapter init:
list1adapter?.let { adapter ->
adapter.setData(list)
val callback: ItemTouchHelper.Callback = ReorderHelperCallback(adapter)
mItemTouchHelperSelected = ItemTouchHelper(callback)
mItemTouchHelperSelected?.attachToRecyclerView(selectedLayout)
}
---
override fun onStartDrag(viewHolder: RecyclerView.ViewHolder?) {
viewHolder?.let {
mItemTouchHelperSelected?.startDrag(it)
}
}
But my fragment contain 2 recycler views. list1 is working fine to user drag and drop to re-order the item but now, I would like to also be able to move an item from my rv list1 to the list2 and vice versa
Any idea, how to make it Kotlin ? I tried an sample code, byt I am losing the re-ordering.
Thanks

MERGE ITEMS in Recycler View when dragged & dropped on one another, in ANDROID?

Using ItemTouchHelper class we can DRAG, DROP, & SWIPE items in the recycler view; but how to just merge two items when dragged & dropped on one another?
Is it possible to do using ItemTouchHelper (or) is there any other API for that?
You could override onMove() in your ItemTouchHelper, it is called when you drag item A over item B. It gets called with the parameters viewHolder: RecyclerView.ViewHolder and target: RecyclerView.ViewHolder where viewHolder is the viewHolder of item A, and target is item B.
Have some variable of type ViewHolder, that you set to target in onMove, to always have a reference to the item below item A.
override clearView() to detect when the item is dropped, update your model in the background, so itemA now is merged with target, then call notifyItemChanged(itemA.adapterPosition) and notifyItemRemoved(itemB.adapterPosition) to animate a "merge"
class MainActivity : AppCompatActivity() {
companion object{
val fruit = arrayListOf("apple", "pear", "orange", "banana", "grape", "pineapple")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView1 = findViewById<RecyclerView>(R.id.testRecycler)
val layoutManager = LinearLayoutManager(this)
recyclerView1.layoutManager = layoutManager
val adapter = FruitAdapter()
recyclerView1.adapter = adapter
val itemTouchHelper = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
0
) {
#SuppressLint("StaticFieldLeak")
var target: RecyclerView.ViewHolder? = null
#SuppressLint("StaticFieldLeak")
var moving: RecyclerView.ViewHolder? = null
override fun clearView(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
) {
if(target!=null && moving != null){
val targetPos = target!!.adapterPosition
val sourcePos = moving!!.adapterPosition
fruit[targetPos] += "\n\n" + fruit[sourcePos]
fruit.removeAt(sourcePos)
target = null
moving = null
adapter.notifyDataSetChanged()
}
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder
): Boolean {
this.target = target
this.moving = viewHolder
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
})
itemTouchHelper.attachToRecyclerView(recyclerView1)
}
}
class FruitAdapter: RecyclerView.Adapter<FruitAdapter.FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.item, parent, false)
return FruitViewHolder(itemView)
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
holder.itemView.findViewById<TextView>(R.id.fruitNameTextView).text = MainActivity.fruit[position]
}
override fun getItemCount(): Int {
return MainActivity.fruit.size
}
class FruitViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
}
item.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:padding="20px"
android:layout_margin="20px"
android:background="#color/teal_200"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/fruitNameTextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="#+id/testRecycler"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Drag and drop issue with RecyclerView, ItemTouchHelper and GridLayoutManager

I have found an issue when dragging an item that doesn't trigger a move on a GridLayoutManager with 2 columns.
When dragging one item from the first column to the top of the list (which scrolls the list upwards) and then back to its original position, the recyclerView creates an empty space next to the original position, pushing the item that was next to the dragged item to the next row.
I have coded the simplest app to show you what I mean. On the first screenshot I start dragging one item towards the top of the list. On the second screenshot I reach the top of the screen. Then I move it back to its position and you can already see how the items have not been laid down properly.
Has anyone encountered this as well? I have not found any solution to this issue.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myAdapter = MyAdapter()
with(findViewById<RecyclerView>(R.id.recyclerView)) {
adapter = myAdapter
layoutManager = GridLayoutManager(this#MainActivity, 2)
ItemTouchHelper(object : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, DOWN or UP or START or END)
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun canDropOver(recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
}).attachToRecyclerView(this)
}
myAdapter.submitList(data)
}
data class ListItem(val id: String, val name: String)
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
class MyAdapter : ListAdapter<ListItem, MyViewHolder>(MyAdapterDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
}
class MyAdapterDiffCallback : DiffUtil.ItemCallback<ListItem>() {
override fun areItemsTheSame(oldItem: ListItem, newItem: ListItem) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: ListItem, newItem: ListItem) = oldItem == newItem
}
}

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.

When changing list item layout RecyclerView scrolls to top

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
}

Categories

Resources