devstory

Le Tutoriel de Android ViewPager2

  1. Android ViewPager2 vs ViewPager
  2. Example: ViewPager2
  3. Example: Transformation

1. Android ViewPager2 vs ViewPager

Android ViewPager est un composant d'interface introduit dans la bibliothèque de soutien d'Android. Cela permet à l'utilisateur de glisser (swipe) vers la gauche ou vers la droite afin d'afficher une toute nouvelle page (écran).
ViewPager/ViewPager2
Un ViewPager/ViewPager2 dont chaque page remplit l'écran, l'utilisateur peut glisser (swipe) vers la gauche pour passer à la page suivante ou vers la droite pour revenir à la page précédente.
Un ViewPager/ViewPager2 vu dans l'application d'apprentissage de langues étrangères Duolingo.
ViewPager2 vs ViewPager
Comme on le sait, l'équipe d'Android publie régulièrement des mises à jour et améliorations pour Android Framework. L'un des plus grands bénéficiaires est ViewPager2. ViewPager2 remplace ViewPager et dispose d'une meilleure efficience et des fonctions supplémentaires.
Quelles sont les nouveautés de ViewPager2?
  • ViewPager2 est une version improvisée de ViewPager qui propose des fonctions supplémentaires et résout des problèmes courants dans ViewPager.
  • ViewPager2 est bâti sur RecyclerView. Ainsi, vous pouvez profiter de grands avantages de RecyclerView. Par exemple: vous pouvez utiliser DiffUtils pour calculer efficacement les différences entre les ensembles de données et les améliorations de ViewPager via les animations.
  • ViewPager2 soutient l'orientation verticale (Vertical Orientation). Si vous utilisez ViewPager, vous devez composer des personnalisations supplémentaires pour ViewPager pour obtenir les mêmes résultats.
  • ViewPager2 soutient Right-to-Left (RTL) et cette fonction est activée automatiquement en fonction d'App Locale.
  • Quand vous travaillez avec un ensemble de Fragment(s). Si l'un des Fragment(s) change d'interface, il suffit de lancer la méthode notifyDatasetChanged() pour mettre à jour efficacement l'interface de l'application.
  • ViewPager2 soutient les transformations de page (transformation), c'est-à-dire que vous pouvez fournir des animations lors du passage d'une page à l'autre. Vous pouvez également écrire votre PageTransformer personnalisé.
  • PagerAdapter est remplacé par RecyclerView.
  • FragmentStatePagerAdapter est remplacé par FragmentStateAdapter.
Library
ViewPager2 est un composant non disponible dans la bibliothèque standard d'Android, donc, si vous voulez l'utiliser, il faut l'installer dans votre projet.
Vous pouvez installer ViewPager2 à partir de Palette dans la fenêtre de conception.
Après l'installation finale, vous trouvez la bibliothèque ViewPager2 déclarée dans build.gradle (Module: app).
implementation 'androidx.viewpager2:viewpager2:1.0.0'

2. Example: ViewPager2

Prendre un exemple à propos de ViewPager2 et utiliser Fragment comme plan directeur pour chaque page. Ci-dessous l'image d'aperçu de l'exemple:
  • Le fichier fragment_employee_page.xml est un plan d'interface d'une page (Page).
  • La classe EmployeePageFragment contient les données de l'objet Employee et les affiche sur une page (Page).
Dans Android Studio, créer un projet:
  • File > New > New Project > Empty Activity
    • Name: ViewPager2Example
    • Package name: org.o7planning.viewpager2example
    • Language: Java
Comme mentionné là-dessus, ViewPager2 est un composant non disponible dans la bibliothèque standard d'Android, donc, à l'étape suivante, il faut l'installer dans votre projet à partir de Palette de la fenêtre de design.
Et créer un Fragment (EmployeePageFragment) correspondant à une page (Page) de ViewPager2:
  • File > New > Fragment > Fragment (Blank)
  • Fragment Name: EmployeePageFragment
  • Fragment Layout Name: fragment_page_employee
  • Source Language: Java
Vous pouvez constater que deux fichiers sont créés:
Ensuite, ouvrir le fichier fragment_employee_page.xml pour concevoir son interface.
fragment_employee_page.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView_fullName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="30dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:background="#C4CFB9"
        android:gravity="center_horizontal"
        android:text="Fullname"
        android:textSize="22sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView71"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="Position:"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView_fullName" />

    <TextView
        android:id="@+id/textView_position"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:text="(Position)"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView71"
        app:layout_constraintTop_toBottomOf="@+id/textView_fullName" />

    <TextView
        android:id="@+id/textView72"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:text="Email:"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView71" />

    <TextView
        android:id="@+id/textView_email"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:text="(Email)"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textView72"
        app:layout_constraintTop_toBottomOf="@+id/textView71" />

