ListAdapter para RecyclerView se intruducio en la versión 27.1.0 de la biblioteca de soporte.
ListAdapter usa DiffUtil para rellenar los datos con los cambios efectuados, lo ejecuta en un subproceso en segundo plano, esto ayudará a RecyclerView a animar los cambios automáticamente con menos trabajo en el hilo principal sin bloquear la interfaz de usuario.
ListAdapter se extiende de RecyclerView.Adapter y implementa 3 cosas
- getItemCount()
- onCreateViewHolder()
- onBindViewHolder()
Crear un ListAdapter
Crear una clase que se extienda de ListAdapter, se le debe asignar el ViewHolder y el DiffCallback
class MyListAdapter : ListAdapter<CounterTask, MyListAdapter.MyViewHolder>(DiffCallback())
En el evento onCreateViewHolder es donde se cargará la vista de cada elemento a mostrarse dentro del RecyclerView
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.simple_list_item_2, parent, false)
return MyViewHolder(v)
}
Creación del MyViewHolder
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val tvText1 = itemView.findViewById(android.R.id.text1) as TextView
private val tvText2 = itemView.findViewById(android.R.id.text2) as TextView
fun bind(item: CounterTask) {
tvText1.text = item.title
tvText2.text = item.clicks.toString()
}
}
Creación del DiffCallback, encargado de comprobar los datos viejos con los nuevos y determinar si es un cambio, añadido o se debe remover
private class DiffCallback : DiffUtil.ItemCallback<CounterTask>() {
override fun areItemsTheSame(oldItem: CounterTask, newItem: CounterTask): Boolean {
return oldItem.title == newItem.title
}
override fun areContentsTheSame(oldItem: CounterTask, newItem: CounterTask): Boolean {
return oldItem == newItem
}
}
Carga de datos
ListAdapter se encarga de la carga de datos con el método submitList(datos) y luego con notifyDataSetChanged() para que el RecyclerView actualice los datos
mAdapter = MyListAdapter()
mAdapter.submitList(dataSet)
mAdapter.notifyDataSetChanged()
Soporte OnItemClickListener
Para implementar que los elementos se pueden clickear añadir en el layout de cada elemento
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
En el adaptador añadir
class MyListAdapter(private val itemClickListener: OnItemClickListener)
...
interface OnItemClickListener {
fun onItemClick(position: Int, item: CounterTask)
fun onItemLongClick(position: Int, item: CounterTask)
}
En la carga del ViewHolder interceptar el evento OnClick
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(getItem(position), itemClickListener)
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val tvText1 = itemView.findViewById(android.R.id.text1) as TextView
private val tvText2 = itemView.findViewById(android.R.id.text2) as TextView
fun bind(item: CounterTask, clickListener: OnItemClickListener) {
tvText1.text = item.title
tvText2.text = item.clicks.toString()
itemView.setOnClickListener { clickListener.onItemClick(adapterPosition, item) }
itemView.setOnLongClickListener {
clickListener.onItemLongClick(adapterPosition, item)
return@setOnLongClickListener false
}
}
}
Y la inicialización del adapter
class ListFragment : Fragment(), MyListAdapter.OnItemClickListener {
...
mAdapter.setOnItemClickListener(this)
...
override fun onItemClick(position: Int, item: CounterTask) {
context.toast("clicked $position " + item.name)
}
override fun onItemLongClick(position: Int, item: CounterTask) {
context.toast("clicked $position " + item.name)
}