Sélection des éléments recylerView à l'aide de dataBinding

Salut. L'autre jour, j'ai rencontré le problème de l'implémentation d'une sélection de plusieurs éléments dans RecyclerView à l'aide de dataBinding.





Commencer

Tout d'abord, écrivons un adaptateur de base qui prend en charge dataBinding.






/**
 *    data binding'.
 * @param layoutRes id layout',     
 * @param lifecycleOwner lifecycle owner   ,    recycler view
 * @param itemBindingId id   layout',     
 * @param onClick ,     
 */
class RecyclerViewAdapter<Item : IRecyclerViewItem>(
    @LayoutRes private val layoutRes: Int,
    private val lifecycleOwner: LifecycleOwner,
    private val itemBindingId: Int? = null,
    private val onClick: ((Item) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerViewAdapter<Item>.ViewHolder>() {
    private val items = mutableListOf<Item>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        //  ViewDataBinding    layoutRes
        val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, layoutRes, parent, false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        //    onBind   
        val item = items[position]
        holder.onBind(item)
    }

    /**
     *    
     *
     * @param newItems  
     */
    fun setItems(newItems: List<Item>) {
        val diffUtilCallback = DiffUtilCallback(newItems)
        val diffResult = DiffUtil.calculateDiff(diffUtilCallback)
        items.apply {
            clear()
            addAll(newItems)
        }
        diffResult.dispatchUpdatesTo(this)
    }

    //    DataBinding'
    inner class ViewHolder(
        private val binding: ViewDataBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(item: Item) {
            binding.apply {
                // 
                setVariable(itemBindingId ?: BR.item, item)

                root.setOnClickListener { onClick?.invoke(item) }
                lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
            }
        }
    }

    private inner class DiffUtilCallback(private val newItems: List<Item>) : DiffUtil.Callback() {
        override fun getOldListSize(): Int = itemCount
        override fun getNewListSize(): Int = newItems.size

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition].id == items[oldItemPosition].id
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition] == items[oldItemPosition]
        }
    }
}
      
      



Aussi, pour que DiffUtil fonctionne, j'ai créé une interface qui montre que l'élément a un champ unique





/**
 *   ui ,   RecyclerViewAdapter.
 * @property id   
 */
interface IRecyclerViewItem {
    val id: Int
}
      
      



Jusqu'à récemment, cet adaptateur était capable de résoudre presque toutes les tâches avec des listes. Selon le projet, onClick peut être remplacé par onBind: (binding: ViewDataBinding) -> Unit, vous pouvez ainsi personnaliser les éléments individuels de l'élément.





Aide à la sélection

, SelectionHelper, dataBinding' .





, , , .





, , :





class SelectionHelper<T : IRecyclerViewItem> : ISelectionHelper<T>() {
    //    
    private val selectedItems = mutableMapOf<Int, T>()
   
    // ,     -  ,  - 
    override fun handleItem(item: T) {
        if (selectedItems[item.id] == null) {
            selectedItems[item.id] = item
        } else {
            selectedItems.remove(item.id)
        }
        // dataBining,     ui)
        notifyChange()
    }

    override fun isSelected(id: Int): Boolean = selectedItems.containsKey(id)
    override fun getSelectedItems(): List<T> = selectedItems.values.toList()
    override fun getSelectedItemsSize(): Int = selectedItems.size
}

//    BaseObservable,  ,   dataBinding    
//   
abstract class ISelectionHelper<T : IRecyclerViewItem> : BaseObservable() {
    abstract fun handleItem(item: T)
    abstract fun isSelected(id: Int): Boolean
    abstract fun getSelectedItems(): List<T>
    abstract fun getSelectedItemsSize(): Int
}
      
      



:





  • viewModel selectionHelper.getSelectedItems, .





  • DataBinding, - adapter





  • , onBind





:





  1. viewModel/presenter ,









  2. xml





- ,





adadpter