</androidx.constraintlayout.widget.ConstraintLayout>
EmployeePageFragment.java
package org.o7planning.viewpager2example;

import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class EmployeePageFragment  extends Fragment {

    private static final String LOG_TAG = "AndroidExample";

    private Employee employee;

    private TextView textViewEmail;
    private TextView textViewPosition;
    private TextView textViewFullName;

    private static int counter = 0;

    // IMPORTANT:
    // Required default public constructor.
    // If configuration change.
    // For example: User rotate the Phone,
    //  Android will create new Fragment (EmployeePageFragment) via default Constructor
    //  so this.employee will be null.
    public EmployeePageFragment() {

    }

    public EmployeePageFragment(Employee employee) {
        this.employee = employee;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = (ViewGroup) inflater.inflate(
                R.layout.fragment_employee_page, container, false);

        counter++;
        if(counter % 2 == 0) {
            view.setBackgroundColor(Color.parseColor("#ebdef0"));
        } else  {
            view.setBackgroundColor(Color.parseColor("#e8f8f5"));
        }

        this.textViewFullName = view.findViewById(R.id.textView_fullName);
        this.textViewPosition = view.findViewById(R.id.textView_position);
        this.textViewEmail = view.findViewById(R.id.textView_email);

        return view;
    }


    // Called when configuration change.
    // For example: User rotate the Phone,
    //  Android will create new Fragment (EmployeePageFragment) object via default Constructor
    //  so this.employee will be null.
    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        Log.i(LOG_TAG, "onSaveInstanceState: save employee data to Bundle");
        // Convert employee object to Bundle.
        Bundle dataBundle = this.employeeToBundle(this.employee);
        outState.putAll(dataBundle);

        super.onSaveInstanceState(outState);
    }


    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        Log.i(LOG_TAG, "onViewStateRestored");

        super.onViewStateRestored(savedInstanceState);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        Log.i(LOG_TAG, "onViewCreated");
        super.onViewCreated(view, savedInstanceState);

        if(this.employee == null)  {
            Log.i(LOG_TAG, "Get employee data from savedInstanceState");
            // The state was saved by onSaveInstanceState(Bundle outState) method.
            this.employee = this.bundleToEmployee(savedInstanceState);
        }
        this.showInGUI(this.employee);
    }

    // Call where View ready.
    private void showInGUI(Employee employee)  {
        this.textViewFullName.setText(employee.getFullName());
        this.textViewPosition.setText(employee.getPosition());
        this.textViewEmail.setText(employee.getEmail());
    }

    private Bundle employeeToBundle(Employee employee)  {
        Bundle bundle = new Bundle();
        bundle.putString("fullName", employee.getFullName());
        bundle.putString("position", employee.getPosition());
        bundle.putString("email", employee.getEmail());

        return bundle;
    }

    private Employee bundleToEmployee(Bundle savedInstanceState) {
        String fullName = savedInstanceState.getString("fullName");
        String position = savedInstanceState.getString("position");
        String email = savedInstanceState.getString("email");
        return new Employee(fullName, email, position);
    }

}
EmployeeFragmentStateAdapter.java
package org.o7planning.viewpager2example;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.ArrayList;
import java.util.List;

public class EmployeeFragmentStateAdapter extends FragmentStateAdapter {

    private List<Employee> employees;


    public EmployeeFragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);

        this.employees = this.intDatas();
    }

    private List<Employee> intDatas()  {
        Employee emp1 = new Employee("James Smith", "jamessmith@example.com", "Web Designer");
        Employee emp2 = new Employee("Elizabeth Johnson", "elizabethjohnson@example.com", "Project Manager");
        Employee emp3 = new Employee("Catherine Johnson", "catherinejohnson@example.com", "President of Sales");

        List<Employee> list = new ArrayList<Employee>();
        list.add(emp1);
        list.add(emp2);
        list.add(emp3);
        return list;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        Employee employee = this.employees.get(position);
        return new EmployeePageFragment(employee);
    }


    @Override
    public int getItemCount() {
        return this.employees.size();
    }
}
Employee.java
package org.o7planning.viewpager2example;

public class Employee {

    private String fullName;
    private String email;
    private String position;

    public Employee(String fullName, String email, String position) {
        this.fullName = fullName;
        this.email = email;
        this.position = position;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

}
Voici l'interface principal de l'application:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager2_employee"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package org.o7planning.viewpager2example;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    private ViewPager2 viewPager2Employee;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.viewPager2Employee = findViewById(R.id.viewPager2_employee);

        // Employee FragmentStateAdapter.
        EmployeeFragmentStateAdapter adapter = new EmployeeFragmentStateAdapter(this);
        this.viewPager2Employee.setAdapter(adapter);
    }

}

3. Example: Transformation

Afin d'afficher les effets d'animation quand l'utilisateur fait défiler les pages, il est nécessaire d'écrire une classe qui installe (implements) l'interface de ViewPager2.PageTransformer et la fournit à l'objet ViewPager2.
ViewPager2 viewPager = findViewById(R.id.pager);
...
viewPager.setPageTransformer(new YourPageTransformer());
L'interface de ViewPager2.PageTransformer ne contient qu'une seule méthode transformPage(), qui est convoquée à chaque transition (transition).
/**
 * Apply a property transformation to the given page.
 *
 * @param page Apply the transformation to this page
 * @param position Position of page relative to the current front-and-center
 *                 position of the pager. 0 is front and center. 1 is one full
 *                 page position to the right, and -2 is two pages to the left.
 *                 Minimum / maximum observed values depend on how many pages we keep
 *                 attached, which depends on offscreenPageLimit.
 *
 * @see #setOffscreenPageLimit(int)
 */
void transformPage(@NonNull View page, float position);
Ajouter des effets d'animation dans l'exemple ci-dessous:
Zoom Out Page Transformer
ZoomOutPageTransformer.java
package org.o7planning.viewpager2example.transformer;

import android.view.View;

import androidx.viewpager2.widget.ViewPager2;

public class ZoomOutPageTransformer implements ViewPager2.PageTransformer {
    private static final float MIN_SCALE = 0.85f;
    private static final float MIN_ALPHA = 0.5f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();
        int pageHeight = view.getHeight();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0f);

        } else if (position <= 1) { // [-1,1]
            // Modify the default slide transition to shrink the page as well
            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            if (position < 0) {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }

            // Scale the page down (between MIN_SCALE and 1)
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

            // Fade the page relative to its size.
            view.setAlpha(MIN_ALPHA +
                    (scaleFactor - MIN_SCALE) /
                            (1 - MIN_SCALE) * (1 - MIN_ALPHA));

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0f);
        }
    }
}
MainActivity.java (Zoom Out Page Transformer)
package org.o7planning.viewpager2example;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Bundle;
import org.o7planning.viewpager2example.transformer.ZoomOutPageTransformer;

public class MainActivity extends AppCompatActivity {

    private ViewPager2 viewPager2Employee;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.viewPager2Employee = findViewById(R.id.viewPager2_employee);

        // Employee FragmentStateAdapter.
        EmployeeFragmentStateAdapter adapter = new EmployeeFragmentStateAdapter(this);
        this.viewPager2Employee.setAdapter(adapter);

        // PageTransformer
        this.viewPager2Employee.setPageTransformer(new ZoomOutPageTransformer());

    }
}
Depth Page Transformer
DepthPageTransformer.java
package org.o7planning.viewpager2example.transformer;

import android.view.View;

import androidx.annotation.RequiresApi;
import androidx.viewpager2.widget.ViewPager2;

@RequiresApi(21)
public class DepthPageTransformer implements ViewPager2.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0f);

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1f);
            view.setTranslationX(0f);
            view.setTranslationZ(0f);
            view.setScaleX(1f);
            view.setScaleY(1f);

        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);
            // Move it behind the left page
            view.setTranslationZ(-1f);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0f);
        }
    }
}
MainActivity.java (Depth Page Transformer)
package org.o7planning.viewpager2example;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Build;
import android.os.Bundle;

import org.o7planning.viewpager2example.transformer.DepthPageTransformer;

public class MainActivity extends AppCompatActivity {

    private ViewPager2 viewPager2Employee;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.viewPager2Employee = findViewById(R.id.viewPager2_employee);

        // Employee FragmentStateAdapter.
        EmployeeFragmentStateAdapter adapter = new EmployeeFragmentStateAdapter(this);
        this.viewPager2Employee.setAdapter(adapter);

        // PageTransformer  
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Api 21+.
           this.viewPager2Employee.setPageTransformer(new DepthPageTransformer());
        }
    }

}

Tutoriels de programmation Android

Show More