Migrar do ViewPager para o ViewPager2

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:

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 ou Fragment em que o objeto ViewPager2 reside. Na maioria dos casos, essa é a melhor opção.
  • Um objeto FragmentManager e um objeto Lifecycle.

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(), modifique getItemCount(). Além do nome, esse método não foi alterado.
  • Em vez de getItem(), substitua createFragment() em classes de adaptadores baseadas em fragmentos. O novo método createFragment() 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:

  1. Mude a superclasse para RecyclerView.Adapter para percorrer visualizações ou FragmentStateAdapter para percorrer fragmentos.
  2. Altere os parâmetros do construtor em classes de adaptador baseadas em fragmento.
  3. Modifique getItemCount() em vez de getCount().
  4. Substitua createFragment() em vez de getItem() 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

Vídeos