O ViewPager2
é uma versão aprimorada da biblioteca ViewPager
que oferece
funcionalidade aprimorada e resolve dificuldades comuns no uso do ViewPager
.
Se seu app já usa o ViewPager
, leia esta página para saber mais sobre
como migrar para ViewPager2
.
Se você quiser usar ViewPager2
no seu app e não estiver usando
ViewPager
, leia Deslizar entre fragmentos com o
ViewPager2 e Criar visualizações deslizáveis com
guias usando o ViewPager2 para mais
informações.
Benefícios da migração para o ViewPager2
O principal motivo para a migração é que ViewPager2
está recebendo suporte
de desenvolvimento ativo e ViewPager
não. No entanto, ViewPager2
também oferece
várias outras vantagens específicas.
Compatibilidade com orientação vertical
O ViewPager2
oferece suporte à paginação vertical, além da paginação horizontal
tradicional. É possível ativar a paginação vertical para um elemento ViewPager2
definindo o
atributo android:orientation
:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:orientation="vertical" />
Também é possível definir esse atributo de maneira programática usando o método setOrientation().
Suporte da direita para a esquerda
O ViewPager2
é compatível com paginação da direita para a esquerda (RTL, na sigla em inglês). A paginação RTL é ativada
automaticamente quando apropriado com base na localidade, mas você também pode ativar
manualmente a paginação RTL para um elemento ViewPager2
, definindo o
atributo android:layoutDirection
:
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layoutDirection="rtl" />
Também é possível definir esse atributo de maneira programática usando o método setLayoutDirection().
Coleções de fragmentos modificáveis
ViewPager2
oferece suporte à paginação por meio de uma coleção modificável de fragmentos,
chamando
notifyDatasetChanged()
para atualizar a IU quando a coleção subjacente muda.
Isso significa que o app pode modificar dinamicamente a coleção de fragmentos no
tempo de execução e que o ViewPager2
exibirá corretamente a coleção modificada.
DiffUtil
A ViewPager2
é criada em RecyclerView
,
o que significa que ela tem acesso à
classe de utilitário
DiffUtil
. Isso resulta em vários benefícios, mas o mais importante é que
os objetos ViewPager2
aproveitam de forma nativa as animações de mudança do conjunto de dados
da classe RecyclerView
.
Migrar seu app para o ViewPager2
Siga estas etapas para atualizar objetos ViewPager
no seu app para ViewPager2
:
Atualizar arquivos de layout XML
Primeiro, substitua os elementos ViewPager
nos arquivos de layout XML por
elementos ViewPager2
:
<!-- A ViewPager element -->
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- A ViewPager2 element -->
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Atualizar as classes do adaptador
Ao usar ViewPager
, você precisou estender a classe do adaptador que
forneceu novas páginas ao objeto. Dependendo do caso de uso, o ViewPager
usava
três classes abstratas diferentes. O ViewPager2
usa apenas duas classes abstratas.
Para cada objeto ViewPager
que você está convertendo em um objeto ViewPager2
,
atualize a classe do adaptador para estender a classe abstrata apropriada da seguinte maneira:
- Quando
ViewPager
usouPagerAdapter
para paginar visualizações, useRecyclerView.Adapter
comViewPager2
. - Quando
ViewPager
usouFragmentPagerAdapter
para paginar um número pequeno e fixo de fragmentos, useFragmentStateAdapter
comViewPager2
. - Quando
ViewPager
tiver usadoFragmentStatePagerAdapter
para paginar um número grande ou desconhecido de fragmentos, useFragmentStateAdapter
comViewPager2
.
Parâmetros do construtor
As classes de adaptador baseadas em fragmentos herdadas de FragmentPagerAdapter
ou
FragmentStatePagerAdapter
sempre aceitam um único objeto FragmentManager
como parâmetro de construtor. Ao estender FragmentStateAdapter
para uma
classe de adaptador ViewPager2
, você tem as seguintes opções para parâmetros
do construtor:
- O objeto
FragmentActivity
ouFragment
em que o objetoViewPager2
reside. Na maioria dos casos, essa é a melhor opção. - Um objeto
FragmentManager
e um objetoLifecycle
.
As classes de adaptador baseadas em visualização herdadas diretamente de RecyclerView.Adapter
não exigem um parâmetro construtor.
Modificar métodos
Suas classes de adaptador também precisam substituir métodos diferentes para ViewPager2
em comparação com ViewPager
:
- Em vez de
getCount()
, modifiquegetItemCount()
. Além do nome, esse método não foi alterado. - Em vez de
getItem()
, substituacreateFragment()
em classes de adaptadores baseadas em fragmentos. O novo métodocreateFragment()
precisa sempre fornecer uma nova instância de fragmento sempre que a função for chamada, em vez de reutilizar instâncias.
Resumo
Em resumo, para converter uma classe de adaptador ViewPager
para uso com ViewPager2
,
é necessário fazer as seguintes mudanças:
- Mude a superclasse para
RecyclerView.Adapter
para percorrer visualizações ouFragmentStateAdapter
para percorrer fragmentos. - Altere os parâmetros do construtor em classes de adaptador baseadas em fragmento.
- Modifique
getItemCount()
em vez degetCount()
. - Substitua
createFragment()
em vez degetItem()
em classes de adaptador baseadas em fragmento.
Kotlin
// A simple ViewPager adapter class for paging through fragments class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = NUM_PAGES override fun getItem(position: Int): Fragment = ScreenSlidePageFragment() } // An equivalent ViewPager2 adapter class class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { override fun getItemCount(): Int = NUM_PAGES override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment() }
Java
// A simple ViewPager adapter class for paging through fragments public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { public ScreenSlidePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new ScreenSlidePageFragment(); } @Override public int getCount() { return NUM_PAGES; } } // An equivalent ViewPager2 adapter class private class ScreenSlidePagerAdapter extends FragmentStateAdapter { public ScreenSlidePagerAdapter(FragmentActivity fa) { super(fa); } @Override public Fragment createFragment(int position) { return new ScreenSlidePageFragment(); } @Override public int getItemCount() { return NUM_PAGES; } }
Refatorar interfaces do TabLayout
O ViewPager2
introduz alterações na integração do TabLayout
. Se você
usa atualmente um ViewPager
com um objeto TabLayout
para exibir guias horizontais
para navegação, é necessário refatorar o objeto TabLayout
para
integração com ViewPager2
.
O TabLayout
foi desacoplado do ViewPager2
e agora está disponível como parte dos
componentes do Material Design. Isso significa que, para usá-lo, é necessário adicionar
a dependência adequada ao seu arquivo build.gradle
:
Groovy
implementation "com.google.android.material:material:1.1.0-beta01"
Kotlin
implementation("com.google.android.material:material:1.1.0-beta01")
Também é necessário mudar o local do elemento TabLayout
na hierarquia do
arquivo de layout XML. Com ViewPager
, o elemento TabLayout
é declarado como
filho de ViewPager
. No entanto, com ViewPager2
, o elemento TabLayout
é declarado diretamente acima do elemento ViewPager2
, no mesmo nível:
<!-- A ViewPager element with a TabLayout -->
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</androidx.viewpager.widget.ViewPager>
<!-- A ViewPager2 element with a TabLayout -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Por fim, atualize o código que anexa o objeto TabLayout
ao
objeto ViewPager
. Embora TabLayout
use o próprio método setupWithViewPager()
para fazer a integração com o ViewPager
, ele exige uma instância TabLayoutMediator
para integrar com o ViewPager2
.
O objeto TabLayoutMediator
também processa a tarefa de gerar títulos de página
para o objeto TabLayout
, o que significa que a classe do adaptador não precisa
substituir getPageTitle()
:
Kotlin
// Integrating TabLayout with ViewPager class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) tabLayout.setupWithViewPager(viewPager) } ... } class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { override fun getCount(): Int = 4 override fun getPageTitle(position: Int): CharSequence { return "OBJECT ${(position + 1)}" } ... } // Integrating TabLayout with ViewPager2 class CollectionDemoFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tabLayout = view.findViewById(R.id.tab_layout) TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = "OBJECT ${(position + 1)}" }.attach() } ... }
Java
// Integrating TabLayout with ViewPager public class CollectionDemoFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); tabLayout.setupWithViewPager(viewPager); } ... } public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter { ... @Override public int getCount() { return 4; } @Override public CharSequence getPageTitle(int position) { return "OBJECT " + (position + 1); } ... } // Integrating TabLayout with ViewPager2 public class CollectionDemoFragment : Fragment() { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { TabLayout tabLayout = view.findViewById(R.id.tab_layout); new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> tab.setText("OBJECT " + (position + 1)) ).attach(); } ... }
Suporte a elementos roláveis aninhados
ViewPager2
não oferece suporte nativo a visualizações de rolagem aninhadas nos casos em que a
visualização de rolagem tem a mesma orientação que o objeto ViewPager2
que a contém. Por exemplo, a rolagem não funcionaria para uma visualização de rolagem vertical dentro de um
objeto ViewPager2
orientado verticalmente.
Para oferecer suporte a uma visualização de rolagem dentro de um objeto ViewPager2
com a mesma orientação,
chame
requestDisallowInterceptTouchEvent()
no objeto ViewPager2
quando
esperar rolar o elemento aninhado. O exemplo de rolagem
aninhada do ViewPager2 demonstra uma maneira de resolver esse problema com um
layout de wrapper personalizado versátil.
Outros recursos
Para saber mais sobre o ViewPager2
, consulte os recursos adicionais a seguir.
Exemplos
- Amostras do ViewPager2 no GitHub (em inglês)
Vídeos
- Virando a página: como migrar para o ViewPager2 (Conferência de Desenvolvedores Android '19)