Android RecyclerView: Qué es, aprenda con ejemplos sencillos
¿Qué es RecyclerView en Android?
El RecicladorVer Es un widget que es una versión más flexible y avanzada de GridView y ListView. Es un contenedor para mostrar grandes conjuntos de datos que se pueden desplazar de manera eficiente manteniendo un número limitado de vistas. Puede utilizar el widget RecyclerView cuando tenga colecciones de datos cuyos elementos cambien en tiempo de ejecución y dependan de un evento de red o de la acción del usuario.
Vistas
El Android La plataforma utiliza las clases View y ViewGroup para dibujar elementos en la pantalla. Estas clases son absolutas.tracLas clases `TextView` y `EditText` se extienden a diferentes implementaciones para adaptarse a un caso de uso específico. Por ejemplo, `TextView` tiene el simple propósito de mostrar contenido de texto en la pantalla. `EditText` hereda de la misma clase `View` y agrega más funcionalidad para permitir al usuario ingresar datos.
Es posible crear nuestras propias vistas personalizadas para poder lograr una mayor flexibilidad al desarrollar.ping interfaces de usuario. La clase View proporciona métodos que podemos sobrescribir para dibujar en la pantalla y una forma de pasar parámetros como ancho, alto y nuestros propios atributos personalizados que queramos agregar a nuestra View para que se comporte como deseamos.
Ver grupos
La clase ViewGroup es un tipo de Vista pero, a diferencia de la clase Vista simple cuya responsabilidad es simplemente mostrar, ViewGroup nos brinda la capacidad de colocar múltiples vistas en una sola, a la que podemos hacer referencia como un todo. En este caso, la Vista que se crea en el nivel superior a la que agregamos otras vistas simples (también podemos agregar grupos de vistas) se llama "principal" y las vistas que se agregan dentro son "secundarias".
Podemos visualizar una vista como una matriz y un grupo de vistas como una matriz de matrices. Dado que un Array of Arrays es un Array en sí mismo, podemos ver cómo un ViewGroup puede tratarse como una Vista.
var arr1 = [1,2,3] //imagine a simple View as an Array //we can imagine this as a NumberTextView which doesn't really exist //but we could imagine there's one that makes it easy to use numbers var arr2 = ["a","b","c"] // We can imagine this as another simple view var nestedArr = [arr1,arr2] //in our anology, we can now group views //together and the structure that would hold that would be what we call the ViewGroup
ViewGroup también nos permite definir cómo se organizan los elementos secundarios dentro de la vista, por ejemplo, se colocan vertical u horizontalmente. Podemos tener diferentes reglas para la interacción dentro de la vista. Por ejemplo, las TextViews que se encuentran una detrás de otra deben tener una distancia de 12dp, mientras que las ImageViews seguidas de TextView deben tener una distancia de 5dp.
Este sería el caso si estuviéramos desarrollandoping nuestro propio ViewGroup desde cero. Para facilitar estas configuraciones, Android proporciona una clase llamada LayoutParams, que podemos usar para ingresar estas configuraciones.
Android documentación proporciona algunos parámetros predeterminados que implementaríamos al configurar nuestro propio ViewGroup. Algunos parámetros comunes son los que pertenecen al ancho, alto y margen. De forma predeterminada, estas configuraciones tienen la estructura android:layout_height para la altura, por ejemplo, android:layout_width. En este sentido, cuando crea su ViewGroup, puede crear LayoutParams específicos de la forma en que desea que se comporte su ViewGroup.
Android viene con Vistas y Grupos de Vistas predeterminados que podemos usar para realizar muchas de las tareas comunes que necesitamos. Un ejemplo que hemos mencionado es TextView. Es una vista simple que viene con aspectos configurables como alto, ancho, tamaño del texto y similares. Contamos con un ImageView para mostrar Imágenes y EditText como habíamos mencionado, entre muchos otros. Android También tiene grupos de vistas personalizados a los que podemos agregar nuestras vistas y obtener el comportamiento esperado.
diseño lineal
LinearLayout nos permite agregar elementos de visualización en él. LinearLayout es un atributo de orientación que dicta cómo se presentará en la pantalla. También tiene LinearLayout.LayoutParams que dictan las reglas para las vistas internas, por ejemplo, el atributo android:center_horizontal centraría las vistas a lo largo del eje horizontal mientras que `android:center_vertical centraría el contenido de la vista a lo largo del eje vertical.
Aquí hay algunas imágenes para comprender cómo centrar. Consideraríamos que esto es un TextView simple dentro de un espacio de 200 px por 200 px, los atributos de centrado harían que se comportara de la siguiente manera.
android:centro_horizontal

Android:center_vertical

android:centro

Componentes principales de RecyclerView

Los siguientes son los componentes importantes de RecyclerView:
RecyclerView.Adaptador
Trabajo preliminar n.º 1: patrón de adaptador
Un adaptador es un dispositivo que transforma los atributos de un sistema o dispositivo en los de un dispositivo o sistema que de otro modo sería incompatible. Algunos de ellos modifican los atributos de señal o potencia, mientras que otros simplemente adaptan la forma física de un conector a otro.
Un ejemplo sencillo que encontramos en la vida real para explicar un Adaptador es cuando necesitamos conectar dispositivos entre sí, pero tienen puertos de conexión que no coinciden entre sí. Este podría ser el caso cuando visitas un país diferente donde utilizan diferentes tipos de enchufes. Si lleva consigo el cargador de su teléfono o computadora portátil, será imposible conectarlo a las tomas de corriente. Sin embargo, no se rendirá, simplemente obtendrá un adaptador que se interpondrá entre la toma de corriente y el cargador y permitirá que se realice la carga.
Este es el caso en programación cuando queremos conectar dos estructuras de datos para realizar la tarea, pero sus puertos predeterminados no tienen una forma de comunicarse entre sí.
Usaremos el ejemplo simple de un dispositivo y un cargador. Tendremos dos instancias de cargadores. Uno americano y otro británico.
class AmericanCharger() {
var chargingPower = 10
}
class BritishCharger(){
var charginPower = 5
}
Luego crearemos dos dispositivos.
class AmericanDevice() class BritishDevice()
Como ejemplo, podemos crear algunas instancias de los dispositivos para seguir el juego.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Luego, presentaremos el concepto de carga para ambos dispositivos agregando un método en los dispositivos llamado charge().
El método toma como entrada su cargador respectivo y realiza la carga en base a él.
sealed trait Device
class AmericanDevice : Device{
fun charge(charger:AmericanCharger){
//Do some American charging
}
}
class BritishDevice: Device{
fun charge(charger:BritishCharger){
//Do some British charging
}
}
En este caso, según nuestra analogía, por una razón u otra necesitaríamos utilizar un cargador británico cuando utilizamos un dispositivo americano o viceversa.
En el mundo de la programación, esto suele ocurrir cuando se mezclan bibliotecas que ofrecen la misma funcionalidad (en nuestro contexto, nuestra funcionalidad compartida es cobrar). Tendríamos que encontrar una manera de permitir esto.
Si seguimos con la analogía, tendremos que ir a una tienda de electrónica y comprar un adaptador que nos permita cargar dispositivos estadounidenses en lugar de cargadores británicos. Desde el punto de vista de la programación, seremos nosotros los que fabricaremos el adaptador.
Haremos un Adaptador para uno que coincida con el patrón exacto que necesitaríamos para crear el otro. Lo implementaremos como una clase de la siguiente manera. No necesariamente tiene que ser una clase y podría ser una función que resalte lo que generalmente hace el patrón del adaptador. Usaremos una clase que coincida con la mayoría del uso en Android.
class AmericanToBritishChargerAdapter(theAmericanCharger:AmericanCharger){
fun returnNewCharger(): BritishCharger{
//convert the American charger to a BritishCharger
//we would change the American charging functionality
//to British charging functionality to make sure the
//adapter doesn't destroy the device. The adapter could
//, for example, control the power output by dividing by 2
//our adapter could encompass this functionality in here
var charingPower:Int = charger.chargingPower / 2
var newBritishCharger = new BritishCharger()
newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2
return newBritishCharger
}
}
En el mundo de la programación, la diferencia en los enchufes es análoga a la diferencia en los métodos internos utilizados para cargar. El hecho de que los cargadores tengan diferentes métodos haría imposible su uso.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Intentar llamar al método charge() en myBritishDevice con americanChargerIFound no funcionaría ya que AmericanDevice solo acepta un AmericanCharger
entonces es imposible hacer esto
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
En este escenario, el adaptador que creamos.
AmericanToBritishChargerAdapter ahora puede resultar útil. Podemos usar el método returnNewCharger() para crear un nuevo BritishCharger, que podemos usar para cargar. Todo lo que necesitamos es crear una instancia de nuestro adaptador y alimentarlo con el AmericanCharger que tenemos, y creará un BritishCharger que podemos usar.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() //We create the adapter and feed it the americanCharger var myAdapter = AmericanToBritishChargerAdapter(theAmericanCharger) //calling returnNewCharger from myAdapter would return a BritishCharger var britishChargerFromAdapter = myAdapter.returnNewCharger() //and once we have the britishCharger we can now use it myBritishDevice.charge(britishChargerFromAdapter)
RecicladorView.LayoutManager
Cuando se trata de un grupo de vistas, tendríamos vistas colocadas dentro de él. Es LayoutManager tendría la tarea de describir cómo se distribuyen las Vistas en su interior.
Para fines de comparación, cuando trabajamos con Linearlayout ViewGroup, el caso de uso que queremos es la capacidad de colocar los elementos vertical u horizontalmente. Esto se implementa fácilmente agregando un atributo de orientación, que nos indica cómo se colocará el diseño lineal en la pantalla. Podemos hacer esto usando android:orientation=VERTICAL|HORIZONTAL atributo.
También tenemos otro grupo de vistas llamado GridLayout, su caso de uso es cuando queremos colocar vistas en una estructura de cuadrícula rectangular. Esto podría deberse a motivos como hacer que los datos que presentamos al usuario de la aplicación sean fáciles de consumir. Por diseño, GridLayout permite configuraciones que le ayudarán a alcanzar este objetivo al tener configuraciones en las que podemos definir las dimensiones de la cuadrícula, por ejemplo, podemos tener una cuadrícula de 4 × 4 o una cuadrícula de 3 x 2.
RecicladorView.ViewHolder
El ViewHolder es un abstracclase que también extendemos de RecyclerView. El ViewHolder nos proporciona métodos comunes para ayudarnos a hacer referencia a una Vista que hemos colocado en el RecyclerView incluso después de que el mecanismo de reciclaje en el RecyclerView haya cambiado varias referencias que desconocemos.
Listas grandes
Los RecyclerViews se utilizan cuando queremos presentar un conjunto realmente grande de Vistas al usuario, sin agotar nuestro RAM en nuestro dispositivo para todas y cada una de las instancias de la Vista creada.
Si tomáramos el caso de una lista de contactos, tendríamos una idea general de cómo se vería un contacto en la lista. Lo que haríamos entonces sería crear un diseño de plantilla (que en realidad es una vista) con espacios donde se rellenarán los distintos datos de nuestra lista de contactos. A continuación, se incluye un pseudocódigo que explica todo el propósito:
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
Tendríamos entonces una ContactList de esta naturaleza
<ContactList> </ContactList>
Si ese es el caso, si estuviéramos codificando el contenido, no tendríamos una forma programática de agregar contenido nuevo a la lista sin reescribir la aplicación. Afortunadamente para nosotros. Agregar una vista a un grupo de vistas es compatible con un addView(view:View) método.
Incluso si ese es el caso, no es así como RecyclerView agrega vistas de niños.
En nuestro caso de uso, tendríamos una larga lista de contactos. Para cada contacto en la lista, necesitaríamos crear OneContactView y completar los datos dentro de la Vista para que coincidan con los campos de nuestra clase Contacto. Luego, una vez que tengamos la vista, necesitaremos agregarla a RecyclerView para mostrar la lista.
data class Contact(var name:String, var address:String, var pic:String, var phoneNumber:Int)
var contact1 = Contact("Guru","Guru97", "SomePic1.jpg", 991)
var contact2 = Contact("Guru","Guru98", "SomePic2.jpg", 992)
var contact3 = Contact("Guru","Guru99", "SomePic3.jpg", 993)
var myContacts:ArrayList<Contact> = arrayListOf<Contact>(contact1,contact2,contact3)
Tenemos una variedad de contactos llamada OneContactView. Contiene espacios para tomar contenidos de la clase Contacto y mostrarlos. En RecyclerView tenemos que agregarle Vistas para que pueda ayudarnos con su capacidad de reciclaje.
RecyclerView realmente no nos permite agregar una vista, pero nos permite agregar un ViewHolder. Entonces, en este escenario, tenemos dos piezas que queremos conectar pero que no coinciden. Aquí es donde entra en juego nuestro Adaptador. RecyclerView nos proporciona un Adaptador muy parecido a nuestro AmericanToBritishChargerAdapter() de antes, eso nos permitió convertir nuestro cargador estadounidense, que no se podía utilizar con nuestro dispositivo británico, en algo utilizable, similar a un adaptador de corriente en la vida real.
En este escenario, el adaptador tomaría nuestra matriz de Contactos y nuestra Vista, y desde allí generaría ViewHolders que RecyclerView está dispuesto a aceptar.
RecyclerView proporciona una interfaz que podemos ampliar para crear nuestro Adaptador a través de la clase RecyclerView.Adapter. Dentro de este Adaptador hay una forma de crear la clase ViewHolder con la que RecyclerView quiere trabajar. Entonces, lo que tenemos es la misma situación que antes, pero con una cosa extra, ese es el Adaptador.
Tenemos una variedad de Contactos, una vista para mostrar un contacto OneContactView. Un RecyclerView es una lista de Vistas que brindan servicios de reciclaje pero que solo están dispuestas a aceptar ViewHolders.
Pero en este escenario, ahora tenemos la clase RecyclerView.Adapter, que tiene un método para crear ViewHolders en su interior.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
El RecyclerView.ViewHolder es un abstracclase que toma nuestra Vista como argumento y la convierte en un ViewHolder.
Utiliza el patrón contenedor que se utiliza para ampliar las capacidades de las clases.
Trabajo preliminar n.° 2: patrón de envoltura
Usaremos un ejemplo sencillo para demostrar cómo podemos hacer que los animales hablen.
sealed trait Animal{
fun sound():String
}
data class Cat(name:String):Animal{
fun sound(){
"Meow"
}
}
data class Dog(name:String):Animal{
fun sound(){
"Woof"
}
}
var cat1 = Cat("Tubby")
var dog1 = Dog("Scooby")
cat1.sound() //meow
dog1.sound() //woof
En el ejemplo anterior, tenemos dos animales. Si por casualidad quisiéramos agregar un método para hacer hablar, pero el autor de la biblioteca no fue divertido, aún podríamos encontrar una manera. Lo que necesitaríamos sería un contenedor para nuestra clase Animal. Haríamos esto tomando Animal como constructor para nuestra clase.
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){
fun sound(){
myAnimal.sound()
}
speak(){
println("Hello, my name is ${myAnimal.name}")
}
}
Ahora podemos pasar una instancia de animal a SpeechPoweredAnimalByWrapper. Llamar al método sound() llamaría al método animal sound() pasado. También tenemos un método talk() adicional, que cuenta como una nueva funcionalidad que agregamos a los animales pasados. Podemos usarlo de la siguiente manera:
var cat1 = Cat("Garfield")
cat1.sound()//"meow"
cat1.speak()// doesn't work as it isn't implemented
var talkingCat = new SpeechPoweredAnimalByWrapper(cat1)
talkingCat.sound() //"meow" the sound method calls the one defined for cat1
talkingCat.speak() //"Hello, my name is Garfield"
Utilizando este patrón, podemos tomar clases y agregarles funcionalidad. Todo lo que necesitaríamos es pasar una instancia de la clase y los nuevos métodos definidos por nuestro envoltorio.ping clase.
En nuestro caso anterior, utilizamos una clase concreta. También es posible implementar lo mismo en un Abs.tracclase t. Lo que tendríamos que hacer es agregar y cambiar la clase SpeechPoweredAnimalByWrapper a abstracY listo. Cambiaremos el nombre de la clase por uno más corto para que sea más legible.
abstract class SpeechPowered(var myAnimal:Animal){
fun sound(){
myAnimal.sound()
}
speak(){
println("Hello, my name is ${myAnimal.name}")
}
}
Es lo mismo que antes, pero significaría algo diferente. En una clase normal, podemos tener una instancia de una clase de la misma manera que creamos cat1 y dog1.tracSin embargo, las clases t no están diseñadas para ser instanciadas, sino para extender otras clases. Entonces, ¿cómo usaríamos el nuevo SpeechPowered(var myAnimal:Animal)?tracclase t. Podemos usarla creando nuevas clases que la extiendan y, a su vez, obtengan su funcionalidad.
En nuestro ejemplo, crearemos una clase SpeechPoweredAnimal que extiende la clase
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby")
var speakingKitty = SpeechPoweredAnimal(cat1)
speakingKitty.speak() //"Hello, my name is Tubby"
Este es el mismo patrón utilizado en el ViewHolder. La clase RecyclerView.ViewHolder es un abstracClase que añade funcionalidad a la Vista, de forma similar a como hemos añadido el método speak a los animales. Esta funcionalidad añadida es lo que permite su funcionamiento al interactuar con RecyclerView.
Así es como crearíamos un OneContactViewHolder desde OneContactView
//The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView)
RecyclerView tiene un adaptador que nos permite conectar nuestra matriz de contactos a ContactsView con RecyclerView
Agregar una vista
ViewGroup no vuelve a dibujar el ViewGroup automáticamente, sino que sigue un cronograma particular. Puede ser el caso de que en su dispositivo se vuelva a dibujar cada 10 ms o 100 ms, o si elegimos un número absurdo, digamos 1 minuto, cuando agregamos una Vista a un ViewGroup, verá los cambios 1 minuto después cuando el ViewGroup se "actualice".
RecicladorView.Recycler
Trabajo preliminar #3. Almacenamiento en caché
Uno de los mejores ejemplos de dónde realizamos actualizaciones periódicamente es en el navegador. Imaginemos, por ejemplo, que el sitio que estamos visitando es estático y no envía contenido de forma dinámica, necesitaríamos seguir actualizándolo para ver los cambios.
Para este ejemplo, imaginemos que el sitio en cuestión es Twitter. Tendríamos una serie de tweets estáticos en la lista y la única forma de ver nuevos tweets sería haciendo clic en el botón Actualizar para recuperar el contenido.
Repintar toda la pantalla es obviamente algo costoso. Si tuviéramos que imaginar que ese fuera el caso, tendríamos un ancho de banda limitado con nuestro proveedor de telefonía. Y nuestra lista de tweets tenía muchas imágenes y videos, sería costoso volver a descargar todo el contenido de la página en cada actualización.
Necesitaríamos una forma de almacenar los Tweets ya cargados y asegurarnos de que nuestra próxima solicitud tenga la capacidad de decir los Tweets que ya tiene. Por lo tanto, no vuelve a descargar todo y solo obtiene los nuevos Tweets que tiene y también verifica si algún Tweet que se había guardado localmente ya no está allí para poder eliminarlo localmente. Lo que estamos describiendo se llama almacenamiento en caché.
La información que enviamos al sitio web sobre los contenidos que tenemos se llama metadatos. Entonces, en sentido real, no solo estamos diciendo "queremos cargar su sitio", decimos "queremos cargar su sitio, y aquí están algunos de los contenidos que ya habíamos guardado desde la última vez que lo cargamos, utilícelos para solo envíanos lo que no está allí, por lo que no usamos mucho ancho de banda ya que no tenemos muchos recursos”.
Llamadas de diseño: la lista de tweets debe ser una locura
Un ejemplo de llamadas de diseño es scrollToPosition
Este es un ejemplo común que está presente en cosas como aplicaciones de chat. Si alguien en un hilo de chat responde a una burbuja de chat anterior, algunas aplicaciones de chat incluyen la respuesta y un enlace a la burbuja de chat, que al hacer clic lo lleva a donde estaba su mensaje original.
En el caso, llamamos a este método antes de agregar un LayoutManager a nuestro RecyclerView y antes de tener un RecyclerView.Adapter, el scrollToPosition(n:Int) simplemente se ignora.
Comunicación entre componentes de RecyclerView
Trabajo preliminar #4. Devoluciones de llamada
RecyclerView, al realizar su trabajo, tiene muchas partes móviles. Tiene que ver con el LayoutManager que nos dice cómo organizar las Vistas, ya sea Linealmente o en una Cuadrícula. Tiene que lidiar con un Adaptador que hace el trabajo de convertir nuestros elementos contactList en Views OneContactView y luego en ViewHolders OneContactViewHolder que RecyclerView está dispuesto a trabajar dentro de los métodos que nos proporciona.
La materia prima para RecyclerView son nuestras Vistas, p. OneContactView y fuente de datos.
contactList:Array<Contact>
Hemos utilizado un escenario simple como punto de partida para tener una idea de lo que RecyclerView intenta lograr.
Un caso base de cuándo tendríamos una matriz estática de 1000 contactos que queremos mostrarle al usuario es fácil de entender.
La maquinaria de RecyclerView realmente comienza a cobrar vida cuando la lista ya no es estática.
Con una lista dinámica, tenemos que pensar en lo que sucede con la Vista en la pantalla cuando agregamos un elemento a la lista o eliminamos un elemento de la lista.
RecicladorView.LayoutManager
Además de decidir cómo se van a organizar nuestras vistas, ya sea linealmente o en una cuadrícula, el LayoutManager realiza mucho trabajo en segundo plano para ayudar a...ping El reciclador sabe cuándo reciclar.
Es responsable de mantenerping track en las vistas actualmente visibles en la pantalla y comunica esta información al mecanismo de reciclaje. A medida que el usuario se desplaza hacia abajo, el administrador de diseño se encarga de informar al sistema de reciclaje sobre las vistas que pierden el foco en la parte superior para que puedan reutilizarse en lugar de que permanezcan allí consumiendo memoria o en lugar de destruirlas y tener que crear otras nuevas.
Lo que esto significa es que el LayoutManager necesita mantener track de la posición del usuario mientras se desplaza por nuestra lista. Esto se logra mediante una lista de posiciones indexadas, es decir, el primer elemento comienza en 0 y aumenta para coincidir con el número de elementos en nuestra lista.
Si podemos ver 10 elementos en nuestra lista de digamos 100, al principio, el LayoutManager es consciente de que tiene en foco la vista-0 hasta la Vista-9. A medida que nos desplazamos, el LayoutManager puede calcular las vistas que salen. de enfoque.
El LayoutManager puede liberar estas vistas al mecanismo de reciclaje para que puedan reutilizarse (se pueden vincular nuevos datos a ellas, por ejemplo, se pueden eliminar los datos de contacto de una vista y los nuevos datos de contacto del siguiente segmento pueden reemplazar los marcadores de posición).
Este sería un caso feliz si la lista que tenemos es estática, pero uno de los casos de uso más comunes del uso de RecyclerView es con listas dinámicas donde los datos pueden provenir de un punto final en línea o incluso tal vez de un sensor. No solo se agregan datos, sino que a veces se eliminan o actualizan datos de nuestra Lista.
El estado dinámico de nuestros datos puede hacer que sea muy difícil razonar sobre el LayoutManager. Por esta razón, LayoutManager mantiene una lista propia sobre los elementos y posiciones, que está separada de la Lista que utiliza el componente Reciclaje. Esto garantiza que haga su trabajo de diseño correctamente.
Al mismo tiempo, el LayoutManager de RecyclerView no quiere distorsionar los datos que tiene. Para funcionar correctamente, el LayoutManager se sincroniza con el RecyclerView.Adapter a intervalos determinados (60 ms) compartiendo información sobre los elementos de nuestra lista (es decir, elementos añadidos, actualizados, eliminados, movidos de una posición a otra). El LayoutManager, al recibir esta información, reorganiza el contenido de la pantalla para que coincida con los cambios cuando sea necesario.
Muchas de las operaciones principales que tratan con RecylerView giran en torno a la comunicación entre RecyclerView.LayoutManager y RecyclerView.Adapter que almacena nuestras listas de datos a veces estáticos y a veces dinámicos.
Más aún, RecyclerView nos presenta métodos que podemos usar para escuchar eventos como onBindViewHolder cuando nuestro RecyclerView.Adapter vincula contenido de nuestra Lista, p. un contacto a un ViewHolder para que ahora se acostumbre a mostrar la información en la pantalla.
Otro es onCreateViewHolder, que nos indica cuándo RecyclerView. El adaptador toma una vista normal como OneContactView y la convierte en un elemento ViewHolder con el que RecyclerView puede trabajar. Desde nuestro ViewHolder para uso de RecyclerView. onViewDetached
Aparte de los mecanismos centrales que permiten el Reciclaje. RecyclerView proporciona formas de personalizar el comportamiento sin afectar el reciclaje.
La reutilización de vistas dificulta hacer cosas comunes a las que estamos acostumbrados con vistas estáticas, como reaccionar a eventos onClick.
Como somos conscientes de que el RecyclerView.LayoutManager que presenta las Vistas al usuario podría tener por un momento una lista de elementos diferente de la RecyclerView.Adapter que tiene la lista que tenemos almacenada en una base de datos o transmitida desde una fuente. Poner eventos OnClick directamente en Vistas puede generar comportamientos inesperados, como eliminar el contacto incorrecto o cambiarlo.
Gradle
Si queremos utilizar RecyclerView, debemos agregarlo como una dependencia en nuestro archivo .gradle de compilación.
En el siguiente ejemplo, hemos utilizado la implementación “androidx.recyclerview:recyclerview:1.1.0”, que es la versión más actual según este artículo.
Después de agregar la dependencia a nuestro Gradle archivo, se nos solicitará Android Studio a Synccronizar los cambios,
Así es como nuestro Gradle El archivo se verá después de agregar un RecyclerView en un proyecto vacío con solo los valores predeterminados.
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.guru99.learnrecycler"
minSdkVersion 17
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "androidx.recyclerview:recyclerview:1.1.0"
}
Por el momento sólo tenemos un archivo de diseño. Comenzaremos con un ejemplo simple en el que usaremos RecyclerView para mostrar una lista de nombres de frutas en la pantalla.
Lista de artículos
Navegaremos a nuestro archivo MainActivity y crearemos una matriz con nombres de frutas dentro justo antes del método onCreate() que se generó durante la configuración.
package com.guru99.learnrecycler
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Nuestro próximo objetivo será presentar esta lista en la pantalla usando un RecyclerView.
Para hacer esto, navegaremos al directorio de diseño que contiene nuestros diseños y crearemos una Vista que será responsable de mostrar una fruta.
Diseño que se utilizará para cada elemento de nuestra lista.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fruitName"
/>
</TextView>
En TextView anterior, hemos agregado un campo de identificación que se usará para identificar una Vista.
No se genera por defecto. Tenemos en nuestro TextView el id fruitName para que coincida con los datos, que estarán vinculados a él.
Agregar RecyclerView al diseño principal
En la misma actividad, está el archivo de diseño main_layout.xml que se generó de forma predeterminada.
Si elegimos un proyecto vacío. Habrá generado un XML que contiene un ConstraintLayout y dentro habrá un TextView con el texto "Hola".
Eliminaremos todo el contenido y haremos que el diseño solo contenga RecyclerView como se muestra a continuación:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView 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/fruitRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
También agregamos un atributo de identificación para RecyclerView que usaremos para hacer referencia a él en nuestro código.
android:id="@+id/fruitRecyclerView"
Luego regresaremos a nuestro archivo MainActivity. Usando las identificaciones que hemos creado, podremos hacer referencia a las Vistas que acabamos de crear.
Comenzaremos haciendo referencia a RecyclerView usando el método findViewById() proporcionado por Android. Haremos esto en nuestro método onCreate().
Nuestro método onCreate() tendrá el siguiente aspecto.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}
Crear un titular de vista
A continuación, crearemos un RecyclerView.ViewHolder que es responsable de tomar nuestra Vista y convertirla en un ViewHolder, que RecyclerView usa para mostrar nuestros elementos.
Haremos esto justo después de nuestro divertido método onCreate().
package com.guru99.learnrecycler
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
}
Crear un RecyclerViewAdapter
A continuación, crearemos una clase FruitArrayAdapter que extiende la clase RecyclerView.Adapter.
El FruitArrayAdapter que creemos será responsable de hacer lo siguiente.
Tomará nombres de frutas de la variedad de frutas. Creará un ViewHolder usando nuestra vista one_fruit_view.xml. Luego vinculará la fruta a un ViewHolder y vinculará dinámicamente el contenido a la Vista que creamos one_fruit_view.xml.
package com.guru99.learnrecycler
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
class FruitArrayAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>()
}
Android Studio agregará líneas onduladas rojas a nuestro FruitArrayAdapter, lo que nos indica que necesitamos implementar un método que el RecyclerView pueda usar para conectar nuestro array a un ViewHolder que el RecyclerView pueda usar.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
}
override fun getItemCount(): Int {
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
}
}
Comenzaremos con la parte más sencilla del código generado: el método getItemCount(). Sabemos cómo obtener la cantidad de elementos en nuestra matriz llamando al método array.length.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
}
override fun getItemCount(): Int {
return fruitArray.size
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
}
}
Luego implementaremos el método override fun onCreateViewHolder.
Aquí es donde RecyclerView nos pide que le ayudemos a construir un FruitHolder.
Si recordamos, así es como se veía nuestra clase FruitViewHolder:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
Requiere nuestro fruitView que creamos como un archivo xml one_fruit_view.xml
Podemos crear una referencia a este xml y convertirlo en una Vista de la siguiente manera.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)
var fruitViewHolder = FruitViewHolder(fruitView)
return fruitViewHolder
}
override fun getItemCount(): Int {
return fruitArray.size
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
}
}
El bit restante es la anulación.
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
RecyclerView.Adapter pregunta con un número entero de posición, que usaremos para buscar un elemento de nuestra lista. También nos proporciona un soporte para que podamos vincular el elemento que obtenemos de fruitArray a la Vista que se encuentra dentro del soporte de vista.
Se puede acceder a la Vista que se encuentra dentro de un ViewHoder a través del campo ViewHolder.itemView. Una vez que obtengamos la vista, podemos usar el id fruitName que creamos anteriormente para configurar el contenido.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)
var aFruitName = fruitArray.get(position)
ourFruitTextView.setText(aFruitName)
}
Con eso nuestro FruitArrayAdapter está completo y tiene el siguiente aspecto.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)
var fruitViewHolder = FruitViewHolder(fruitView)
return fruitViewHolder
}
override fun getItemCount(): Int {
return fruitArray.size
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)
var aFruitName = fruitArray.get(position)
ourFruitTextView.setText(aFruitName)
}
}
Finalmente, estamos listos para conectar las piezas restantes de nuestro RecyclerView. Los cuales están creando un LayoutManager, que le indicará a RecyclerView cómo mostrar el contenido de la lista. Ya sea para mostrar de manera lineal usando LinearLayoutManager o en una cuadrícula usando GridLayoutManager o StaggeredGridLayoutManager.
Crear administrador de diseño
Regresaremos a nuestra función onCreate y agregaremos LayoutManager.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
var fruitLinearLayout = LinearLayoutManager(this)
myFruitRecyclerView.layoutManager =fruitLinearLayout
}
Conecte nuestro adaptador a los artículos y configúrelo en RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
var fruitLinearLayout = LinearLayoutManager(this)
myFruitRecyclerView.layoutManager =fruitLinearLayout
var fruitListAdapter = FruitListAdapter(fruitNames)
myFruitRecyclerView.adapter =fruitListAdapter
}
También creamos una instancia de fruitListAdapter y le suministramos la matriz de nombres de frutas.
Y básicamente, hemos terminado.
El archivo MainActivity.kt completo tiene el siguiente aspecto.
package com.guru99.learnrecycler
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
var fruitLinearLayout = LinearLayoutManager(this)
myFruitRecyclerView.layoutManager =fruitLinearLayout
var fruitListAdapter = FruitListAdapter(fruitNames)
myFruitRecyclerView.adapter =fruitListAdapter
}
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {
var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)
var fruitViewHolder = FruitViewHolder(fruitView)
return fruitViewHolder
}
override fun getItemCount(): Int {
return fruitArray.size
}
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)
var aFruitName = fruitArray.get(position)
ourFruitTextView.setText(aFruitName)
}
}
}
