Ejemplo de como mostrar una vista vacia en un RecyclerView cuando no haya datos, controlado desde el adaptador, para mostrar la vista vacia se implementa sistema de mostrar diferentes vistas.
Layout de vistas, para la vista vacia item_empty.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:padding="8dp"
android:paddingStart="?listPreferredItemPaddingStart"
android:paddingEnd="?listPreferredItemPaddingEnd"
android:text="@string/allowed_devices_empty"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
android:textColor="?android:textColorSecondary" />
</RelativeLayout>
Adaptador RecyclerView
Se debe definir las constantes por cada vista
companion object {
private val TAG = MyListAdapter::class.java.simpleName
private const val VIEW_TYPE_EMPTY = 0
private const val VIEW_TYPE_ITEM = 1
}
Para determinar que tipo de vista cargar en cada momento, se debe forzar el RecyclerView que cuando no haya datos que permite visualizar una vista, se hace sobrecargando getItemCount()
override fun getItemCount(): Int {
return when (val count = dataSet.count()) {
0 -> 1
else -> count
}
}
Luego para obtener que vista debe aplicarse, si no hay datos la vista vacia VIEW_TYPE_EMPTY y si hay datos la vista normal VIEW_TYPE_ITEM
override fun getItemViewType(position: Int): Int = if (dataSet.count() == 0) {
VIEW_TYPE_EMPTY
} else {
VIEW_TYPE_ITEM
}
Debemos crear los ViewHolders para las vistas EmptyViewHolder para la vista vacia y para la vista con datos MyViewHolder
inner class EmptyViewHolder(itemView: View) : BaseViewHolder<String>(itemView) {
private val tvText1 = itemView.findViewById(android.R.id.text1) as TextView
override fun bind(item: String) {
tvText1.text = item
}
}
inner class MyViewHolder(itemView: View) : BaseViewHolder<DeviceModel>(itemView) {
private val tvText1 = itemView.findViewById(android.R.id.text1) as TextView
private val tvText2 = itemView.findViewById(android.R.id.text2) as TextView
override fun bind(item: DeviceModel) {
tvText1.text = item.name
tvText2.text = item.macAddress
}
}
En onCreateViewHolder se cargará el layout corresponiente al tipo de vista a mostrar
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) {
VIEW_TYPE_EMPTY -> EmptyViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_empty, parent, false)
)
VIEW_TYPE_ITEM -> MyViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.simple_list_item_2, parent, false)
)
else -> throw IllegalArgumentException("Invalid view type")
}
En onBindViewHolder establecer los datos a mostrar en cada vista
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
if (position != RecyclerView.NO_POSITION) {
when (holder) {
is EmptyViewHolder -> holder.bind(emptyText)
is MyViewHolder -> holder.bind(dataSet[position])
else -> throw IllegalArgumentException("Invalid ViewHolder")
}
}
}
La carga de datos se hace con la funciñon submitList
fun submitList(newData: List<DeviceModel>) {
val needNotifyDataChanged = dataSet.size == 0
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(dataSet, newData))
diffResult.dispatchUpdatesTo(this)
dataSet.clear()
dataSet.addAll(newData)
if (needNotifyDataChanged) notifyItemChanged(dataSet.size)
}
Para vaciar la lista método clearData()
fun clearData() {
val size: Int = dataSet.size
dataSet.clear()
notifyItemRangeRemoved(0, size)
}
Su uso
mAdapter.emptyText = "Sin datos"
...
val dataSet: MutableList<DeviceModel> = arrayListOf()
dataSet.add(DeviceModel(true,"aaa22","ff",null,"device1"))
dataSet.add(DeviceModel(true,"bbb","ff",null,"device2"))
dataSet.add(DeviceModel(true,"ccc","ff",null,"device3"))
mAdapter.submitList(dataSet)
...
mAdapter.clearData()
Código
Dejo el código del ejemplo en mi GIT