Crea el proyecto
    Para este proyecto partiremos de una copia del proyecto RecyclerView.
    Clona o descarga: https://github.com/gerardfp/room_template
    
        Puedes añadir ahora las dependencias para la librería Room, 
            o puedes hacer que el propio Android Studio lo haga después (con Ctrl + Intro)
        
            build.gradle (Module: app)
        
        
            
            dependencies {
                // ...
            
            
                implementation 'androidx.room:room-runtime:2.2.5'
                annotationProcessor 'androidx.room:room-compiler:2.2.5'
            
            
            }
            
        
    
    Room
    La librería Room es una capa de acceso a bases de datos SQLite en aplicaciones Android. 
        Proporciona una forma sencilla de crear y acceder a la base de datos. Utiliza el mapeo objeto-relacional, 
        que consiste en que cada fila de una tabla de la base de datos corresponde con un objeto en el código Java. 
        Además está diseñada para integrarse fácilmente con la arquitectura MVVM. 
    
    Para usar la librería Room hay que definir 3 componentes:
    
        Database: contiene la referencia a la base de datos.
 
        Entity: representa una tabla de la base de datos.
 
        Dao: contiene los métodos con las consultas para acceder a la base de datos.
 
    
    Database
    Para crear la base de datos se ha de crear una clase que debe:
    
        Ser abstracta
 
        Extender RoomDatabase
 
        Estar anotada con @Database
 
        Incluir la lista de entidades dentro de la anotación @Database
 
        Contener un método abstracto que retorne la clase anotada con @Dao
 
    
    Dentro de la clase se llama al metodo estático Room.databaseBuilder() para obtener la referencia a la base de datos.
    Es conveniente utilizar el patrón singleton, 
        que asegura que solo habrá una única instancia de la base de datos.
    
    Crea la clase ElementosBaseDeDatos:
    
        ElementosBaseDeDatos.java
    
    
        import android.content.Context;
        import androidx.room.Database;
        import androidx.room.Room;
        import androidx.room.RoomDatabase;
        @Database(entities = {}, version = 1, exportSchema = false)
        public abstract class ElementosBaseDeDatos extends RoomDatabase {
            private static volatile ElementosBaseDeDatos INSTANCIA;
            static ElementosBaseDeDatos obtenerInstancia(final Context context) {
                if (INSTANCIA == null) {
                    synchronized (ElementosBaseDeDatos.class) {
                        if (INSTANCIA == null) {
                            INSTANCIA = Room.databaseBuilder(context,
                                    ElementosBaseDeDatos.class, "elementos.db")
                                    .fallbackToDestructiveMigration()
                                    .build();
                        }
                    }
                }
                return INSTANCIA;
            }
        }
    
    Cuando se llame al método obtenerInstancia(), Room creará la base de datos SQLite (elementos.db), 
        o utilizará la base de datos existente si ya se había creado anteriormente.
    La llamada al método .fallbackToDestructiveMigration() hará que en caso de que alteremos el esquema de la base de datos 
        (añadiendo tablas, campos, etc...), la base de datos se destruya y se vuelva a crear. 
    
    
        También se pueden añadir métodos para proporcionar una migración no destructiva. Ver:
            Migrating Room databases
        
    
    Entity
    Las tablas de la base de datos se definen anotando una clase con @Entity. Room creará una tabla en la base de datos por cada 
    clase anotada con @Entity. Los campos de la tabla corresponderán con los campos de la clase.
    En esta app habrá una única tabla para almacenar los Elementos. Para definir la tabla será suficiente con anotar la clase Elemento
    con @Entity.
 
    Añadiremos un campo id que anotaremos con @PrimaryKey, que será la clave primaria de la tabla. La propiedad
    autoGenerate = true, hará que SQLite asigne el valor automáticamente.
    
    Anota la clase Elemento de la siguiente forma:
    
        Elemento.java
    
    
        import androidx.room.Entity;
        import androidx.room.PrimaryKey;
        
        @Entity
        
        public class Elemento {
        
            @PrimaryKey(autoGenerate = true)
            int id;
        
            String nombre;
            String descripcion;
            float valoracion;
            public Elemento(String nombre, String descripcion) {
                this.nombre = nombre;
                this.descripcion = descripcion;
            }
        }
    
    Una vez anotadas las entidades, hay que asociarlas con una base de datos.
    Añade la entidad Elemento.class a la lista de entities de la base de datos:
    
        ElementosBaseDeDatos.java
    
    
        
        @Database(entities = { Elemento.class }, version = 1, exportSchema = false)
        
        public abstract class ElementosBaseDeDatos extends RoomDatabase {
            // ...
        }
    
    A partir de estas anotaciones Room creará la siguiente tabla en la base de datos:
    
        CREATE TABLE Elemento(id INTEGER PRIMARY KEY, nombre TEXT, descripcion TEXT, valoracion REAL);
        
    Dao
    Para acceder a los datos, Room utiliza data access objects, o DAOs.
    La clase DAO contiene métodos para acceder a la base de datos (select, insert, update, delete). Cada método está asociado a una consulta SQL.
    Room proporciona anotaciones para autogenerar las consultas SQL (@Query, @Insert, @Update, @Delete).
    El DAO puede ser un interfaz o una clase abstracta. Room implementará automáticamente los métodos 
        a partir de las anotaciones, generando las consultas SQL apropiadas.
    Puedes crear la clase ElementosDao dentro mismo de la clase ElementosBaseDeDatos (también se podría crear 
        en un fichero a parte):
    
        ElementosBaseDeDatos.java
    
    
        
        public abstract class ElementosBaseDeDatos extends RoomDatabase {
        
        
            public abstract ElementosDao obtenerElementosDao();
        
        
            //...
        
        
            @Dao
            interface ElementosDao {
                @Query("SELECT * FROM Elemento")
                LiveData<List<Elemento>> obtener();
        
                @Insert
                void insertar(Elemento elemento);
        
                @Update
                void actualizar(Elemento elemento);
        
                @Delete
                void eliminar(Elemento elemento);
            }
        
        
        }
        
    
    
        - 
            
