ProgressDialog deprecated, e agora?
A partir do SDK 26 do Android, a classe ProgressDialog
ficou deprecated (obseleta), gerando dúvidas entre diversos devs que utilizam essa solução em cenários onde a execução não é imediata, como por exemplo, ao enviar requisições para uma API.
Na própria documentação, somos informados que o motivo é pelo fato do ProgressDialog
segurar o usuário fazendo com que ele não consiga interagir com a App…
“Então qual abordagem devemos considerar para lidar com esse tipo de situação?”
Além desta informação, a documentação sugere o uso do ProgressBar
para substituir o ProgressDialog
. Sendo assim, vamos implementar um exemplo no qual faremos uso do ProgressBar
.
Projeto de exemplo
Neste artigo farei uso do Ceep, um projeto que desenvolvi em alguns dos meus posts sobre Android onde compartilho no meu agregador de artigos de Kotlin:
Sendo mais específico, vou usar como base a quarta parte da série de Retrofit com Kotlin.
Contextualizando a situação
Neste projeto fizemos uma App na qual realiza a listagem, inserção e alteração de notas. Entretanto, todas as ações realizadas não possuem algum tipo de feedback no qual indique, por exemplo, que está acontecendo algum tipo de carregamento.
Sendo assim, o nosso objetivo será adicionar esse tipo de comportamento utilizando o ProgressBar
.
Adicionando o ProgressBar no layout
Como primeiro passo, precisamos adicionar o ProgressBar
no layout onde queremos que ele apareça, nesse caso é no activity_note_list
:
Repare que já foi atribuido o id note_list_progress
para que possamos buscá-lo na Activity e realizar a configuração desejada. Ao executarmos a App apenas com essa modificação, temos o seguinte resultado:
Por mais que o ProgressBar
esteja aparecendo, note que não é um comportamento esperado, pois além de começar a carregar sem um motivo específico, ele não desaparece!
Considerações durante a implementação do ProgressBar
Em outras palavras, ao declarar um ProgressBar
precisamos levar algumas considerações:
- Ele não deve aparecer sem um motivo;
- Precisamos mostrá-lo apenas se uma ação não tiver um tempo de finalização determinado;
- Após finalizar a execução do processo que pode demorar, é necessário fazer com que o
ProgressBar
desapareça.
Considerando os detalhes listados, vamos resolver o primeiro deles. Esse é bem simples, basta apenas modificar o visibility
diretamente no XML:
<ProgressBar
android:id="@+id/note_list_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
Apenas com essa modificação temos nossa App fica volta ao estado de antes:
Configurando o ProgressBar
Mas agora entramos no caso que precisamos pensar em qual momento podemos acionar o ProgressBar
, ou seja, em situações que podem demorar. Uma primeira abordagem seria no momento em que tentamos enviar uma nota para o servidor.
Entretanto, do jeito que o nosso Web Client foi implementado, não somos capazes de executar uma ação em um momento antes da requisição ser realizada.
Em outras palavras, precisamos aplicar alguma técnica para permitir esse tipo de ação! E agora?
Adicionando uma Higher-Order Function pré-execução
Uma possibilidade é adicionando mais uma HOF que vai servir apenas para executar alguma ação antes da requisição ser chamada:
Repare que executamos a HOF de pré-ação antes de chamar o insert
do Web Client. Após esse ajuste, na Activity, precisamos implementar a HOF:
Perceba que para apresentar o ProgressBar
basta apenas modificar o valor do atributo visibility
novamente. É válido observar que na chamada do add()
precisamos mandar duas HOFs:
- 1º Ação antes de realizar a requisição.
- 2º Ação ao receber a resposta de nota criada.
Executando a App e tentando adicionar uma nota, temos o seguinte resultado:
Note que o ProgressBar
aparece logo depois de tocar no botão SAVE, porém com um comportamento um tanto quanto não esperado, pois ele aparece e não some!
Isso significa que após finalizar a requisição, precisamos esconder novamente o ProgressBar
! Como podemos fazer isso?
Uma abordagem seria realizar esse procedimento diretamente na expressão lambda quando recebemos a nota como resposta:
fab_add_note.setOnClickListener {
NoteDialog( window.decorView as ViewGroup, this)
.add({
note_list_progress.visibility = ProgressBar.VISIBLE
}) {
notes.add(it)
configureList()
note_list_progress.visibility = ProgressBar.GONE
}
}
Ao testarmos novamente, ele funciona perfeitamente, entretanto, quando colocamos, por exemplo, no modo avião (que perde todas as conexões) novamente o ProgressBar
não desaparece:
Isso significa que a nossa pós execução só acontece apenas em situações que dão certo! Ou seja, independentemente da requisição falhar ou responder com sucesso, ao ser finalizada, a ação precisa ser executada!
Criando uma HOF de pós-execução do Callback
Para isso, podemos criar uma HOF que vai indicar a finalização de execução:
Veja que dentro da função insert()
, além das HOFs de sucesso e falha, agora recebemos a de finalização que é executada logo depois de ambas as HOFs. Então, na classe de Dialog, precisamos enviar a HOF para o Web Client:
Note que ao invés de executar a HOF, estamos enviando-a via parâmetro para a função do WebClient
. Na Activity, basta apenas executarmos a ação desejada:
fab_add_note.setOnClickListener {
NoteDialog(window.decorView as ViewGroup, this)
.add({
note_list_progress.visibility = ProgressBar.VISIBLE
}, {
note_list_progress.visibility = ProgressBar.GONE
}) {
notes.add(it)
configureList()
}
}
Desta maneira não temos mais a necessidade de executar a ação na HOF que devolve uma nota criada! Testando a App novamente tanto online como também offline, temos o seguinte resultado:
Veja que agora, a requisição sendo bem sucedida ou não, o ProgressBar
aparece e também some!
É importante ressaltar que pode ser que o
ProgressBar
nem apareça dependendo da velocidade da requisição. Para garantir a aparição do mesmo, eu adicionei um delay proposital de 1 segundo.É válido mencionar que já existe uma implementação de
ProgressBar
já preparada para esse tipo de comportamento que éContentLoadingProgressBar
.
Código fonte
Caso tenha alguma dúvida ou simplesmente queira consultar o código desenvolvido durante o artigo, fique à vontade em consultar o repositório do GitHub:
Para saber mais
Por mais que esse exemplo seja funcional, ainda existem diversos detalhes importantes para mantermos uma implementação mais consistente, como por exemplo:
- Implementação das HOFs: Veja que não é tão claro o que implementamos quando temos mais de uma HOF, o exemplo mais crítico acontece na chamada da função
add()
do Dialog que recebe 3 HOFs e não sabemos, logo de cara, o que cada uma delas fazem… - Obrigação de implementar as funções de pré execução e finalização: Nesse caso faz sentido para implementarmos ambas HOFs, porém, nem sempre esse tipo de implementação se faz necessário, ou seja, aplicar uma técnica na qual deixa esse tipo de implementação opcional é bem mais interessante.
- Reutilização de comportamento: Se tentarmos utilizar o comportamento de pré e pós execução nas outras ações do Web Client, somos obrigados a enviar novamente a HOF de finalização ou até mesmo implementar a de pré-execução no código que vai chamar o Web Client… Isso significa que faz muito mais sentido centralizar esses comportamentos.
Todos esses detalhes são conteúdos o suficiente para um próximo artigo! Nem precisa falar que já está em palta, né?
Um outro ponto importante é que existem diversas variações do ProgressBar
para cenários distintos, recomendo que realize uma pesquisa para identificar a que melhor lhe atende 😉
Conclusão
Neste artigo vimos como podemos implementar o ProgressBar
que é uma alternativa ao ProgressDialog
que agora é depreciado…
Considerando o nosso projeto base, vimos que existem diversas peculiaridades, porém, o conceito básico ainda é mantido.
Inicialmente o ProgressBar
fica escondido, então, ele aparece quando é necessário, mas, quando finalizar a tarefa, precisamos fazer com que ele suma novamente 😄
O que achou dessa abordagem para apresentar comportamentos de carregamento? Compartilhe comigo nos comentários!