Guia simples Paging3.0 do JetPack!

Autor: Chsmy

Há um artigo sobre o uso e análise de Paging2.x antes. O efeito de Paging2.x executando o deslizamento infinito é muito bom, mas o código é um pouco complicado de escrever e a função não é muito perfeita. Por exemplo, o o método de atualização pull-down não é fornecido, temos que chamar o método DataSource # invalidate () para redefinir os dados você mesmo. Recentemente, o Google lançou a versão beta 3.0, que é mais poderosa e fácil de usar. Vamos tentar agora.

Primeiro, dê uma olhada na introdução do site oficial ao Paging3.0

  • Os dados de paginação são armazenados em cache na memória para garantir que os aplicativos usem os recursos do sistema com mais eficácia ao processar os dados da página
  • Ao mesmo tempo, várias solicitações idênticas irão acionar apenas uma, garantindo que o aplicativo use recursos de rede e recursos do sistema de forma eficaz
  • Você pode configurar os adaptadores de RecyclerView para iniciar solicitações automaticamente ao deslizar para o fim
  • Bom suporte para corrotina e fluxo Kotlin, LiveData, RxJava
  • Atualização integrada, nova tentativa, tratamento de erros e outras funções

Para começar a usar, primeiro introduza bibliotecas dependentes

def paging_version = "3.0.0-alpha02"
implementation "androidx.paging:paging-runtime:$paging_version"

Para configurar um RecyclerView, existem duas partes principais: uma é o adaptador e a outra são os dados. Comece com o adaptador

Adaptador de construção

A criação de Adapter é semelhante a Paging2.x, mas a classe herdada é diferente. Paging2.x herda PagedListAdapter. No 3.0, PagedListAdapter não está mais disponível e precisa herdar PagingDataAdapter.

class ArticleAdapter : PagingDataAdapter<Article,ArticleViewHolder>(POST_COMPARATOR){

    companion object{
        val POST_COMPARATOR = object : DiffUtil.ItemCallback<Article>() {
            override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean =
                    oldItem == newItem

            override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean =
                    oldItem.id == newItem.id
        }
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
         holder.tvName.text = getItem(position)?.title
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        return ArticleViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item,parent,false))
    }

}
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
   val tvName: TextView = itemView.findViewById(R.id.tvname)
}

O método de escrita é basicamente o mesmo que o RecyclerView.Adapter normal, com uma coisa adicionada, um DiffUtil.ItemCallback precisa ser passado no construtor para determinar a regra de cálculo quando a diferença é atualizada.

O adaptador está concluído, a seguir estão os dados, usamos Retrofit e co-rotina Kotlin para obter os dados da rede e definir os dados para o adaptador

Obtenha dados e defina-os como Adaptador

No site oficial, o Google recomenda que eu use uma arquitetura de três camadas para completar os dados para as configurações do adaptador, como a figura a seguir no site oficial

A primeira camada do Repositório de camada de data warehouse

A camada de repositório é implementada principalmente usando o componente paging de PagingSource. Cada objeto PagingSource corresponde a uma fonte de dados e como localizar dados da fonte de dados. O PagingSource pode localizar dados de qualquer fonte de dados única, como a rede ou banco de dados.

A camada Repositório tem outro componente de paginação que pode usar RemoteMediator, que é uma fonte de dados hierárquica, como uma fonte de dados de rede com um cache de banco de dados local.

Crie nosso PagingSource e Repositório abaixo

class ArticleDataSource:PagingSource<Int,Article>() {

