En este tutorial veremos cómo actualizar datos con Room a través de la anotación @Update
.
La anotación @Update
nos permite actualizar tanto un solo registro como múltiples a la vez dentro de una transacción. Además podemos actualizar solo las filas necesarias como en la inserción parcial vista anteriormente.
La Anotación @Update
La anotación @Update
marca a un método de un DAO como un método de actualización.
Como se decía al inicio, puedes modificar 1 o más filas relacionadas a una clase @Entity
.
Por ejemplo, actualizar las filas de nuestra tabla shopping_list
desde ShoppingListDao
se vería así:
@Update
void updateShoppingList(ShoppingList shoppingList);
@Update
void updateShoppingLists(List<ShoppingList> shoppingLists);
Si no quisiéramos crear toda la entidad para enviarla en actualización, entonces podemos mapear la inserción en un POJO con las columnas necesarias.
Cabe resaltar que hay que especificar la propiedad entity
con la clase.
Ejemplo:
En nuestra App de listas de compras necesitamos marcar las listas como favoritas. Esto implica la modificación de la columna is_favorite
y last_updated
. Basado en esto, creamos la siguiente clase:
public class ShoppingListFavorite {
public String id;
@ColumnInfo(name = "is_favorite")
public boolean favorite;
@ColumnInfo(name = "last_updated")
public String lastUpdated;
}
Luego en nuestro DAO añadimos un método de actualización que reciba como parámetro este tipo:
@Update(entity = ShoppingList.class)
void markFavorite(ShoppingListFavorite shoppingList);
Por otro lado, es importante saber que la actualización parcial también puede ser representada con la anotación @Query
de la siguiente forma:
@Query("UPDATE shopping_list SET is_favorite = NOT is_favorite WHERE id = :id)
void markFavorite(String id);
Esta variante puede serte de utilidad si no deseas pasar una entidad.
Ejemplo De Actualización De Datos Con Room
Para practicar la actualización implementaremos permitiremos al usuario marcar como favoritas las listas ya sea de forma individual o en una selección múltiple como se ve en el siguiente boceto:
Puedes descargar el código completo desde el siguiente enlace si deseas guiarte a la par con él:
Codifiquemos la solución.
1. Agregar Columna Para Favoritas
Agrega una campo booleano llamado is_favorite
a la entidad ShoppingList
con un valor por defecto en 0
(FALSE
). Modifica el constructor y agrega un método get respectivamente:
@ColumnInfo(name = "is_favorite", defaultValue = "0")
private final boolean mFavorite;
Ya sabes que al cambiar el esquema debemos subir la versión de la base de datos (o si quieres desinstala la App para seguir con el mismo número), por lo que vamos a ShoppingListDatabase
y aumentamos a 3
el valor.
2. Añadir Botón De Favorito
Necesitamos ver el botón de la siguiente forma:
Dirígete al layout shopping_list_item.xml y agrega un CheckButton al costado derecho del layout para representar un toggle button. La idea es que si es favorito se muestre el icono relleno, de lo contrario delineado.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:padding="@dimen/normal_padding"
android:layout_height="?listPreferredItemHeight">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/favorite_button"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Lista de ejemplo" />
<CheckBox
android:id="@+id/favorite_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:button="@drawable/sl_favorite_24"
app:buttonTint="@color/favorite_color"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Añade los vectores star y star_outlined con forma de estrella desde New > Vector Asset. Y luego añade un selector llamado sl_favorite_24.xml, que use ambos iconos dependiendo del estado:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/ic_star_outline_24"
android:state_checked="false"
/>
<item
android:drawable="@drawable/ic_star_24"
android:state_checked="true"
/>
<item android:drawable="@drawable/ic_star_outline_24" />
</selector>
3. Actualizar Modelo De Listas De Compras
Actualmente usamos la clase ShoppingListForList
para mostrar los datos necesarios en la lista. Para sostener a la columna is_favorite
agregamos un campo equivalente:
public class ShoppingListForList {
public String id;
public String name;
@ColumnInfo(name = "is_favorite")
public boolean favorite;
}
Luego bindeamos su resultado al view del ítem en el adaptador:
public void bind(ShoppingListForList item) {
mNameText.setText(item.name);
mFavoriteButton.setChecked(item.favorite);
}
Si ejecutas verás a todos las listas desmarcadas:
También es necesario cambiar los métodos de consulta en el DAO para que consultemos las tres columnas:
@Query("SELECT id, name, is_favorite FROM shopping_list")
LiveData<List<ShoppingListForList>> getAll();
@Query("SELECT id, name, is_favorite FROM shopping_list WHERE category IN(:categories)")
LiveData<List<ShoppingListForList>> getShoppingListsByCategories(List<String> categories);
4. Procesar Evento En Adaptador
Escuchar el clic sobre el botón de favorito requiere que añadamos un nuevo método a la interfaz ItemListener
del adaptador, que notifique a MainActivity
dicho evento.
interface ItemListener {
void onClick(ShoppingListForList shoppingList);
void onFavoriteIconClicked(ShoppingListForList shoppingList);
}
El ViewHolder enlazara la escucha OnClickListener
con el método onFavoriteIconClicked()
:
public class ShoppingListViewHolder extends RecyclerView.ViewHolder {
private final TextView mNameText;
private final CheckBox mFavoriteButton;
public ShoppingListViewHolder(@NonNull View itemView) {
super(itemView);
mNameText = itemView.findViewById(R.id.name);
mFavoriteButton = itemView.findViewById(R.id.favorite_button);
// Setear eventos
mFavoriteButton.setOnClickListener(this::manageEvents);
itemView.setOnClickListener(this::manageEvents);
}
private void manageEvents(View view) {
if (mItemListener != null) {
ShoppingListForList clickedItem = mShoppingLists.get(getAdapterPosition());
// Manejar evento de click en Favorito
if (view.getId() == R.id.favorite_button) {
mItemListener.onFavoriteIconClicked(clickedItem);
return;
}
mItemListener.onClick(clickedItem);
}
}
public void bind(ShoppingListForList item) {
mNameText.setText(item.name);
mFavoriteButton.setChecked(item.favorite);
}
}
La idea es comunicarle al ViewModel
que debe iniciar una actualización de la lista de compras con el Id determinado. El siguiente código muestra como:
// Asignar escucha de ítems
mAdapter.setItemListener(new ShoppingListAdapter.ItemListener() {
@Override
public void onClick(ShoppingListForList shoppingList) {
editShoppingList(shoppingList);
}
@Override
public void onFavoriteIconClicked(ShoppingListForList shoppingList) {
mViewModel.markFavorite(shoppingList);
}
});
5. Propagar Acciones Hacia ViewModel Y Repositorio
En ShoppingListViewModel
crearemos el método markFavorite()
como una envoltura para el repositorio de listas de compras.
public void markFavorite(ShoppingListForList shoppingList) {
ShoppingListFavorite favorite = new ShoppingListFavorite();
favorite.id = shoppingList.id;
favorite.favorite = !shoppingList.favorite;
favorite.lastUpdated = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss",
Locale.getDefault())
.format(new Date());
mRepository.markFavorite(favorite);
}
Preparamos uno objeto ShoppingListFavorite
con el id, la negación del estado actual de favoritismo y la obtención de la fecha con el formato que tenemos actualmente.
Acto seguido, nos creamos un método equivalente en el repositorio, para llamar el método markFavorite()
de ShoppingListDao
en un hilo separado:
public void markFavorite(ShoppingListFavorite shoppingLists) {
ShoppingListDatabase.dbExecutor.execute(
() -> mShoppingListDao.markFavorite(shoppingLists)
);
}
Finalmente, si has seguido todos los pasos anteriores, ejecuta y marca como favoritas las que desees. Verás el siguiente resultado y como estos valores persisten localmente:
Siguiente tutorial: Eliminar Datos Con Room
Únete Al Discord De Develou
Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!