En este tutorial verás cómo insertar datos con Room desde una actividad de creación de listas de compras.
Recuerda que puedes ver el alcance general de este ejemplo en la descripción de la App.
Al haber creado la base de datos de listas de compra, ahora agregaremos un RecyclerView
para mejorar la vista de MainActivity
.
Adicionalmente pondremos un FAB que inicie una nueva actividad de creación. El siguiente boceto muestra nuestro plan:
Puedes descargar el código completo desde el siguiente link:
Codifiquemos la solución.
1. Añadir RecyclerView Al Layout
Reemplazar el layout actual requiere las siguientes acciones:
- Reemplazar
ConstraintLayout
raíz porCoordinatorLayout
- Reemplazar
TextView
porRecyclerView
- Añadir FAB en la parte inferior derecha
- Añadir icono con símbolo ‘+’
- Añadir soporte de vectores
Basado en ello, abre el layout activity_main.xml y pega el siguiente código:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_height="match_parent"
tools:context=".shoppinglists.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floating_action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:srcCompat="@drawable/ic_add_24"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Para agregar el icono haz clic derecho en tu carpeta drawable, presiona New, luego Vector Asset. Seguido selecciona Clip Art en Asset Type, escribe «add» en el diálogo emergente y selecciona el icono que necesitamos.
Nómbralo ic_add_24 y confirma su inclusión.
Ahora abre el archivo build.gradle del módulo y pega la siguiente instrucción para habilitar el uso de vectores en el proyecto Android Studio:
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
Sincroniza y tendremos completa nuestra interfaz para mostrar las listas de compras.
2. Crear Adaptador Del RecyclerView
El layout para el ítem de la lista es simple. Crea el archivo shopping_list_item.xml y recubre un TextView
con un ConstraintLayout
:
<?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_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Lista de ejemplo" />
</androidx.constraintlayout.widget.ConstraintLayout>
A continuación crea la clase ShoppingListAdapter
.
La idea es inflar el layout del ítem. Agregar un método para actualizar los ítems y bindear el nombre de las listas de compra con el TextView existente.
Solución:
public class ShoppingListAdapter
extends RecyclerView.Adapter<ShoppingListViewHolder> {
private List<ShoppingList> mShoppingLists;
@NonNull
@Override
public ShoppingListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return ShoppingListViewHolder.create(parent);
}
@Override
public void onBindViewHolder(@NonNull ShoppingListViewHolder holder, int position) {
ShoppingList item = mShoppingLists.get(position);
holder.bind(item);
}
@Override
public int getItemCount() {
return mShoppingLists == null ? 0 : mShoppingLists.size();
}
public void setItems(List<ShoppingList> items) {
mShoppingLists = items;
notifyDataSetChanged();
}
}
Crea también la clase ShoppingListViewHolder
para procesar el inflado y el binding de cada ítem:
public class ShoppingListViewHolder extends RecyclerView.ViewHolder {
private final TextView mNameText;
public ShoppingListViewHolder(@NonNull View itemView) {
super(itemView);
mNameText = itemView.findViewById(R.id.name);
}
public void bind(ShoppingList item) {
mNameText.setText(item.getName());
}
public static ShoppingListViewHolder create(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.shopping_list_item, parent, false);
return new ShoppingListViewHolder(v);
}
}
3. Poblar RecyclerView Desde Room
¿Cómo ponemos el contenido de la base de datos en la lista?
- Abre
MainActivity
- Obtén la instancia del
RecyclerView
- Crea una instancia del Adaptador y asígnala al recycler
- Dirígete donde observamos el LiveData del ViewModel. Reemplaza ese código por la llamada del método
setItems()
del adaptador
Lo anterior reflejado en código sería así:
public class MainActivity extends AppCompatActivity {
private ShoppingListViewModel mViewModel;
private RecyclerView mList;
private ShoppingListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication());
mViewModel = new ViewModelProvider(this, factory)
.get(ShoppingListViewModel.class);
setupList();
setupFab();
}
private void setupList() {
mList = findViewById(R.id.list);
mAdapter = new ShoppingListAdapter();
mList.setAdapter(mAdapter);
mViewModel.getShoppingLists().observe(this, mAdapter::setItems);
}
private void setupFab() {
findViewById(R.id.floating_action_button)
.setOnClickListener(view -> addNewShoppingList());
}
private void addNewShoppingList() {
startActivity(new Intent(this, AddShoppingListActivity.class));
}
}
Si ejecutas el proyecto en este punto deberías ver este resultado:
4. Añadir Actividad De Creación De Listas De Compras
Para la actividad de «Nueva lista» usaremos una plantilla Empty Activity. El nombre de la clase será AddShoppingListActivity
.
El layout consta de un EditText y un botón de guardar. Abre activity_add_shopping_list.xml para satisfacer el diseño:
<?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:layout_height="match_parent"
android:padding="@dimen/normal_padding"
tools:context=".addshoppinglist.AddShoppingListActivity">
<Button
android:id="@+id/create_button"
style="?attr/materialButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/create_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/name_layout" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/name_layout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/shopping_list_hint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:id="@+id/name_field"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
También debemos asegurarnos de retornar con el Up Button. Por lo que habilitamos su aparición con en onCreate()
:
public class AddShoppingListActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_shopping_list);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
5. Guardar Lista Nueva En Base De Datos
Al presionar el botón de crear, debemos comunicárselo al ViewModel que es el encargado de insertar datos con Room con su método insert()
.
¿Cómo lo haces?
- Obtén la instancia de
ShoppingListViewModel
enonCreate()
- Obtén la instancia del boton de crear
- Asigna un objeto
OnClickListener
al botón - Llama a
insert()
del view model enonClick()
- Finaliza la actividad
Al completar las tareas anteriores tu actividad de creación se verá así:
public class AddShoppingListActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_shopping_list);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ViewModelProvider.AndroidViewModelFactory factory
= ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication());
ShoppingListViewModel vm = new ViewModelProvider(this, factory)
.get(ShoppingListViewModel.class);
setupCreateButton(vm);
}
private void setupCreateButton(ShoppingListViewModel vm) {
findViewById(R.id.create_button).setOnClickListener(
view -> {
// Obtener valor del campo de texto
EditText nameField = findViewById(R.id.name_field);
String name = nameField.getText().toString();
// Ignorar acción si hay 0 caracteres
if (name.isEmpty()) {
return;
}
// Crear entidad y guardarla
String id = UUID.randomUUID().toString();
ShoppingList shoppingList = new ShoppingList(id, name);
vm.insert(shoppingList);
// Ir a la lista
finish();
});
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
}
No olvides iniciar esta actividad al presionar el FAB de la lista:
private void setupFab() {
findViewById(R.id.floating_action_button)
.setOnClickListener(view -> addNewShoppingList());
}
private void addNewShoppingList() {
startActivity(new Intent(this, AddShoppingListActivity.class));
}
Ejecuta el proyecto y comprueba la inserción de listas.
Nota: Aunque en este ejemplo estamos usando un solo ViewModel, normalmente debes crear uno por cada controlador de vista.
6. Insertar Una Lista De Entities
Room tiene la capacidad de insertar múltiples entidades si pasamos una lista como parámetro.
Para ver su funcionamiento agreguemos a ShoppingListDao
el siguiente método:
@Insert(onConflict = OnConflictStrategy.IGNORE)
void insertShoppingLists(List<ShoppingList> lists);
Ahora desde ShoppingListDatabase
creamos una lista de 5 elementos para reemplazar la prepoblación que hacíamos antes de forma individual:
// Prepoblar base de datos con callback
private static final RoomDatabase.Callback mRoomCallback = new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
dbExecutor.execute(() -> {
ShoppingListDao dao = INSTANCE.shoppingListDao();
List<ShoppingList> lists = new ArrayList<>();
for (int i = 0; i < 5; i++) {
String id = UUID.randomUUID().toString();
lists.add(new ShoppingList(id, "Lista " + (i+1)));
}
dao.insertShoppingLists(lists);
});
}
};
De esta forma podremos hacer en una misma transacción un bulk de inserciones de listas de compras.
Siguiente tutorial: Consultar Datos Con Room [en proceso…]
Ú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!