    /**
     * 实现这个方法来触发异步加载(例如从数据库或网络)。 这是一个suspend挂起函数,可以很方便的使用协程异步加载
     */
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {

        return try {
            val page = params.key?:0
            //获取网络数据
            val result = WanRetrofitClient.service.getHomeList(page)
            LoadResult.Page(
                    //需要加载的数据
                    data = result.data.datas,
                    //如果可以往上加载更多就设置该参数,否则不设置
                    prevKey = null,
                    //加载下一页的key 如果传null就说明到底了
                    nextKey = if(result.data.curPage==result.data.pageCount) null else page+1
            )
        }catch (e:Exception){
            LoadResult.Error(e)
        }

    }
}
  • Herdar PagingSource requer dois tipos genéricos. O primeiro representa o método de carregamento da próxima página de dados. Por exemplo, usar o número da página para carregar pode passar Int, use um determinado atributo da última parte dos dados para carregar a próxima página e passar outros tipos, como String, etc.
  • Implemente seu método de carregamento para acionar o carregamento assíncrono, você pode ver que é uma função de suspensão decorada com suspensão, que pode ser facilmente carregada com co-rotina de forma assíncrona.
  • Há um valor-chave no parâmetro LoadParams, que podemos usar para carregar a próxima página.
  • O valor de retorno é um LoadResult. Se ocorrer uma exceção, LoadResult.Error (e) é chamado e o método LoadResult.Page é chamado na abertura forçada normal para definir os dados obtidos da rede ou banco de dados.
  • prevKey e nextKey representam respectivamente o fator de carregamento que precisa ser fornecido ao carregar para cima ou para baixo na próxima vez. Por exemplo, quando carregamos os dados de cada página por meio do aumento contínuo de página, nextKey pode ser passado para a próxima página página + 1 . Se for definido como nulo, não há dados.

Criar Repositório

class ArticleRepository {

    fun getArticleData() = Pager(PagingConfig(pageSize = 20)){
        ArticleDataSource()
    }.flow

}

O código é pequeno, mas existem dois objetos importantes: Pager e PagingData

  • Pager é a entrada principal para paging, requer 4 parâmetros: PagingConfig, Key, RemoteMediator, PagingSource, o primeiro e o quarto dos quais são obrigatórios.
  • PagingConfig é usado para configurar alguns atributos durante o carregamento, como quantos itens são contados como uma página, a que distância da parte inferior para começar a carregar a próxima página, o número de itens carregados inicialmente e assim por diante.
  • PagingData é usado para armazenar os resultados de cada aquisição de dados de paginação
  • Flow é o fluxo de dados assíncrono de Kotlin, semelhante ao Observable de RxJava

Segunda camada ViewModel

O repositório finalmente retorna um pacote de fluxo assíncrono PagingDataFlow <PagingData>, PagingData armazena os resultados dos dados e, finalmente, pode usá-lo para associar os dados à interface da IU.

LiveData é geralmente usado em ViewModel para interagir com a camada de IU, e a função de extensão de Flow pode ser convertida diretamente em um objeto observável LiveData.

class PagingViewModel:ViewModel() {

    private val repository:ArticleRepository by lazy { ArticleRepository() }
    /**
     * Pager 分页入口 每个PagingData代表一页数据 最后调用asLiveData将结果转化为一个可监听的LiveData
     */
    fun getArticleData() = repository.getArticleData().asLiveData()

}

Camadas da interface do usuário

A camada de IU está na verdade em nossa atividade, defina o adaptador para RecycleView, defina os dados para Adater

class PagingActivity : AppCompatActivity() {

    private val viewModel by lazy { ViewModelProvider(this).get(PagingViewModel::class.java) }

    private val adapter: ArticleAdapter by lazy { ArticleAdapter() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_paging)

        val refreshView:SmartRefreshLayout = findViewById(R.id.refreshView)
        val recyclerView :RecyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = adapter.withLoadStateFooter(PostsLoadStateAdapter(adapter))
        //获取数据并渲染UI
        viewModel.getArticleData().observe(this, Observer {
            lifecycleScope.launchWhenCreated {
                adapter.submitData(it)
            }
        })
        //监听刷新状态当刷新完成之后关闭刷新
        lifecycleScope.launchWhenCreated {
            @OptIn(ExperimentalCoroutinesApi::class)
            adapter.loadStateFlow.collectLatest {
                if(it.refresh !is LoadState.Loading){
                    refreshView.finishRefresh()
                }
            }
        }
        refreshView.setOnRefreshListener {
            adapter.refresh()
        }
    }
}
  • Crie uma instância do adaptador escrito anteriormente e defina-a como RecyclerView
  • Chame o método viewModel.getArticleData () para obter LiveData e monitorar os dados retornados
  • Chame o método submitData do adaptador para acionar a renderização da página. Este método é um método de suspensão modificado de suspensão, portanto, é chamado em uma co-rotina com um ciclo de vida. Se você não quiser colocá-lo na co-rotina, pode chamar outro método de dois parâmetros adapter.submitData (lifecycle, it) e passá-lo para lifecycle.

