O ViewPager é um dos componentes mais famosos e amplamente usados da Biblioteca de suporte do Android. Todos os carrosséis, onboardings e sliders mais simples são feitos nele. Em fevereiro de 2019, a equipe de desenvolvimento do AndroidX lançou o ViewPager2. Vejamos quais eram esses pré-requisitos e quais as vantagens da versão atualizada do componente.

ViewPager 2
No momento da redação da publicação (julho de 2019), uma versão beta do 
ViewPager2 estava disponível , o que significa que os problemas mencionados abaixo podem ser corrigidos e a funcionalidade aprimorada e expandida. Os desenvolvedores prometem no futuro adicionar suporte ao TabLayout (embora ele possa funcionar apenas com a primeira versão), otimizar o desempenho do adaptador, fazer muitas pequenas correções e finalizar a documentação.
Integração
O componente não é fornecido com pacotes padrão, mas é conectado separadamente. Para fazer isso, adicione a seguinte linha ao bloco de dependências no script gradle do seu módulo:
implementation "androidx.viewpager2:viewpager2:1.0.0-beta02" 
Implementação
Vamos começar com as boas notícias: a transição da primeira para a segunda versão é a mais simples possível e se resume a uma mudança nas importações. A boa e antiga sintaxe não foi tocada: o 
método getCurrentItem () retorna a página atual, 
ViewPager2.onPageChangeCallback permite que 
você assine o 
estado do pager, o adaptador ainda está instalado via 
setAdapter ().
Vale a pena ir mais fundo, pois fica claro que o primeiro e o segundo pagers não têm nada em comum, exceto as interfaces. A familiaridade com a implementação do método setAdapter () não deixa margem para dúvidas:
 public final void setAdapter(@Nullable Adapter adapter) { mRecyclerView.setAdapter(adapter); } 
Sim, o ViewPager2 é apenas um invólucro do 
RecyclerView . Por um lado, essa é uma grande vantagem, por outro - acrescenta dor de cabeça. Disfarçar o 
RecyclerView como um folheto tornou-se possível com o advento do 
PagerSnapHelper . Esta classe muda a física do scroll. Quando o usuário solta o dedo, 
PagerSnapHelper calcula qual item da lista está mais próximo da linha central da lista e, com uma animação suave, o alinha exatamente no centro. Assim, se o furto foi nítido o suficiente, a lista rola para o próximo elemento, caso contrário - com a animação retorna ao seu estado original.
 new PagerSnapHelper().attachToRecyclerView(mRecyclerView); 

Ao usar o PagerSnapHelper, verifique se a largura e a altura do próprio RecyclerView, bem como de todos os seus ViewHolders, estão definidas como MATCH_PARENT. Caso contrário, o comportamento do SnapHelper será imprevisível, podem ocorrer erros em locais completamente inesperados. Tudo isso torna a criação de um carrossel de elementos de pequena estatura bastante demorada, embora possível.
Dado todo o exposto, no layout o widget será semelhante a este:
 <androidx.viewpager2.widget.ViewPager2 android:id="@+id/main_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> 
No mesmo pacote que o 
ViewPager2, também podemos encontrar a classe 
ScrollEventAdapter , que ajuda a manter a continuidade da sintaxe. 
ScrollEventAdapter implementa 
RecyclerView.OnScrollListener e transforma eventos de rolagem 
em eventos 
OnPageChangeCallback .
 @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { if (mAdapterState != STATE_IN_PROGRESS_MANUAL_DRAG && newState == RecyclerView.SCROLL_STATE_DRAGGING) { ... dispatchStateChanged(SCROLL_STATE_DRAGGING); return; } ... } 