El método obtener() está anotado con @Query y se especifica la consulta SQL que se debe hacer cuando se llame a este método.
                El resultado de la consulta (la lista de elementos List<Elemento>) se retornará como un LiveData. De esta forma 
                el Array se actualizará cuando cambien los datos de la tabla Elemento, y podemos observarlo desde la Vista para actualizar la Interfaz de usuario
                automáticamente. Es decir, no habrá que estar continuamente consultando la base de datos, sino que se observará y ella misma notificará de los cambios. 
                
            
         
        - Los métodos insertar(), actualizar() y eliminar(), reciben un objeto Elemento como parámetro y Room ejecutará las sentencias 
        SQL correspondientes automáticamente con los datos de ese Elemento.
 
    
    El método abstracto obtenerElementosDao() será implementado por Room, y retornará un objeto de 
        clase ElementosDao con todos los métodos implementados. 
    Utilizaremos los métodos de este objeto en la app para acceder a la base de datos. 
    Acceso a la base de datos
    Una vez creada la base de datos, definidas las tablas, y los métodos de acceso, ya podemos utilizarla en la app.
    En la versión actual de la app los Elementos se guardaban en un ArrayList de la clase ElementosRepositorio. 
        Modificaremos esta clase para que guarde los Elementos en la base de datos utilizando el ElementosDao.
    Pasaremos de esta arquitectura:
    
    A esta:
    
    Empezaremos borrando todas las referencias al ArrayList.
    Elimina las líneas resaltadas:
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
        
            List<Elemento> elementos = new ArrayList<>();
        
            interface Callback {
                void cuandoFinalice(List<Elemento> elementos);
            }
        
            ElementosRepositorio(){
        
                elementos.add(new Elemento("Elemento químico", "Es un átomo con moléculas, aquella sustancia que no puede ser descompuesta mediante una reacción química, en otras más simples. Pueden existir dos átomos de un mismo elemento con características distintas y, en el caso de que estos posean número másico distinto, pertenecen al mismo elemento pero en lo que se conoce como uno de sus isótopos."));
                elementos.add(new Elemento("Elemento de un conjunto", "En teoría de conjuntos, un elemento o miembro de un conjunto (o familia de conjuntos) es un objeto que forma parte de ese conjunto (o familia)."));
                elementos.add(new Elemento("Elemento sintético", "En química, un elemento sintético es un elemento químico que no aparece de forma natural en la Tierra, y solo puede ser creado artificialmente."));
                elementos.add(new Elemento("Elemento algebraico", "En matemáticas, más concretamente en álgebra abstracta y teoría de cuerpos, se dice que un elemento es algebraico sobre un cuerpo si es raíz de algún polinomio con coeficientes en dicho cuerpo. Los elementos algebraicos sobre el cuerpo de los números racionales reciben el nombre de números algebraicos."));
                elementos.add(new Elemento("Elementos de la naturaleza","Los cuatro o cinco elementos de la naturaleza —normalmente agua, tierra, fuego y aire, a los que se añade la quintaesencia o éter— eran, para muchas doctrinas antiguas, los constituyentes básicos de la materia y explicaban el comportamiento de la naturaleza. El modelo estuvo vigente hasta que la ciencia moderna empezó a desentrañar los elementos y reacciones químicas."));
                elementos.add(new Elemento("Elemento constructivo","Un elemento constructivo es cada uno de los componentes materiales que integran una obra de construcción. Se suelen clasificar en estructurales y compartimentadores."));
        
            }
            List<Elemento> obtener() {
        
                return elementos;
          
            }
        
            void insertar(Elemento elemento, Callback callback){
        
                elementos.add(elemento);
                callback.cuandoFinalice(elementos);
        
            }
        
            void eliminar(Elemento elemento, Callback callback) {
        
                elementos.remove(elemento);
                callback.cuandoFinalice(elementos);
        
            }
        
            void actualizar(Elemento elemento, float valoracion, Callback callback) {
        
                elemento.valoracion = valoracion;
                callback.cuandoFinalice(elementos);
        
            }
        }
    
    Por su parte, el ElementosViewModel accedía directamente al ArrayList de Elementos. 
        Ahora que ya no existe el Array debemos borrar dicha referencia.
    Elimina la siguiente línea:
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {
        
            public ElementosViewModel(@NonNull Application application) {
                // ...
        
                listaElementos.setValue(elementosRepositorio.obtener());
        
            }
            //...
        }
        
    Obtener el ElementosDao
    La clase ElementosRepositorio necesitará un objeto ElementosDao para poder ejecutar los métodos obtener(), insertar(), 
    actualizar() y eliminar(). 
    
