
Trabalhar com o carregamento de página por página em aplicativos móveis é um tópico bastante simples e não possui armadilhas.
O desenvolvedor é confrontado com uma tarefa semelhante com bastante frequência e, consequentemente, repetidamente, faz a mesma coisa é chato e preguiçoso.
Mas qualquer tarefa até a mais comum pode ser resolvida de outra maneira, mais interessante, para obter novos conhecimentos e apenas esticar o cérebro.
Paginação
Em resumo, todo o trabalho na implementação da paginação consiste nos itens listados abaixo:
- Adicionar ouvinte à exibição do reciclador;
- Faça o download dos dados primários;
- Capture um retorno de chamada quando o usuário rolar pela lista;
- Mostrar o download na lista depois de todos os itens;
- Envie uma solicitação para novos itens;
- Exibir dados novamente.
No nosso caso, o mecanismo de paginação da página foi implementado, mas não houve exibição da carga enquanto o usuário estava percorrendo a lista.
Ok, fazer isso uma vez em um aplicativo não é um problema. Se houver várias telas em que essa funcionalidade é necessária, você poderá gravar um adaptador básico que possa funcionar com um tipo de visualização adicional.
Mas, e se a tarefa for um pouco mais complicada?
Digamos que existem vários projetos novos que possuem um módulo principal com funcionalidade básica.
Como os projetos estão longe do primeiro ano, como resultado, muitos códigos herdados foram acumulados relacionados a essa tarefa, ou seja, muitos adaptadores com diferentes classes base.
Como posso resolver o problema?
Crie um novo adaptador básico que possa mostrar o progresso do download, reescreva todos os adaptadores antigos + telas que os utilizam, porque você precisa controlar o estado de exibição da carga em cada tela, em cada apresentador ou exibição.
Isso pode ser bastante demorado. E também a refatoração grande pode levar a uma série de novos erros, especialmente se a cobertura de teste em projetos tende a zero. Além disso, uma alta carga no departamento de controle de qualidade com teste de regressão de todas as telas com paginação para diversas aplicações.
Seria ótimo escrever um código que exija um tempo mínimo do cliente de desenvolvimento para integrar a solução básica em seu recurso. Para não ter que implementar a lógica de controle de exibir o progresso do carregamento - mostrando e ocultando.
Nesse ponto, pensei em usar a classe
ItemDecoration .
Por que não usar essa classe para encapsular todo o trabalho relacionado a
- determinar quando mostrar a carga e quando ocultá-la
- desenho download progresso
Infelizmente, nenhuma solução pronta foi encontrada na Internet - ninguém tentou implementá-la ou simplesmente não compartilhou a experiência após a implementação.
O que é necessário para implementar a exibição de carregamento durante a paginação usando o ItemDecoration?
- Recuar para renderizar a carga;
- Entenda a implementação do ItemDecoration quando precisarmos mostrar progresso;
- Desenhar download;
Indentação
Qualquer desenvolvedor que tenha criado SpacingItemDecoration pelo menos uma vez sabe como recuar. O método getDecorOffsets ItemDecoration nos ajudará com isso:
override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, parent, state) }
Para qualquer elemento da lista, podemos adicionar recuos nos dois lados. Especificamente, no nosso caso, precisamos entender que o usuário rolou a lista até o final e, no último recuo do elemento, igual ao valor do futuro progresso do download exibido + recuos do próprio progresso.
Como entender que a lista é rolada para baixo?
O código abaixo nos ajudará com isso:
private fun isLastItem(recyclerView: RecyclerView, view: View): Boolean { val lastItemPos = recyclerView.getChildAdapterPosition(view) return lastItemPos == recyclerView.adapter!!.itemCount - 1 }
No total, temos código para definir e implementar o recuo:
override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, recyclerView, state) when (isLastItem(recyclerView, view)) { true -> outRect.set(Rect(0, 0, 0, 120)) else -> outRect.set(Rect(0, 0, 0, 0)) } } private fun isLastItem(recyclerView: RecyclerView, view: View): Boolean { val lastItemPos = recyclerView.getChildAdapterPosition(view) return lastItemPos == recyclerView.adapter!!.itemCount - 1 }
Um terço do trabalho está feito!
Determinamos o tempo de exibição do progresso do download e chamamos de renderização do progresso
O método ItemDecoration onDrawOver nos ajudará com isso:
override fun onDrawOver(canvas: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) { super.onDrawOver(canvas, recyclerView, state) }
O método onDrawOver difere do método onDraw somente na ordem de renderização. As decorações do OnDrawOver serão renderizadas somente após o desenho do item da lista.
No método onDrawOver, precisamos entender em que ponto precisamos desenhar o progresso, em que ponto removê-lo e causar atualizações nos itens da lista para ocultar o recuo já recuado.
Código para implementar as ações descritas acima:
override fun onDrawOver(canvas: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) { super.onDrawOver(canvas, recyclerView, state) when (showLoading(recyclerView)) { true -> { PaginationProgressDrawer.drawSpinner(recyclerView, canvas) isProgressVisible = true } else -> { if (isProgressVisible) { isProgressVisible = false recyclerView.invalidateItemDecorations() } } } } private fun showLoading(recyclerView: RecyclerView): Boolean { val manager = recyclerView.layoutManager as LinearLayoutManager val lastVisibleItemPos = manager.findLastCompletelyVisibleItemPosition() return lastVisibleItemPos != -1 && lastVisibleItemPos >= recyclerView.adapter!!.itemCount - 1 }
Progresso do desenho
O código de renderização é bastante volumoso e será apresentado em um arquivo separado, um link ao qual apresentarei abaixo.
Quero me debruçar apenas sobre as nuances necessárias para a implementação.
Todo o trabalho de desenho é feito em tela. Portanto, será necessário configurar uma instância do objeto Paint e desenhar arcos, indicando os ângulos inicial e final.
Uma nuance importante é que, para renderizar o mais próximo possível de uma ProgressBar regular, você precisa criar seu próprio análogo de um interpolador para que a animação de renderização ocorra de maneira não linear.
Para entender o trabalho dos interpoladores e o que você deseja obter com sua animação, recomendo que você se familiarize com os gráficos que representam o trabalho de vários tipos de interpoladores, por exemplo, neste
artigoReferências de código:
PaginationLoadingDecorationPaginationProgressDrawerSe necessário, você pode criar uma interface ProgressDrawer e substituir implementações em PaginationLoadingDecoration.
Download do vídeo de demonstração:
Obrigado pela leitura, aproveite a codificação :)