Agora 
OnPageChangeCallback é representado não por uma interface, mas por uma classe abstrata, que permite substituir apenas os métodos necessários (na maioria dos casos, você só precisa do 
nPageSelected (Int) , que funciona quando uma página específica é selecionada):
 main_pager.registerOnPageChangeCallback( object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) {  
Funcionalidades
Digno de nota é o método 
setPageTransformer () , que usa o 
ViewPager2.PageTransformer como parâmetro. Ele define um 
retorno de 
chamada para cada evento de seleção de página e serve para definir sua própria animação para esta página. 
O retorno de chamada recebe a 
visualização da página atual e seu número como entrada. O análogo mais próximo desse método é o 
ItemAnimator do 
RecyclerView .
Nas novas versões da biblioteca, duas implementações do transformador foram adicionadas:
CompositePageTransformer e 
MarginPageTransformer . O primeiro é responsável pela combinação de transformadores para aplicar várias transformações em um pager de uma vez e o segundo pelo recuo entre páginas:

Além disso, o novo widget suporta alterações de orientação: basta chamar o 
método setOrientation () , você pode transformar seu pager em uma lista vertical com furtos de cima para baixo:
 main_pager.setOrientation(ViewPager2.ORIENTATION_VERTICAL) 
Isso acontece novamente graças à transição para o 
RecyclerView : sob o capô, uma mudança na orientação do 
LayoutManager é chamada , responsável pela exibição dos itens da lista. Note-se que delegar um grande número de tarefas para outras classes beneficiou o novo componente: sua listagem se tornou muito mais compacta e legível.
Este não é o fim da diversão. Em uma atualização, o 
ViewPager2 recebeu suporte para o 
ItemDecoration : uma classe 
auxiliar para decorar o 
View filho. Este mecanismo pode ser usado para desenhar separadores entre elementos, bordas e destaque de células.
Já existem muitas implementações prontas de decoradores, porque por muitos anos elas foram usadas com sucesso ao trabalhar com o 
RecyclerView usual. Todos os desenvolvimentos agora são aplicáveis aos pagers. Pronto para uso, está disponível uma implementação padrão de separadores de pager:
 main_pager.addItemDecoration( DividerItemDecoration(this, RecyclerView.HORIZONTAL) ) 
Juntamente com a próxima atualização em maio de 2019, o 
ViewPager2 adicionou outro método importante: 
setOffscreenPageLimit (Int) . Ele é responsável por quantos elementos à direita e à esquerda da central serão inicializados no pager. Embora o 
RecyclerView seja responsável por armazenar em cache e exibir a 
Visualização por padrão, usando este método, você pode definir explicitamente o número desejado de itens a serem carregados.
Adaptador
O sucessor ideológico do primeiro adaptador de pager é o 
FragmentStateAdapter : as interfaces de interação e a nomeação de classes são quase as mesmas. As alterações afetaram apenas a nomeação de alguns métodos. Se anteriormente era necessário implementar a função abstrata 
getItem (position) para retornar a instância do 
Fragment desejada para a posição especificada e essa nomeação poderia ser interpretada de duas maneiras, agora essa função foi renomeada para 
createFragment (position) . O número total de fragmentos é fornecido como antes pela função 
getCount () .
Das importantes mudanças estruturais na interface, também deve ser observado que o adaptador agora tem a capacidade de controlar o ciclo de vida de seus elementos; portanto, junto com o 
FragmentManager no construtor, ele aceita um 
objeto de Ciclo de Vida , uma 
Atividade ou um 
Fragmento . Por esse 
motivo , por segurança, os 
métodos saveState () e 
restoreState () foram declarados finais e fechados para herança.
A classe 
FragmentViewHolder é responsável por armazenar fragmentos dentro do 
RecyclerView . O método 
onCreateViewHolder () de 
FragmentStateAdapter chama 
FragmentViewHolder.create () .
 static FragmentViewHolder create(ViewGroup parent) { FrameLayout container = new FrameLayout(parent.getContext()); container.setLayoutParams( new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ); container.setId(ViewCompat.generateViewId()); container.setSaveEnabled(false); return new FragmentViewHolder(container); } 
Quando o método 
onBindViewHolder () é 
chamado , o identificador do elemento na posição atual e o identificador 
ViewHolder são 
associados , para anexar ainda mais o fragmento a ele:
 final long itemId = holder.getItemId(); final int viewHolderId = holder.getContainer().getId(); final Long boundItemId = itemForViewHolder(viewHolderId); ... mItemIdToViewHolder.put(itemId, viewHolderId); ensureFragment(position);  
E, finalmente, ao anexar um contêiner do 
ViewHolder à hierarquia do 
View , uma 
FragmentTransaction é executada, adicionando um 
Fragment ao contêiner:
 void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) { Fragment fragment = mFragments.get(holder.getItemId()); ... scheduleViewAttach(fragment, container); mFragmentManager.beginTransaction() .add(fragment, "f" + holder.getItemId()) .setMaxLifecycle(fragment, STARTED) .commitNow(); ... } 
