Participe do evento ⁠#Android11: apresentação de lançamento da versão Beta no dia 3 de junho.

Criar visualizações deslizáveis com guias usando o ViewPager

As visualizações deslizáveis permitem que você navegue entre telas irmãs, como guias, com um gesto horizontal com o dedo ou deslizando. Este padrão de navegação também é conhecido como paginação horizontal. Este tópico ensina como criar um layout com visualizações deslizáveis para alternar entre guias, além de mostrar uma faixa de título em vez de guias.

Implementar visualizações deslizáveis

É possível criar visualizações deslizáveis usando o widget ViewPager do AndroidX. Para usar ViewPager e guias, você precisa adicionar uma dependência no ViewPager e nos Componentes do Material do projeto.

Para configurar o layout com ViewPager, adicione o <ViewPager> ao layout XML. Por exemplo, se cada página na visualização deslizável consumir todo o layout, ele ficará assim:

<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" />
    

Para inserir visualizações secundárias que representem cada página, você precisa vincular esse layout a um PagerAdapter. Você pode escolher entre dois tipos de adaptadores integrados:

  • FragmentPagerAdapter: use este ao navegar entre um pequeno número fixo de telas irmãs.
  • FragmentStatePagerAdapter: use esta opção ao paginar um número desconhecido de páginas. FragmentStatePagerAdapter otimiza o uso da memória destruindo fragmentos à medida que o usuário navega.

Por exemplo, veja como você pode usar FragmentStatePagerAdapter para deslizar por uma coleção de objetos Fragment:

Kotlin

    class CollectionDemoFragment : Fragment() {
        // When requested, this adapter returns a DemoObjectFragment,
        // representing an object in the collection.
        private lateinit var demoCollectionPagerAdapter: DemoCollectionPagerAdapter
        private lateinit var viewPager: ViewPager

        override fun onCreateView(inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?): View? {
           return inflater.inflate(R.layout.collection_demo, container, false)
        }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            demoCollectionPagerAdapter = DemoCollectionPagerAdapter(childFragmentManager)
            viewPager = view.findViewById(R.id.pager)
            viewPager.adapter = demoCollectionPagerAdapter
        }
    }

    // Since this is an object collection, use a FragmentStatePagerAdapter,
    // and NOT a FragmentPagerAdapter.
    class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {

        override fun getCount(): Int  = 100

        override fun getItem(i: Int): Fragment {
            val fragment = DemoObjectFragment()
            fragment.arguments = Bundle().apply {
                // Our object is just an integer :-P
                putInt(ARG_OBJECT, i + 1)
            }
            return fragment
        }

        override fun getPageTitle(position: Int): CharSequence {
            return "OBJECT ${(position + 1)}"
        }
    }

    private const val ARG_OBJECT = "object"

    // Instances of this class are fragments representing a single
    // object in our collection.
    class DemoObjectFragment : Fragment() {

       override fun onCreateView(inflater: LayoutInflater,
               container: ViewGroup?,
               savedInstanceState: Bundle?): View {
           return inflater.inflate(R.layout.fragment_collection_object, container, false)
       }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            arguments?.takeIf { it.containsKey(ARG_OBJECT) }?.apply {
                val textView: TextView = view.findViewById(android.R.id.text1)
                textView.text = getInt(ARG_OBJECT).toString()
            }
        }
    }
    

Java

    public class CollectionDemoFragment extends Fragment {
        // When requested, this adapter returns a DemoObjectFragment,
        // representing an object in the collection.
        DemoCollectionPagerAdapter demoCollectionPagerAdapter;
        ViewPager viewPager;

        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater,
                @Nullable ViewGroup container,
                @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.collection_demo, container, false);
        }

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            demoCollectionPagerAdapter = new DemoCollectionPagerAdapter(getChildFragmentManager());
            viewPager = view.findViewById(R.id.pager);
            viewPager.setAdapter(demoCollectionPagerAdapter);
       }
    }

    // Since this is an object collection, use a FragmentStatePagerAdapter,
    // and NOT a FragmentPagerAdapter.
    public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
        public DemoCollectionPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int i) {
            Fragment fragment = new DemoObjectFragment();
            Bundle args = new Bundle();
            // Our object is just an integer :-P
            args.putInt(DemoObjectFragment.ARG_OBJECT, i + 1);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public int getCount() {
            return 100;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "OBJECT " + (position + 1);
        }
    }

    // Instances of this class are fragments representing a single
    // object in our collection.
    public class DemoObjectFragment extends Fragment {
        public static final String ARG_OBJECT = "object";

        @Override
        public View onCreateView(LayoutInflater inflater,
                ViewGroup container, Bundle savedInstanceState) {
           return inflater.inflate(R.layout.fragment_collection_object, container, false);
        }

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            Bundle args = getArguments();
            ((TextView) view.findViewById(android.R.id.text1))
                    .setText(Integer.toString(args.getInt(ARG_OBJECT)));
        }
    }
    

As seções a seguir mostram como você pode adicionar guias para facilitar a navegação entre as páginas.

Adicionar guias usando um TabLayout

Um TabLayout oferece uma maneira de exibir guias horizontalmente. Quando usado junto com ViewPager, um TabLayout pode fornecer uma interface familiar para navegar entre páginas em uma visualização deslizável.

Figura 1: um TabLayout com quatro guias.

Para incluir um TabLayout em um ViewPager, adicione um elemento <TabLayout> dentro do elemento <ViewPager>, conforme mostrado abaixo:

<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>
    

Em seguida, use setupWithViewPager() para vincular o TabLayout ao ViewPager. As guias individuais em TabLayout são preenchidas automaticamente com os títulos das páginas de PagerAdapter:

Kotlin

    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)}"
        }
        ...
    }
    

Java

    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);
        }

        ...
    }
    

Para outras orientações sobre o design de layouts de guias, consulte a documentação do Material Design para guias (link em inglês).