Atualizar e tentar novamente

O método de chamar a atualização no Paging3.0 é muito mais conveniente do que no Paging2.x. Ele fornece diretamente um método de atualização e também fornece um método de nova tentativa após o carregamento de erro de dados.

No código de atividade anterior, o método adapter.refresh () pode ser chamado diretamente no monitor suspenso do controle de atualização suspenso para concluir a atualização. Quando fechar a animação de atualização, você precisa chamar o adaptador.loadStateFlow Método .collectLatest para monitorar

 lifecycleScope.launchWhenCreated {
            @OptIn(ExperimentalCoroutinesApi::class)
            adapter.loadStateFlow.collectLatest {
                if(it.refresh !is LoadState.Loading){
                    refreshView.finishRefresh()
                }
            }
        }

O estado do fluxo de coleção, se não for o estado de carregamento, o carregamento está concluído, você pode fechar a animação.

PagingDataAdapter pode definir o progresso de carregamento da cabeça e da parte inferior ou o layout quando ocorrer um erro de carregamento, de modo que quando estiver no estado de carregamento, a animação de carregamento possa ser exibida e o botão de nova tentativa possa ser exibido quando o erro de carregamento ocorrer. Também é simples e confortável de usar.

Você precisa personalizar um adaptador para herdar de LoadStateAdapter e definir esse adaptador como o adaptador inicial.

class PostsLoadStateAdapter(
        private val adapter: ArticleAdapter
) : LoadStateAdapter<NetworkStateItemViewHolder>() {
    override fun onBindViewHolder(holder: NetworkStateItemViewHolder, loadState: LoadState) {
        holder.bindTo(loadState)
    }

    override fun onCreateViewHolder(
            parent: ViewGroup,
            loadState: LoadState
    ): NetworkStateItemViewHolder {
        return NetworkStateItemViewHolder(parent) { adapter.retry() }
    }
}

ViewHolder

class NetworkStateItemViewHolder(
    parent: ViewGroup,
    private val retryCallback: () -> Unit
) : RecyclerView.ViewHolder(
    LayoutInflater.from(parent.context).inflate(R.layout.network_state_item, parent, false)
) {
    private val progressBar = itemView.findViewById<ProgressBar>(R.id.progress_bar)
    private val errorMsg = itemView.findViewById<TextView>(R.id.error_msg)
    private val retry = itemView.findViewById<Button>(R.id.retry_button)
        .also {
            it.setOnClickListener { retryCallback() }
        }

    fun bindTo(loadState: LoadState) {
        progressBar.isVisible = loadState is Loading
        retry.isVisible = loadState is Error
        errorMsg.isVisible = !(loadState as? Error)?.error?.message.isNullOrBlank()
        errorMsg.text = (loadState as? Error)?.error?.message
    }
}

Existem três tipos de LoadState: NotLoading, Loading, Error, podemos alterar o estilo de layout na parte inferior ou superior de acordo com diferentes estados

Finalmente definido na atividade, adicione um layout inferior abaixo

recyclerView.adapter = adapter.withLoadStateFooter(PostsLoadStateAdapter(adapter))

Basta chamar os métodos relevantes do adaptador diretamente, existem três métodos no total, adicionar o fundo, adicionar o cabeçalho e adicionar ambos.

O simples uso do Paging3.0 obteve os seguintes efeitos:

Se você estiver mais interessado em novas tecnologias, preste atenção a este número, e publicaremos artigos práticos mais detalhados posteriormente.

O máximo. O máximo. O máximo. Finalmente, aqui também compartilho o PDF de aprendizado do Android que coletei e organizei. Há uma explicação detalhada do Jetpack. Espero que possa ajudá-lo a aprender a melhorar e economizar tempo na busca de materiais na Internet. Você pode compartilhar com os amigos ao seu redor para aprenderem juntos

Se precisar, você pode  obtê-lo aqui

Acho que você gosta

Origin blog.csdn.net/River_ly/article/details/107073014
Recomendado
Clasificación