Assim, 
surgem dois usos do 
ViewPager2 : herdando a classe do adaptador, diretamente do 
RecyclerView.Adapter ou do 
FragmentStateAdapter .
Certamente você terá uma pergunta: por que usar um segundo pager com fragmentos e um adaptador para eles quando existe uma primeira versão normalmente funcionando? 
O ViewPager está longe de ser uma “bala de prata” ao trabalhar com grandes listas de dados dinâmicos. É ótimo para criar carrosséis com um conjunto estático de fotos ou banners, mas feeds de notícias paginados com o carregamento de postagens de publicidade, a filtragem dá origem a monstros feios e com forte suporte. Mais cedo ou mais tarde, você certamente encontrará um desejo ardente de reescrever tudo no 
RecyclerView . Agora você não precisa fazer isso, porque o pager se transformou nele, emprestando seus poderosos recursos para trabalhar com listas dinâmicas, enquanto os agrupa na sintaxe usual.
A única coisa que o 
PagerAdapter pode 
nos oferecer é o método 
notifyDataSetChanged () , que força o 
ViewPager a redesenhar todos os itens da lista renderizada. Você pode razoavelmente perceber que ninguém está nos impedindo de armazenar uma lista de posições para elementos existentes e retornando 
POSITION_UNCHANGED do método 
getItemPosition () para eles, é isso. No entanto, essa solução não pode ser chamada de bonita; é bastante complicada; além disso, é difícil expandir os casos em que os elementos da lista estão constantemente mudando e não apenas adicionados sequencialmente ao final. 
O FragmentStateAdapter possui um arsenal completo de métodos 
RecyclerView.Adapter , para que a lógica dos elementos redesenhados possa ser configurada com muito mais flexibilidade. Além disso, junto com o 
FragmentStateAdapter, você pode usar o 
DiffUtil , que permite automatizar quase completamente o trabalho de notificação de alterações.

Atenção! Para que os métodos notify ... funcionem corretamente (exceto para notifyDataSetChanged ), os métodos getItemId (Int) ec ontainsItem (Long) devem ser redefinidos. Isso é feito porque a implementação padrão olha apenas para o número da página e, por exemplo, se você adicionar um novo elemento após o atual, ele não será adicionado, pois o getItemId permanecerá inalterado. Um exemplo de substituição desses dois métodos com base em uma lista de elementos do tipo Int :
 override fun getItemId(position: Int): Long { return items[position].toLong() } override fun containsItem(itemId: Long): Boolean { return items.contains(itemId.toInt()) } 
A principal razão para a aparência do 
ViewPager2 é a relutância em reinventar a roda. Por um lado, a 
equipe de desenvolvimento do 
AndroidX está claramente pronta para abandonar o obsoleto 
ViewPager agora e certamente não vai investir na expansão de sua funcionalidade. Sim e porque? Afinal, o 
RecyclerView já sabe tudo o que é necessário. Por outro lado, a remoção e o término do suporte a um componente tão amplamente usado obviamente não acrescentam lealdade à comunidade.
Para resumir: o 
ViewPager2 é definitivamente digno de atenção, embora no momento não esteja isento de falhas sérias.
Contras
- Umidade e um grande número de bugs (desculpável pela versão beta);
- Proximidade. O RecyclerView é um campo privado do ViewPager2 , que nos priva de muitas oportunidades: é impossível implementar deslizar para dispensar ou arrastar e soltar (o ItemTouchHelper se conecta diretamente ao RecyclerView ), você não pode redefinir o ItemAnimator de nenhuma forma, não acesse o LayoutManager diretamente e use o RecycledViewPool . No entanto, com o lançamento de novas versões do componente, o número de métodos de interface herdados do RecyclerView está aumentando (por exemplo, ItemDecoration ), e podemos esperar adicionar os métodos ausentes no futuro.
Prós
- Suporte para todas as vantagens do RecyclerView.Adapter : combinando elementos de diferentes tipos em uma lista, adicionando e removendo elementos diretamente durante o furto, redesenhando animado o conteúdo da lista ao mudar;
- Suporte para todo o espectro de métodos de notificação ... e cálculo automático de alterações usando o DiffUtil ;
- Facilidade de transição devido à continuidade da sintaxe;
- Suporte para orientação vertical e horizontal "fora da caixa";
- Suporte a RTL ;
- ItemDecorator de suporte;
- Suporte para rolagem de software através de fakeScrollBy () ;
- Capacidade de definir manualmente o número de itens carregados;
- A capacidade de usar qualquer uma das soluções de código aberto prontas para reduzir o código padrão , o que é inevitável ao escrever o RecyclerView.Adapter personalizado. Por exemplo, EasyAdapter .
Como resumo, quero dizer que o 
ViewPager2 realmente vale a pena olhar 
mais de perto . Esta é uma solução promissora, extensível e funcional. E embora seja muito cedo para lançar um 
novo widget em produção, é seguro dizer que, após um lançamento completo, ele pode e deve suplantar completamente seu ancestral.
Para aqueles ousados e decisivos, que o artigo inspirou para experimentar, o 
PagerSnapHelper apareceu na 28ª versão da 
Biblioteca de suporte , o que significa que você pode usá-lo junto com o 
RecyclerView criando o 
ViewPager2 .
A operação de 
exemplo do 
ViewPager2 e 
FragmentStateAdapter .
Notas de versão oficiais 
do ViewPager2