Para obtener el ElementosDao se debe obtener la instancia de la base de datos con ElementosDatabase.obtenerInstancia(), 
    y llamar al método obtenerElementosDao().
    Para llamar al método ElementosDatabase.obtenerInstancia() se necesita el parámetro Application, así que lo añadimos al constructor
        de la clase ElementosRepositorio.
    
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
        
            ElementosBaseDeDatos.ElementosDao elementosDao;
            ElementosRepositorio(Application application){
                elementosDao = ElementosBaseDeDatos.obtenerInstancia(application).obtenerElementosDao();
            }
        
            // ...
        }
    
    Por su parte, el ElementosViewModel deberá pasar la referencia a Application cuando instancia el 
    repositorio:
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {
        
            public ElementosViewModel(@NonNull Application application) {
                // ...
        
                elementosRepositorio = new ElementosRepositorio(application);
        
            }
            //...
        }
    
    Query
    Para consultar los Elementos de la base de datos, el repositorio únicamente debe llamar al método obtener()
    del DAO. El DAO retorna el resultado de la consulta como un LiveData. Este LiveData se irá actualizando permanentemente
    según vaya cambiando el contenido de la base de datos.
    Será suficiente con que el Repositorio retorne este LiveData para que pueda ser observado por la Vista.
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
            // ...
        
        
            LiveData<List<Elemento>> obtener(){
                return elementosDao.obtener();
            }
        
        }
    
    El ViewModel transmite directamente el objeto LiveData a la Vista:
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {
            // ...
        
        
            LiveData<List<Elemento>> obtener(){
                return elementosRepositorio.obtener();
            }
        
        }
    
    Insert, Update, Delete
    Las operaciones que modifican la base de datos se deben hacer en una tarea en segundo plano. 
        Para ello creamos un objeto Executor en el repositorio:
    
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
            
        
            Executor executor = Executors.newSingleThreadExecutor();
        
            //...
        }
    
    Los métodos del Repositorio ejecutan las consultas correspondientes del DAO en segundo plano para no 
        bloquear el Thread Principal:
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
            //...
        
            void insertar(Elemento elemento){
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        elementosDao.insertar(elemento);
                    }
                });
            }
        
            void eliminar(Elemento elemento) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        elementosDao.eliminar(elemento);
                    }
                });
            }
        
            public void actualizar(Elemento elemento, float valoracion) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        elemento.valoracion = valoracion;
                        elementosDao.actualizar(elemento);
                    }
                });
            }
        
        }
    
    Debido a que estos cambios sobre los datos se reflejarán automáticamente en el LiveData de la consulta SELECT, 
        ya no es necesario el callback para retornar la lista resultante.
    
    De esta forma el ViewModel se simplifica bastante, ya que no es necesario que mantenga el LiveData actualizado, sino 
        que será el propio Room el que lo vaya actualizando.
    
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {
            //...
        
            void insertar(Elemento elemento){
                elementosRepositorio.insertar(elemento);
            }
        
            void eliminar(Elemento elemento){
                elementosRepositorio.eliminar(elemento);
            }
        
            void actualizar(Elemento elemento, float valoracion){
                elementosRepositorio.actualizar(elemento, valoracion);
            }
        
        }
    
    Por último, solamente queda limpiar el código que queda sin utilizar:
    Elimina estas líneas:
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {        
         
        
            MutableLiveData<List<Elemento>> listElementosMutableLiveData = new MutableLiveData<>();
        
        }
    
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
        
        
            interface Callback {
                void cuandoFinalice(List<Elemento> elementos);
            }
        
        }
    
    Ahora puedes ejecutar la app y los datos ya se guardan en la base de datos.
    Observa que no ha sido necesario tocar el código de la Vista (fragments) para implementar los cambios 
        en el Modelo.
    
    Más Valorados y Búsqueda
    Añadiremos un par de Pantallas a la app:
    
    Añadiremos también un BottomNavigationView para navegar a estas pantallas, y un SearchView para introducir el término.
    Pantallas y navegación
    Las pantallas RecyclerValoracionFragment y RecyclerBusquedaFrgment, són en realidad iguales que la pantalla
        que ya tenemos RecyclerElementosFragment. Lo único que cambia en ellas es la consulta SQL que se debe realizar.
    
    
    Para crearlos podríamos copiar&pegar el 
        fragment RecyclerElementosFragment y cambiar la llamada a obtener() por otras llamadas que obtengan
        los más valorados o la búsqueda. Sin embargo, podemos hacer uso de la herencia y extender el RecyclerElementosFragment. 
        Es decir, los fragments de valoración y búsqueda aprovecharán el código Java y el layout XML del 
        fragment RecyclerElementosFragment
    De esta forma, para crear los dos fragments únicamente necesitamos una clase Java que extienda de 
        RecyclerElementosFragment.
    Crea estas dos clases Java:
    
        RecyclerValoracionFragment.java
    
    
        public class RecyclerValoracionFragment extends RecyclerElementosFragment {
        }
    
    
        RecyclerBusquedaFragment.java
    
    
        public class RecyclerValoracionFragment extends RecyclerElementosFragment {
        }
    
    NavGraph
    Ahora hay que añadir estos dos fragments al nav_graph.xml. Y también modificar 
        las acciones de navegación, ya que a los fragments MostrarElemento y NuevoElemento 
        también se podrá acceder ahora desde las pantallas de valoración y búsqueda.
        
    En lugar de triplicar las flechas de navegación, usaremos acciones globales:
    
    
    El nav_graph quedará así:
    
        nav_graph.xml
    
    
        <?xml version="1.0" encoding="utf-8"?>
        <navigation 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:id="@+id/nav_graph"
            app:startDestination="@id/recyclerElementosFragment">
        
            <fragment
                android:id="@+id/recyclerElementosFragment"
                android:name="com.company.room.RecyclerElementosFragment"
                android:label="fragment_recycler_elementos"
                tools:layout="@layout/fragment_recycler_elementos" />
            <fragment
                android:id="@+id/recyclerValoracionFragment"
                android:name="com.company.room.RecyclerValoracionFragment"
                android:label="RecyclerValoracionFragment"
                tools:layout="@layout/fragment_recycler_elementos" />
            <fragment
                android:id="@+id/recyclerBuscarFragment"
                android:name="com.company.room.RecyclerBuscarFragment"
                android:label="RecyclerBuscarFragment"
                tools:layout="@layout/fragment_recycler_elementos" />
        
            <action
                android:id="@+id/action_mostrarElementoFragment"
                app:destination="@id/mostrarElementoFragment" />
            <action
                android:id="@+id/action_nuevoElementoFragment"
                app:destination="@id/nuevoElementoFragment" />
        
            <fragment
                android:id="@+id/nuevoElementoFragment"
                android:name="com.company.room.NuevoElementoFragment"
                android:label="fragment_nuevo_elementok"
                tools:layout="@layout/fragment_nuevo_elemento" />
            <fragment
                android:id="@+id/mostrarElementoFragment"
                android:name="com.company.room.MostrarElementoFragment"
                android:label="fragment_mostrar_elemento"
                tools:layout="@layout/fragment_mostrar_elemento" />
        </navigation>
    
    También hay que cambiar las referencias a las acciones de navegación que tenemos puestas en el código Java:
    
        RecyclerElementosFragment.java
    
    
        
        navController.navigate(R.id.action_recyclerElementosFragment_to_nuevoElementoFragment);
        
        
        navController.navigate(R.id.action_nuevoElementoFragment);
        
        // ...
        
        navController.navigate(R.id.action_recyclerElementosFragment_to_mostrarElementoFragment);
        
        
        navController.navigate(R.id.action_mostrarElementoFragment);
        
    
    MainActivity
    Navegaremos a estas pantallas usando un BottomNavigationView, que añadimos en la MainActivity. 
    También añadimos el SearchView que permitirá introducir el término de búsqueda:
    
        activity_main.xml
    
    
        <?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"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        
            <androidx.appcompat.widget.SearchView
                android:id="@+id/searchView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toTopOf="@id/fragment"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"/>
        
            <androidx.fragment.app.FragmentContainerView
                android:id="@+id/fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                app:defaultNavHost="true"
                app:navGraph="@navigation/nav_graph"
                app:layout_constraintTop_toBottomOf="@id/searchView"
                app:layout_constraintBottom_toTopOf="@id/bottom_nav_view"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"/>
        
            <com.google.android.material.bottomnavigation.BottomNavigationView
                android:id="@+id/bottom_nav_view"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                app:menu="@menu/bottom_menu"
                app:labelVisibilityMode="unlabeled"
                app:layout_constraintTop_toBottomOf="@id/fragment"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"/>
        
        </androidx.constraintlayout.widget.ConstraintLayout>
    
    Creamos el fichero de menú bottom_menu.xml, con los ítems para navegar a las pantallas:
    
        res/menu/bottom_menu.xml
    
    
        <?xml version="1.0" encoding="utf-8"?>
        <menu xmlns:android="http://schemas.android.com/apk/res/android">
            <item
                android:id="@+id/recyclerElementosFragment"
                android:icon="@android:drawable/ic_input_get"
                android:title="Elementos" />
        
            <item
                android:id="@+id/recyclerValoracionFragment"
                android:icon="@android:drawable/btn_star_big_on"
                android:title="Valoración" />
        
            <item
                android:id="@+id/recyclerBuscarFragment"
                android:icon="@android:drawable/ic_menu_search"
                android:title="Buscar" />
        </menu>
    
    También configuramos la navegación en la MainActivity:
    
        MainActivity.java
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // ...
        
            NavController navController = ((NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment)).getNavController();
            NavigationUI.setupWithNavController(binding.bottomNavView, navController);
        
        }
    
    La navegación de la app queda así:
    
    La MainActivity tiene el SearchView, el NavHost y el BottomNavigationView.
    No obstante, no en todos los fragments queremos mostrar el BottomNavigationView: cuando se navegue a los 
    fragments de Mostrar y Nuevo no queremos que se muestre. Y el SearchView únicamente lo queremos mostrar cuando se navegue a la pantalla de buscar.
    Los componentes del la MainActivity que queremos mostrar/ocultar, los podemos gestionar en el callback 
        onDestinationChanged(). Este callback es llamado por el navController cada vez que se 
        navega a un destino, y se pasa como parámetro el destino al cual se ha navegado. Aprovecharemos para 
        mostrar/ocultar el BottomNavigationView y el SearchView en las pantallas que deseamos: 
    
    
        MainActivity.java
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // ...
        
            navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
                @Override
                public void onDestinationChanged(@NonNull NavController controller,
                                                @NonNull NavDestination destination, @Nullable Bundle arguments) {
                    if (destination.getId() == R.id.nuevoElementoFragment
                            || destination.getId() == R.id.mostrarElementoFragment) {
                        binding.bottomNavView.setVisibility(View.GONE);
                    } else {
                        binding.bottomNavView.setVisibility(View.VISIBLE);
                    }
                    if (destination.getId() == R.id.recyclerBuscarFragment){
                        binding.searchView.setVisibility(View.VISIBLE);
                        binding.searchView.setIconified(false);
                        binding.searchView.requestFocusFromTouch();
                    } else {
                        binding.searchView.setVisibility(View.GONE);
                    }
                }
            });
        
        }
    
    
        La llamada a setIconified(false) hace que el SearchView se muestre expandido y se pueda introducir el texto.
        La llamada a requestFocusFromTouch() hace que el foco se ponga sobre el SearchView y automáticamente se depliegue el teclado.
    
    Ahora ya tenemos implementada la navegación a las diferentes pantallas de al app. El siguiente paso es 
        implementar la funcionalidades "más valorados" y "búsqueda".
    
    Model
    Añadiremos al ElementosDao las consultas correspondientes a "más valorados" y "búsqueda":
    
        ElementosBaseDeDatos.java
    
    
        @Dao
        interface ElementosDao {
            // ...
        
            @Query("SELECT * FROM Elemento ORDER BY valoracion DESC")
            LiveData<List<Elemento>> masValorados();
    
            @Query("SELECT * FROM Elemento WHERE nombre LIKE '%' || :t || '%'")
            LiveData<List<Elemento>> buscar(String t);
        
        }
    
    
        Para añadir un parámetro a una consulta, ha que poner el nombre del parámetro precedido de dos puntos : 
            (ver: query-params).
        
        
            El operador || es el operador de concatenación SQL.
        
    
    Estas consultas serán llamadas por el repositorio:
    
        ElementosRepositorio.java
    
    
        public class ElementosRepositorio {
            // ...
        
            LiveData<List<Elemento>> masValorados() {
                return elementosDao.masValorados();
            }
        
            LiveData<List<Elemento>> buscar(String t) {
                return elementosDao.buscar(t);
            }
        
        }
    
    ViewModel
    El ViewModel expondrá a la Vista el acceso a los métodos masValorados() y busqueda().
    El método masValorados() no tiene nada en especial, simplemente retorna el resultado de la consulta:
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {
            // ... 
        
            LiveData<List<Elemento>> masValorados(){
                return elementosRepositorio.masValorados();
            }
        
        }
    
    El método busqueda(), sin embargo, tiene que pasar como parámetro el término de búsqueda introducido en el 
    SearchView que hemos puesto en la MainActivity.
    Crearemos una variable MutableLiveData a la que llamaremos terminoBusqueda, 
        que guardará el texto introducido en el SearchView. 
        También añadiremos un método para establecer el terminoBusqueda.
    Por otra parte, usaremos Transformations para observar la variable terminoBusqueda  
    y realizar la consulta que buscará el término en la base de datos. Guardaremos el resultado en la variable resultadoBusqueda.
    También añadiremos el getter buscar() para obtener la variable resultadoBusqueda. 
    
    
        ElementosViewModel.java
    
    
        public class ElementosViewModel extends AndroidViewModel {
            // ... 
        
            MutableLiveData<String> terminoBusqueda = new MutableLiveData<>();
            LiveData<List<Elemento>> resultadoBusqueda = Transformations.switchMap(terminoBusqueda, new Function<String, LiveData<List<Elemento>>>() {
                @Override
                public LiveData<List<Elemento>> apply(String input) {
                    return elementosRepositorio.buscar(input);
                }
            });
        
            // ...
        
            LiveData<List<Elemento>> buscar(){
                return resultadoBusqueda;
            }
        
            void establecerTerminoBusqueda(String t){
                terminoBusqueda.setValue(t);
            }
        
        }
    
    View
    Establecemos un listener al SearchView en la MainActivity para detectar los cambios en el texto 
    introducido. Cuando se produzca algún cambio actualizaremos el terminoBusqueda en el ViewModel (automáticamente se
    ejecutará la Transformación y se llamará a la consulta).
    
        MainActivity.java
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // ...
        
            binding.searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) { return false; }
                @Override
                public boolean onQueryTextChange(String newText) {
                    elementosViewModel.establecerTerminoBusqueda(newText);
                    return false;
                }
            });
        
        }
    
    
    Ya solo queda hacer que los fragments Valorados y Buscar, obtengan los datos correspondientes del ViewModel.
    Para ello, lo único que hay que cambiar respecto al RecyclerElementosFragment es la llamada al método 
    obtener() del ViewModel, y sustiturirla por las llamadas a masValorados() y buscar().
    Es esta llamada la que se debe anular/sobreescribir (@Override) en los fragments Valorados y Buscar.
    Haremos un método en el RecyclerElementosFragment que realice esta llamada, y de esta forma 
        los fragments Valorados y Buscar podrán sobreescribirlo con sus llamadas correspondientes.
    Sustituye la llamada a elementosViewModel.obtener(), por una llamada al método obtenerElementos() que 
    retorne la llamada original a elementosViewModel.obtener(): 
    
        RecyclerElementosFragment.java
    
    
        public class RecyclerElementosFragment extends Fragment {
            // ...
            @Override
            public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
                // ...
        
                obtenerElementos().observe(getViewLifecycleOwner(), new Observer<List<Elemento>>() {
        
                    @Override
                    public void onChanged(List<Elemento> elementos) {
                        elementosAdapter.establecerLista(elementos);
                    }
                });
                // ...
            }
        
            LiveData<List<Elemento>> obtenerElementos(){
                return elementosViewModel.obtener();
            }
        
        }
    
    Los fragments Buscar y Valorados, solo tienen que sobreescribir/anular la llamada a obtenerElementos(), y 
    efectuar sus llamadas correspondientes:
    
        RecyclerBuscarFragment.java
    
    
        public class RecyclerBuscarFragment extends RecyclerElementosFragment {
        
            @Override
            LiveData<List<Elemento>> obtenerElementos() {
                return elementosViewModel.buscar();
            }
        
        }
    
    
        RecyclerValoracionFragment.java
    
    
        public class RecyclerValoracionFragment extends RecyclerElementosFragment {
        
            @Override
            LiveData<List<Elemento>> obtenerElementos() {
                return elementosViewModel.masValorados();
            }
        
        }        
    
    Práctica
    Modifica tu práctica anterior del RecyclerView para que almacene los datos en una base de datos.
    Además de las pantallas master/detail, añade una pantalla para introducir elementos en la base de datos.