Paginación en Jetpack Compose con y sin Paginación 3

Contenidos

Tabla de contenido

Usamos Retrofit & Hilt en este artículo para que comprenda mejor cómo funcionan. Además, usamos esta API para realizar pruebas. Te recomiendo registrarte y tu API key.def paging_version = «3.1.1»implementation «androidx.paging:paging-runtime:$paging_version»implementation «androidx.paging:paging-compose:1.0.0-alpha17″/ /Other dependenciesdef retrofit_version = «2.9.0» implementación «com.squareup.retrofit2:retrofit:$retrofit_version» implementación «com.squareup.retrofit2:converter-gson:$retrofit_version»def hilt_version = «2.44» implementación «com.google. dagger: hilt-android:$hilt_version»kapt «com.google.dagger:hilt-compiler:$hilt_version»implementation «androidx.hilt:hilt-navigation-compose:1.0.0» No olvide configurar el permiso de Internet en AndroidManifest.xml agregarAntes de configurar la actualización, veamos la respuesta del punto final que vamos a usar. Endpoint, https://newsapi.org/v2/everything?q=apple&sortBy=popularity&apiKey=APIKEY&pageSize=20&page=1{«status»: «ok»,»totalResults»: 65739,»articles»: [{«source»: {«id»: «wired»,»name»: «Wired»},»author»: «Parker Hall»,»title»: «Apple Music Sing Adds ‘Karaoke Mode’ to Streaming Songs»,»description»: «America’s most popular music streaming service is adding the ability to turn down the vocals and sing along.»,»url»: «https://www.wired.com/story/apple-music-sing/»,»urlToImage»: «https://media.wired.com/photos/638f959b54aee410695ffa12/191:100/w_1280,c_limit/Apple-Music-Sing-Featured-Gear.jpg»,»publishedAt»: «2022-12-06T20:51:11Z»,»content»: «When it comes to advanced technical features and seamless compatibility with iOS devices, Apple Music has Spotify well and truly beaten. The Swedish streaming giant has essentially the same content l… [+3348 chars]»},]}Modelos de respuesta, colóquelos en archivos separados. Los coloco en un bloque de código para facilitar la lectura. data class NewsResponse(val artículos: Lista

,val estado: Cadena,val totalResultados: Int)Fuente de la clase de datos(ID válido: Cadena,nombre del valor: Cadena)Artículo de la clase de datos(autor del valor: Cadena,contenido del valor: Cadena,descripción del valor: Cadena,valor publicadoEn: Cadena,valor source: Source,Value Title: String ,val url: String,val urlToImage: String) Ahora vamos a crear un servicio API y un repositorio, será una interfaz simple NewsApiService {@GET(«everything?q=apple&sortBy=popularity&apiKey =${ Constants.API_KEY}&pageSize= 20») Suspender diversión getNews(@Query(«page») page: Int): NewsResponse}Eso es todo. Ahora podemos comenzar a implementar la paginación.

fuente de paginación

Comencemos por crear el origen de paginación, clase NewsPagingSource(private val newsApiService: NewsApiService,): PagingSource() {anular diversión getRefreshKey (estado: PagingState): ¿En t? {return state.anchorPosition?.let { AnchorPosition ->state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)}}override suspend fun load (parámetros: LoadParams): CargarResultado {return try {val page = params.key?: 1val respuesta = newsApiService.getNews(page = page)LoadResult.Page(data = response.articles,prevKey = if (page == 1) null else page.minus(1) ,nextKey = if (response.articles.isEmpty()) null else page.plus(1),)} catch (e: Exception) {LoadResult.Error( e)}}}

El componente principal de la biblioteca de paginación en la capa del repositorio es PagingSource. Cada objeto PagingSource define una fuente de datos y cómo se recuperan los datos de esa fuente. Un objeto PagingSource puede cargar datos de cualquier fuente, incluidas fuentes de red y bases de datos locales.

En nuestro ejemplo, PagingSource , int es el tipo de clave de paginación, en nuestro caso son los números de índice de las páginas. El artículo es el tipo de datos cargados. PagingSource Debido a la invalidación de este PagingSource.load, la biblioteca de paginación llama a la función para obtener de forma asíncrona más datos que se mostrarán a medida que el usuario se desplaza. Eso es todo para PagingSource, podemos crear un repositorio y ver un modelo.

Repositorio y modelo de vista

No es realmente necesario tener un repositorio ya que PagingSource se comporta como tal. Puede eliminar el repositorio y hacer las mismas llamadas a funciones en view model.class NewsRepository @Inject constructor(private val newsApiService: NewsApiService) { fun getNews() = Pager(config = PagingConfig (pageSize = 20,),pagingSourceFactory = {NewsPagingSource( newsApiService) }).flow}@HiltViewModelclass NewsViewModel @Inject constructor(private val repository: NewsRepository,): ViewModel() {fun getBreakingNews(): Flow

> = repositorio.getNews().cachedIn(viewModelScope)}

El componente Pager proporciona una API pública para crear instancias de PagingData que se exponen en transmisiones reactivas basadas en un objeto PagingSource y un objeto de configuración PagingConfig.

PagingConfig, esta clase establece opciones para cargar contenido desde un PagingSource, p. B. qué tan lejos cargar, requisito de tamaño para la carga inicial y otros. El único parámetro obligatorio que debe definir es el tamaño de página, pagingSourceFactory, una función que define cómo se crea PagingSource. Eso es. Ahora podemos implementar la interfaz de usuario y ver los resultados.

