CRUD ListAdapter RecyclerView en Kotlin


Ejemplo de implementación de CRUD (Create Read Update Delete) sobre un ListAdapter integrado en un RecyclerView usando Kotlin.

El modelo a mostrar es simple, titulo y un contador numerico, que se actualizará cada vez que es pulsado el elemento

data class CounterTask(var title : String, var clicks : Int = 0)
  • Pulsar FabButton se añade un elemento a la lista, con un titulo aleatorio
  • Pulsar sobre un elemento se incrementará el valor númerico
  • Pulsación larga sobre un elemento se eliminará de la lista

Layout de representación de los datos `simple_list_item_2.xml`

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    android:orientation="vertical"
    android:background="?attr/selectableItemBackgroundBorderless"
    android:clickable="true"
    android:focusable="true"
    android:paddingStart="?attr/listPreferredItemPaddingStart"
    android:paddingEnd="?attr/listPreferredItemPaddingEnd">

    <TextView
        android:id="@android:id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:textAppearance="?attr/textAppearanceListItem"
        tools:text="Line one" />

    <TextView
        android:id="@android:id/text2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="?android:textColorSecondary"
        android:textAppearance="?attr/textAppearanceListItemSecondary"
        tools:text="Line two" />


</LinearLayout>

Layout del fragmento con el recyclerview fragment_list.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".ListFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvList"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fabAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@drawable/ic_add_24dp" />


</FrameLayout>

Adaptador ListAdapter

Adaptador MyListAdapter que implementa la funcionalidad de pulsar sobre cada elemento con el OnItemClickListener

class MyListAdapter(private val itemClickListener: OnItemClickListener) :
    ListAdapter<CounterTask, MyListAdapter.MyViewHolder>(DiffCallback()) {

    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
        }

    }

    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)
    }

    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
            }
        }
    }

    interface OnItemClickListener {
        fun onItemClick(position: Int, item: CounterTask)
        fun onItemLongClick(position: Int, item: CounterTask)
    }

}

Inicialización del RecyclerView (Read)

A continuación el código de carga de datos, se almacenarán en la variable dataSet

private var dataSet: MutableList<CounterTask> = arrayListOf()
private lateinit var mAdapter: MyListAdapter

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    initRecyclerView()

    ...
}

private fun initRecyclerView() {
    mAdapter = MyListAdapter(this)
    rvList.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
    rvList.adapter = mAdapter
}

Añadir datos (Create)

en onViewCreated añadir la detección de pulsar sobre el fabAdd y añade un elemento a la lista y actualice los datos con submitList

    fabAdd.setOnClickListener {
        dataSet.add(CounterTask(randomStr()))

        //Update new list
        mAdapter.submitList(dataSet)
        mAdapter.notifyDataSetChanged()
    }

Actualizar datos (Update)

Al pulsar sobre un elemento de la lista, actualice el contador, se añade la intercepción del evento MyListAdapter.OnItemClickListener

class ListFragment : Fragment(), MyListAdapter.OnItemClickListener
...
override fun onItemClick(position: Int, item: CounterTask) {

    Log.d(TAG, "onItemClick($position) item(${item.toString()})")

    val item = dataSet[position]
    item.clicks = item.clicks + 1
 
    dataSet[position] = item

    mAdapter.submitList(dataSet)
    mAdapter.notifyDataSetChanged()
}

Eliminar datos (Delete)

Al realizar una pulsación larga sobre el elemento, se elimine y actualice la lista

override fun onItemLongClick(position: Int, item: CounterTask) {

    Log.d(TAG, "onItemLongClick($position) item(${item.toString()})")

    dataSet.removeAt(position)

    mAdapter.submitList(dataSet)
    mAdapter.notifyDataSetChanged()

}

Animar acción

Si queremos añadir animación al añadir elementos, actualizar o eliminar, se debe añadir animateLayoutChanges a true en el layout padre

android:animateLayoutChanges="true"

Publicado por Codelaby

Mobile DevDesigner

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

A %d blogueros les gusta esto: