Руководство Android ViewPager2
1. Android ViewPager2 vs ViewPager
Android ViewPager это компонент интерфейса представленный в библиотеке поддержки Android, он позволяет пользователю свайпать (swipe) на лево или на право, чтобы посмтреть абсолютно новую страницу (экрана).
ViewPager/ViewPager2
Один ViewPager/ViewPager2 где каждая страница заполняет экран, пользователь может свайпать (swipe) на лево, чтобы перейти к следующей странице или на право, чтобы вернуться к предыдущей.
ViewPager/ViewPager2 может увидеть на приложении по обучению иностранного языка Duolingo.
ViewPager2 vs ViewPager
Как мы уже знаем, команда Android постоянно выпускает новые обновления и улучшения для Android Framework. Один из компонентов получивших большие обновления это ViewPager2. ViewPager2 это замена ViewPager и имеет некоторые улучшения по производительности и дополнительных функций.
Так что эе нового в ViewPager2?
- ViewPager2 это импровизированная версия у ViewPager предоставляющая дополнительные функции и разрешает часто встречаемые проблемы в ViewPager.
- ViewPager2 построен наRecyclerView. Поэтому у вас есть прекрасные преимущества, что есть у RecyclerView. Например: вы можете воспользоваться DiffUtils чтобыы эффектвно рассчитать разницу между наборами данных и обновлять ViewPager с анимациями.
- ViewPager2 поддерживает вертикальное направление (Vertical Orientation). Если использовать ViewPager, вам нужно написать дополнительные кастомизации для ViewPager чтобы получить одинаковые результаты.
- ViewPager2 поддерживает Right-to-Left (RTL) и это будет автоматически включено на основании App Locale.
- При работе с набором Fragment. Если один из Fragment меняет свой интерфейс, вам нужно только вызвать метод notifyDatasetChanged() чтобы обновить интерфейс приложения правильным способом.
- ViewPager2 поддерживает трансформацию (transformation) страницы, тое есть вы можете предоставить анимации при переключении между страницами. Вы так же можете написать собственный кастомизированный PageTransformer.
- PagerAdapter заменен на RecyclerView.
- FragmentStatePagerAdapter заменен на FragmentStateAdapter.
Library
ViewPager2 это компонент не имеющийся наготове в стандартной библиотеке Android, поэтому если вы хотите использовать его, вам нужно его установить в ваш project.
Вы можете установить ViewPager2 из Palette окна дизайна.
После установки, вы увидите библиотеку ViewPager2 объявленную в build.gradle (Module: app).
implementation 'androidx.viewpager2:viewpager2:1.0.0'
2. Example: ViewPager2
Теперь мы выполним пример ViewPager2 и используем Fragment в качестве схемы для каждой страницы. Ниже является изображение для просмотра примера:
- Файл fragment_employee_page.xml это схема интерфейса страницы (Page).
- Класс EmployeePageFragment содержит данные объекта Employee и отображает их на одной странице (Page).
На Android Studio создайте новый project:
- File > New > New Project > Empty Activity
- Name: ViewPager2Example
- Package name: org.o7planning.viewpager2example
- Language: Java
Как было сказано выше ViewPager2 это компонент не имеющийся в стандартной библиотеке Android, поэтому следующий шаг это вам нужно установить его в ваш project из Palette окна дизайна.
И создайте Fragment (EmployeePageFragment), он соответствует странице (Page) ViewPager2:
- File > New > Fragment > Fragment (Blank)
- Fragment Name: EmployeePageFragment
- Fragment Layout Name: fragment_page_employee
- Source Language: Java
Теперь вы увидите два созданных файла:
Откройте файл fragment_employee_page.xml чтобы создать дизайн интерфейса.
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;
}
}
Главный интерфейс приложения:
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
Чтобы отобразить анимационный эффект, когда пользователь переключает между страницами, вам нужно написать класс, который применяет (implements) interface ViewPager2.PageTransformer и предоставляет его для объекта ViewPager2.
ViewPager2 viewPager = findViewById(R.id.pager);
...
viewPager.setPageTransformer(new YourPageTransformer());
Interface ViewPager2.PageTransformer имеется только в одном методе transformPage(), который вызывается один раз для каждого перехода (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);
Прдолжаем с примером выше, мы добавим к нему анимационный эффект.
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());
}
}
}
Pуководства Android
- Настроить Android Emulator в Android Studio
- Руководство Android ToggleButton
- Создать простой File Finder Dialog в Android
- Руководство Android TimePickerDialog
- Руководство Android DatePickerDialog
- Что мне нужно для начала работы с Android?
- Установите Android Studio в Windows
- Установите Intel® HAXM для Android Studio
- Руководство Android AsyncTask
- Руководство Android AsyncTaskLoader
- Руководство Android для начинающих - основные примеры
- Как узнать номер телефона Android Emulator и изменить его?
- Руководство Android TextInputLayout
- Руководство Android CardView
- Руководство Android ViewPager2
- Получить номер телефона в Android с помощью TelephonyManager
- Руководство Android Phone Call
- Руководство Android Wifi Scanning
- Руководство Android 2D Game для начинающих
- Руководство Android DialogFragment
- Руководство Android CharacterPickerDialog
- Руководство Android для начинающих - Hello Android
- Использование Android Device File Explorer
- Включить USB Debugging на устройстве Android
- Руководство Android UI Layouts
- Руководство Android SMS
- Руководство Android SQLite Database
- Руководство Google Maps Android API
- Руководство Текст в речь на Android
- Руководство Android Space
- Руководство Android Toast
- Создание пользовательских Android Toast
- Руководство Android SnackBar
- Руководство Android TextView
- Руководство Android TextClock
- Руководство Android EditText
- Руководство Android TextWatcher
- Форматирование номера кредитной карты с помощью Android TextWatcher
- Руководство Android Clipboard
- Создать простой File Chooser в Android
- Руководство Android AutoCompleteTextView и MultiAutoCompleteTextView
- Руководство Android ImageView
- Руководство Android ImageSwitcher
- Руководство Android ScrollView и HorizontalScrollView
- Руководство Android WebView
- Руководство Android SeekBar
- Руководство Android Dialog
- Руководство Android AlertDialog
- Руководство Android RatingBar
- Руководство Android ProgressBar
- Руководство Android Spinner
- Руководство Android Button
- Руководство Android Switch
- Руководство Android ImageButton
- Руководство Android FloatingActionButton
- Руководство Android CheckBox
- Руководство Android RadioGroup и RadioButton
- Руководство Android Chip и ChipGroup
- Использование Image assets и Icon assets Android Studio
- Настройка SD Card для Android Emulator
- Пример ChipGroup и Chip Entry
- Как добавить внешние библиотеки в Android Project в Android Studio?
- Как отключить разрешения, уже предоставленные приложению Android?
- Как удалить приложения из Android Emulator?
- Руководство Android LinearLayout
- Руководство Android TableLayout
- Руководство Android FrameLayout
- Руководство Android QuickContactBadge
- Руководство Android StackView
- Руководство Android Camera
- Руководство Android MediaPlayer
- Руководство Android VideoView
- Воспроизведение звуковых эффектов в Android с помощью SoundPool
- Руководство Android Networking
- Руководство Android JSON Parser
- Руководство Android SharedPreferences
- Руководство Android Internal Storage
- Руководство Android External Storage
- Руководство Android Intents
- Пример явного Android Intent, вызов другого Intent
- Пример неявного Android Intent, откройте URL, отправьте email
- Руководство Android Services
- Использовать оповещения в Android - Android Notification
- Руководство Android DatePicker
- Руководство Android TimePicker
- Руководство Android Chronometer
- Руководство Android OptionMenu
- Руководство Android ContextMenu
- Руководство Android PopupMenu
- Руководство Android Fragment
- Руководство Android ListView
- Android ListView с Checkbox с помощью ArrayAdapter
- Руководство Android GridView
Show More