capa de interfaz de usuario

collectAsLazyPagingItems, recopila valores de este flujo de PagingData y los representa en una instancia de LazyPagingItems.La instancia de LazyPagingItems puede ser utilizada por los elementos de LazyListScope y los métodos itemsIndexed para mostrar los datos obtenidos de un flujo de PagingData.

Primero creamos LazyColumn y dentro de ella usamos elementos llamados LazyPagingItems esperar y establecer un valor único para la clave. Eso es. No tenemos que hacer nada ya que estamos recuperando y paginando datos que se insertarán en LazyColumn. Dado que también debemos especificar cuándo se recuperarán nuestros datos, debemos mostrar a los usuarios la interfaz de usuario de carga. LazyPagingItems viene al rescate. LazyPagingItems tiene un objeto loadState que es CombinedLoadStates.CombinedLoadStates.source es un tipo LoadStates con campos para tres tipos diferentes de LoadState:

  • LoadStates.append: para el estado de carga de los elementos recuperados por la posición actual del usuario.
  • LoadStates.prepend: para el estado de carga de los elementos recuperados antes de la posición actual del usuario.
  • LoadStates.refresh: Para el LoadState de la carga inicial.

Cada LoadState en sí mismo puede ser uno de los siguientes:

  • LoadState.Loading: los elementos se cargan.
  • LoadState.NotLoading: los elementos no se cargan.
  • LoadState.Error: se produjo un error durante la carga.

Para la carga inicial, comprobamos items.loadState.refresh y, si el estado es LoadState.Loading, mostramos la interfaz de usuario de carga. El código completo se encuentra al final del artículo. Eso es. Veamos el resultado.Page 3 Paginación Antes de comenzar, es posible que se pregunte por qué estamos reinventando la rueda. Porque en algunos casos, la paginación 3 puede introducir código repetitivo y agregar complejidad. Implementar la paginación sin la paginación 3 puede brindarnos más libertad y menos código repetitivo. Como ya implementamos ApiService, podemos comenzar a crear el repositorio.

repositorio

class NewsManuelPagingRepository @Inject constructor(private val newsApiService: NewsApiService) {Suspender getNews(página: Int): Flujo = flujo {intentar {emit(newsApiService.getNews(page))} catch (error: Exception) {emit( NewsResponse(emptyList(), error.message ?: «», 0))}}.flowOn(Dispatchers. IO) }Esto es muy simple y mal ejecutado para nuestro ejemplo, y no recomiendo que lo use en producción de esta manera. Consulte estos artículos para obtener más información.

ver modelo

Antes de crear un modelo de vista, creemos una clase de enumeración para List State.enum class ListState {IDLE, LOADING, PAGINATING, ERROR, PAGINATION_EXHAUST,} Esta clase de enumeración nos ayudará a administrar el estado. Ahora podemos crear un modelo de vista. Primero tenemos 3 variables, Página es para mantener el número de página. canPaginate debería verificar si podemos continuar con la paginación o si hay un error. listState es la variable de estado para la interfaz de usuario. Dentro de init hacemos la primera solicitud, buscamos la primera página cuando se crea el objeto del modelo de vista. La lógica de la función getNews se puede modificar según los puntos finales y los requisitos. En este ejemplo, configuramos listState en Loading o Paginating según el número de página y hacemos la llamada al punto final. Dado que el punto final devuelve el estado «ok» para una solicitud exitosa, verifiquemos si fue exitosa o no. Si tiene éxito, agregamos nuevos elementos a la lista y establecemos los valores canPaginate y listState. Eso es. La lógica es muy simple y está abierta a mejoras. Puede probarlo usted mismo y cambiar en consecuencia. Finalmente, veamos la interfaz de usuario.

capa de interfaz de usuario

Esto va a tomar un poco más de tiempo, así que hagámoslo pieza por pieza. (lazyColumnListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: -9) >= (lazyColumnListState.layoutInfo.totalItemsCount – 6)}}val article = viewModel.newsListlazyColumnListState es necesario para configurar la información del elemento visible para Lazy Column.shouldStartPaginate es determinar si debemos iniciar la paginación o no. Usaremos StateOff derivado para un mejor rendimiento. Puedes leer más desde este enlace. Primero verificamos si podemos paginar o no, viewModel.canPaginate. Luego obtenemos el índice del último elemento visible, lazyColumnListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index, y verificamos si el número de índice es mayor o igual que el número total de elementos, lazyColumnListState.layoutInfo.totalItemsCount, menos uno usted establece Número. Decidí configurarlo en 6 para nuestro caso. Puede cambiarlo de acuerdo con su lista y tamaño LaunchedEffect(key1 = shouldStartPaginate.value) {if (shouldStartPaginate.value && viewModel.listState == ListState.IDLE)viewModel.getNews()} realizar la solicitud. Cada vez que cambia StartPaginate.value, comenzamos la paginación y eso es todo. Ahora podemos crear Lazy Column. Establecer state = lazyColumnListState es muy importante para escuchar la paginación, ¡no lo olvides! Creo que la única parte que necesita un poco de explicación es when(viewModel.listState) y es muy simple. Usando la clase Enum que creamos anteriormente, verificaremos el estado de la lista y mostraremos la interfaz de usuario necesaria. Puede encontrar el código completo al final del artículo. Eso es. Veamos los resultados.Paginación sin paginación 3

Código completo

Paginación MrNtlu/JetpackCompose (github.com)

Fuentes:

Deja una respuesta

Tu dirección de correo electrónico no será publicada.