Reputation: 275
I have added an animation to the recyclerview to show the below transition.
When I long press on an item it shows the radio button and the item card moves to the right. The issue is that after the initial selection when ever I click or select other items, item6 and the items below animates again.
Can someone explain why this is happening and how I can fix this.
ListAdapter.kt:
class ListItemAdapter(private val values: List<PlaceholderContent.PlaceholderItem>
) : RecyclerView.Adapter<ListItemAdapter.ItemViewHolder>() {
private lateinit var itemClick: OnItemClick
private var selectedIndex: Int = -1
private var selectedItems: SparseBooleanArray = SparseBooleanArray()
private var isActive: Boolean = false
private var activateAnimation: Boolean = false
fun setItemClick(itemClick: OnItemClick) {
this.itemClick = itemClick
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ListItemAdapter.ItemViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.fragment_item,
parent,
false
)
return ItemViewHolder(view)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.itemView.apply {
findViewById<TextView>(R.id.item_number).text = values[position].id
findViewById<TextView>(R.id.content).text = values[position].content
}
holder.itemView.findViewById<CardView>(R.id.list_item).setOnClickListener {
itemClick.onItemClick(values[position], position)
}
holder.itemView.findViewById<CardView>(R.id.list_item).setOnLongClickListener {
itemClick.onLongPress(values[position], position)
true
}
toggleIcon(holder, position)
}
override fun getItemCount(): Int {
return values.size
}
private fun itemTransition(holder: ItemViewHolder){
val animator = ObjectAnimator.ofFloat(holder.itemView.findViewById(R.id.list_item), View.TRANSLATION_X, 150f)
animator.start()
}
private fun itemTransitionBack(holder: ItemViewHolder){
val animator = ObjectAnimator.ofFloat(holder.itemView.findViewById(R.id.list_item), View.TRANSLATION_X, 0f)
animator.start()
}
fun toggleIcon(holder: ItemViewHolder, position: Int){
val checkBox = holder.itemView.findViewById<RadioButton>(R.id.is_selected)
if(selectedItems.get(position, false)){
checkBox.isGone = false
checkBox.isChecked = true
}
else{
checkBox.isGone = true
checkBox.isChecked = false
}
if(isActive) checkBox.isGone = false
if(activateAnimation){
itemTransition(holder)
}
else
itemTransitionBack(holder)
if(selectedIndex == position) selectedIndex = - 1
}
fun selectedItemCount() = selectedItems.size()
fun toggleSelection(position: Int){
selectedIndex = position
if (selectedItems.get(position, false)){
selectedItems.delete(position)
}else {
selectedItems.put(position, true)
}
notifyItemChanged(position)
isActive = selectedItems.isNotEmpty()
activateAnimation = selectedItems.isNotEmpty()
notifyDataSetChanged()
}
fun clearSelection(){
selectedItems.clear()
notifyDataSetChanged()
}
interface OnItemClick {
fun onItemClick(item: PlaceholderContent.PlaceholderItem, position: Int)
fun onLongPress(item: PlaceholderContent.PlaceholderItem, position: Int)
}
inner class ItemViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
}
}
ItemFragment.kt
adapter = ListItemAdapter(PlaceholderContent.ITEMS)
val recyclerViewList = view.findViewById<RecyclerView>(R.id.list)
recyclerViewList.adapter = adapter
recyclerViewList.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false)
val myHelper = ItemTouchHelper(myCallback)
myHelper.attachToRecyclerView(recyclerViewList)
adapter.setItemClick(object : ListItemAdapter.OnItemClick{
override fun onItemClick(
item: PlaceholderContent.PlaceholderItem,
position: Int
) {
if(adapter.selectedItemCount() > 0)
toggleSelection(position)
}
override fun onLongPress(
item: PlaceholderContent.PlaceholderItem,
position: Int
) {
toggleSelection(position)
}
})
private fun toggleSelection(position: Int){
adapter.toggleSelection(position)
}
Upvotes: 4
Views: 1523
Reputation: 112
You can override getItemViewType(int position)
this will returning the id of the view which is always unique.
@Override
public int getItemViewType(int position) {
return position;
}
or
@Override
public int getItemViewType(final int position) {
return R.layout.fragment_item;
}
Since the Android system stores a static reference to each layout as an Integer in the “R” (resources) class, we can simply return the layout resource id to be used in the onCreateViewHolder()
method.
Upvotes: 1
Reputation: 173
Instead of position try & use holder.getAdapterPosition() same for all the click action your are performing in to the bind method of the adapter,
holder.itemView.findViewById<CardView>(R.id.list_item).setOnClickListener {
itemClick.onItemClick(values[position], holder.getAdapterPosition())
}
holder.itemView.findViewById<CardView>(R.id.list_item).setOnLongClickListener {
itemClick.onLongPress(values[position], holder.getAdapterPosition())
true
}
toggleIcon(holder, holder.getAdapterPosition())
Upvotes: 0
Reputation: 779
This is kind of a hack, but have you consider using?
holder.setIsRecyclable(false);
If the list would not be large and if it solves the issue, it can be used as a quick solution.
Upvotes: 0
Reputation: 27236
You're calling notifyDataSetChanged()
inside your adapter.toggleSelection(position)
, regardless of whether this position was updated or not, this is re-binding all the visible views (and running the animations again).
As stated in the comments, the reason why the 6th item is animated is likely due to the default ViewPool that the RecyclerView keeps (5 items). The 6th view is not part of that so it gets re-bound, re-displayed, and... re-animated.
What I would do is:
notifyDataSetChanged()
? Why are you calling that?Other than this, you could try to increase the RecycledViewPool as suggested by Pawel in the comments. Keep in mind this would likely be considered a code smell because different resolution, densities, screen sizes, etc., may affect how this behaves at runtime; this would be flaky and prone to fail, but depending on your particular use-case, may allow you go get away with it for now.
Upvotes: 1