class RecyclerViewAdapter<Item : IRecyclerViewItem>(
    @LayoutRes private val layoutRes: Int,
    private val lifecycleOwner: LifecycleOwner,
    private val itemBindingId: Int? = null,
    //  ,     ,     
    private val selectionHelper: ISelectionHelper<Item>? = null,
    private val onClick: ((Item) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerViewAdapter<Item>.ViewHolder>() {
  ...
	    inner class ViewHolder(
        private val binding: ViewDataBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(item: Item) {
            binding.apply {
                // 
                setVariable(itemBindingId ?: BR.item, item)
                selectionHelper?.let { setVariable(BR.selectionHelper, it) }

                root.setOnClickListener { 
                  	//  
                		selectionHelper?.handleItem(item)
                  	onClick?.invoke(item)
                }
                lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
            }
        }
    }  
}
      
      



/ , , onBind, - .





class RecyclerViewAdapter<Item : IRecyclerViewItem>(
    @LayoutRes private val layoutRes: Int,
    private val lifecycleOwner: LifecycleOwner,
    private val itemBindingId: Int? = null,
    private val selectionHelper: ISelectionHelper<Item>? = null,
    private val onClick: ((Item) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerViewAdapter<Item>.ViewHolder>() {
    private val items = mutableListOf<Item>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, layoutRes, parent, false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = items[position]
        holder.onBind(item)
    }

    /**
     *    
     *
     * @param newItems  
     */
    fun setItems(newItems: List<Item>) {
        val diffUtilCallback = DiffUtilCallback(newItems)
        val diffResult = DiffUtil.calculateDiff(diffUtilCallback)
        items.apply {
            clear()
            addAll(newItems)
        }
        diffResult.dispatchUpdatesTo(this)
    }

    inner class ViewHolder(
        private val binding: ViewDataBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(item: Item) {
            binding.apply {
                // 
                setVariable(itemBindingId ?: BR.item, item)
                selectionHelper?.let { setVariable(BR.selectionHelper, it) }

                root.setOnClickListener { 
                  	//  
                		selectionHelper?.handleItem(item)
                  	onClick?.invoke(item)
                }
                lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
            }
        }
    }

    private inner class DiffUtilCallback(private val newItems: List<Item>) : DiffUtil.Callback() {
        override fun getOldListSize(): Int = itemCount
        override fun getNewListSize(): Int = newItems.size

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition].id == items[oldItemPosition].id
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition] == items[oldItemPosition]
        }
    }
}
      
      







, xml , selectionHelper xml





<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="item"
            type="ImageItemUi" />
				<!--    ) -->
        <variable
            name="selectionHelper"
            type="dev.syncended.ctime.utils.ui.ISelectionHelper&lt;ImageItemUi>" />

        <import type="dev.syncended.ctime.models.ui.ImageItemUi" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_margin="@dimen/ui_spacing_normal"
            android:padding="@dimen/1dp"
            android:scaleType="centerCrop"
            app:file="@{item.file}"
            app:item_id="@{item.id}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="1:1"
            app:layout_constraintTop_toTopOf="parent"
            app:selection_helper="@{selectionHelper}"
            tools:ignore="ContentDescription" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
      
      



padding = 1dp, , , .





bindingAdapter selectionHelper





//  selectionHelper',     id 
@BindingAdapter("selection_helper", "item_id", requireAll = true)
fun <T : IRecyclerViewItem> handleSelection(
    view: View,
    selectionHelper: ISelectionHelper<T>,
    itemId: Int
) {
    //   
    val isSelected = selectionHelper.isSelected(itemId)
    //     
    val color = if (isSelected) {
        R.color.color_primary
    } else {
        android.R.color.transparent
    }
    view.setBackgroundColor(ContextCompat.getColor(view.context, color))
}
      
      



, background.





:





:





En conséquence, nous avons un outil assez simple pour sélectionner les éléments de la liste. Si vous vous éloignez de la sélection habituelle avec un cadre, vous pouvez modifier l'état, par exemple, d'une case à cocher, selon qu'un élément est sélectionné ou non.





android:checked=@{selectionHelper.isSelected(item.id)}
      
      



Par analogie, vous pouvez faire un tas de différentes variantes d'utilisation de cet assistant.





Merci d'avoir lu, ceci est mon premier article, alors ne jugez pas strictement, et gardez également le chat.












All Articles