Правильная реализация ViewPager2 в Android

Я узнал о ViewPager2 и попытался реализовать его, но не нашел подходящего примера.

Может ли кто-нибудь сказать мне, как я могу его использовать.

Я ищу правильное использование, а не пример.

Вот объяснение и пример проверки: michaelevans.org/blog/2019/02/07/hands-on-with-viewpager2

Mehul Kabaria 12.02.2019 06:32

Тот, кто дал близкие голоса за recommend or find a book, tool, software library, tutorial or other off-site resource Пожалуйста, внимательно прочитайте, что я не ищу, например.

Pratik Butani 12.02.2019 06:57

Есть официальный образец: github.com/googlesamples/android-viewpager2

Albert Vila Calvo 18.09.2019 11:33

ценный вопрос, выглядит как блог: D

Nikunj Paradva 11.06.2020 08:03

@NikunjParadia Ответ Нилеша Ратода очень ценен и также выглядит как сообщение в блоге. Вопрос как раз простой.

Pratik Butani 11.06.2020 09:31

Вот статья о реализации т.лы/3qgj

iamnaran 05.11.2021 13:56
128
6
147 251
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Use of ViewPager2 in Android

Как упоминалось в Сайт разработчика

Изменения API

FragmentStateAdapter replaces FragmentStatePagerAdapter

RecyclerView.Adapter replaces PagerAdapter

registerOnPageChangeCallback replaces addPageChangeListener

В Simple Words адаптер View Pager работает как Recycle View Adapter.

Примечание. Нам не нужно использовать фрагмент в View Pager 2. Он полностью зависит от макета RecyclerView.Adapter inflate.

Вот пример репозитория gitHub Ссылка на сайт

Пример:-

MainActivity.class

public class MainActivity extends AppCompatActivity {
    
    private ViewPager2 mPager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().setTitle("View Pager 2");
        mPager = findViewById(R.id.pager);
        mPager.setAdapter(new MyViewPagerAdapter(this));
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (R.id.change == item.getItemId()) {
            mPager.setOrientation(mPager.getOrientation() != ViewPager2.ORIENTATION_VERTICAL ? ViewPager2.ORIENTATION_VERTICAL : ViewPager2.ORIENTATION_HORIZONTAL);
        }
        return super.onOptionsItemSelected(item);
    }
}

activity_main.xml

<?xml version = "1.0" encoding = "utf-8"?>
<android.support.constraint.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/pager"
        android:layout_width = "match_parent"
        android:layout_height = "match_parent" />

</android.support.constraint.ConstraintLayout>

MyViewPagerAdapter.class

public class MyViewPagerAdapter extends RecyclerView.Adapter<MyHolder> {
    
    private Context context;
    
    public MyViewPagerAdapter(Context context) {
        this.context=context;
    }
    
    @NonNull
    @Override
    public MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyHolder(LayoutInflater.from(context).inflate(R.layout.cell_item, parent, false));
    }
    
    @Override
    public void onBindViewHolder(@NonNull MyHolder holder, int position) {
      holder.mText.setText("Page "+(position+1));
    }
    
    @Override
    public int getItemCount() {
        return 10;
    }
}

cell_item.xml

<?xml version = "1.0" encoding = "utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent">

    <TextView
        android:id = "@+id/text"
        android:layout_width = "match_parent"
        android:layout_height = "match_parent"
        android:gravity = "center"
        android:text = "Page 1"
        android:textSize = "20sp" />

</android.support.constraint.ConstraintLayout>

MyHolder.class

class MyHolder extends RecyclerView.ViewHolder {
    
    public TextView mText;
    
    public MyHolder(@NonNull View itemView) {
        super(itemView);
        mText = itemView.findViewById(R.id.text);
    }
}

вывод:

Нельзя ли использовать ViewPager2 с фрагментами?

ysfcyln 17.02.2019 18:12

@ysfcyln да, вы можете использовать Fragment с ViewPager2, пожалуйста, проверьте мой ответ выше, я добавил пример кода для How use Fragment with ViewPager2

AskNilesh 12.03.2019 08:58

Спасибо, но репозиторий удален.

CoolMind 01.08.2019 16:42

@CoolMind Какой репозиторий удален?

sushildlh 01.08.2019 17:26
github.com/sushildlh/ViewPager2. См. также medium.com/swlh/android-viewpager2-tablayout-3099aae2f396 для получения новой информации. :)
CoolMind 01.08.2019 17:27

Вот репо -> github.com/android/views-widgets-samples/tree/master/ViewPag‌​er2

androidStud 16.10.2019 01:48
Ответ принят как подходящий

ОБНОВЛЕНИЕ 7

Проверить : Переход с ViewPager на ViewPager2

Проверить : Создание прокрутки с вкладками с помощью ViewPager2

ОБНОВЛЕНИЕ 6

Проверьте мой ответ, если вы хотите реализовать карусель с помощью View Pager2

ОБНОВЛЕНИЕ 5

How to use TabLayout with ViewPager2

ОБРАЗЕЦ КОДА

Используйте ниже dependencies

implementation 'com.google.android.material:material:1.1.0-alpha08'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'

ОБРАЗЕЦ КОДА

XMl layout

<?xml version = "1.0" encoding = "utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        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">

    <com.google.android.material.appbar.AppBarLayout
            android:layout_width = "match_parent"
            android:layout_height = "wrap_content"
            android:theme = "@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
                android:id = "@+id/toolbar"
                android:layout_width = "match_parent"
                android:layout_height = "?attr/actionBarSize"
                android:background = "?attr/colorPrimary"
                app:layout_scrollFlags = "scroll|enterAlways"
                app:popupTheme = "@style/ThemeOverlay.AppCompat.Light"/>

        <com.google.android.material.tabs.TabLayout
                android:id = "@+id/tabs"
                android:layout_width = "match_parent"
                android:layout_height = "wrap_content"/>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager2.widget.ViewPager2
            android:id = "@+id/viewpager"
            app:layout_anchor = "@id/tabs"
            app:layout_anchorGravity = "bottom"
            android:layout_width = "match_parent"
            android:layout_height = "match_parent"
    />


</androidx.coordinatorlayout.widget.CoordinatorLayout>

Activity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import com.google.android.material.tabs.TabLayoutMediator

import com.google.android.material.tabs.TabLayout


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//        setSupportActionBar(toolbar)
        viewpager.adapter = AppViewPagerAdapter(supportFragmentManager, lifecycle)

        TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
            override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
                // Styling each tab here
                tab.text = "Tab $position"
            }
        }).attach()


    }
}

OUTPUT

TabLayout с ViewPager2

Из Документов

ViewPager2

New features

  • Поддержка макета справа налево (RTL)
  • Поддержка вертикальной ориентации
  • notifyDataSetChanged полностью функциональный

API changes

  • FragmentStateAdapter заменяет FragmentStatePagerAdapter
  • RecyclerView.Adapter заменяет PagerAdapter
  • registerOnPageChangeCallback заменяет addPageChangeListener

ОБРАЗЕЦ КОДА

add the latest dependencies for ViewPager2

implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha01'

layout

<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    xmlns:tools = "http://schemas.android.com/tools"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    android:orientation = "vertical"
    tools:context = ".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id = "@+id/view_pager"
        android:layout_width = "match_parent"
        android:layout_height = "match_parent" />

</LinearLayout>

activity

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

import java.util.ArrayList;

public class MyActivity extends AppCompatActivity {

    ViewPager2 myViewPager2;
    MyAdapter MyAdapter;
    private ArrayList<String> arrayList = new ArrayList<>();

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

        myViewPager2 = findViewById(R.id.view_pager);

        arrayList.add("Item 1");
        arrayList.add("Item 2");
        arrayList.add("Item 3");
        arrayList.add("Item 4");
        arrayList.add("Item 5");

        MyAdapter = new MyAdapter(this, arrayList);


        myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

        myViewPager2.setAdapter(MyAdapter);
    }
}

MyAdapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
}

Новые возможности

now we need to use ViewPager2.OnPageChangeCallback() to get Swipe event of ViewPager2

ОБРАЗЕЦ КОДА

    myViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }

        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);

            Log.e("Selected_Page", String.valueOf(position));
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
        }
    });

we can set Orientation using myViewPager2.setOrientation()

ОБРАЗЕЦ КОДА

Для использования HORIZONTAL Orientation

myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);

Для использования VERTICAL Orientation

myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

We can use notifyDataSetChanged same as we are using in RecyclerView.Adapter

ОБРАЗЕЦ КОДА для добавления нового товара

    btnAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            arrayList.add("New ITEM ADDED");
            MyAdapter.notifyDataSetChanged();
        }
    });

ОБРАЗЕЦ КОДА для удаления нового элемента

    btnRemove.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            arrayList.remove(3);
            MyAdapter.notifyItemRemoved(3);
        }
    });

ОБНОВИТЬ

Попробуйте это, если вы хотите использовать Fragment с ViewPager2.

First create a ViewPagerFragmentAdapter class which extends FragmentStateAdapter

import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class ViewPagerFragmentAdapter extends FragmentStateAdapter {

    private ArrayList<Fragment> arrayList = new ArrayList<>();

    public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return arrayList.get(position);
    }

    public void addFragment(Fragment fragment) {
        arrayList.add(fragment);
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }
}

Now use like this in your activity

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;

public class MainActivity extends AppCompatActivity {

    ViewPager2 myViewPager2;
    ViewPagerFragmentAdapter myAdapter;

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

        myViewPager2 = findViewById(R.id.view_pager);

        myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager());

        // add Fragments in your ViewPagerFragmentAdapter class
        myAdapter.addFragment(new FragmentOne());
        myAdapter.addFragment(new Fragmenttwo());
        myAdapter.addFragment(new FragmentThree());

        // set Orientation in your ViewPager2
        myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

        myViewPager2.setAdapter(myAdapter);

    }

}

для получения дополнительной информации проверьте это

ОБНОВЛЕНИЕ 2

Version 1.0.0-alpha02

Новые возможности

  • Возможность отключить пользовательский ввод (setUserInputEnabled, isUserInputEnabled)

Изменения API

  • ViewPager2 класс финал

Исправление ошибок

  • Исправления стабильности FragmentStateAdapter

ОБРАЗЕЦ КОДА для отключения свайпа в viewpager2

myViewPager2.setUserInputEnabled(false);// SAMPLE CODE to disable swiping in viewpager2


myViewPager2.setUserInputEnabled(true);//SAMPLE CODE to enable swiping in viewpager2

ОБНОВЛЕНИЕ 3

Version 1.0.0-alpha03

New features

  • Возможность программной прокрутки ViewPager2: поддельныйDragBy(смещениеPx).

API changes

  • FragmentStateAdapter теперь требует объект Lifecycle. Добавлены два служебных конструктора для получения его с хоста FragmentActivity или фрагмента хоста.

ОБРАЗЕЦ КОДА

ViewPagerFragmentAdapter

import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class ViewPagerFragmentAdapter extends FragmentStateAdapter {

    private ArrayList<Fragment> arrayList = new ArrayList<>();


    public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return arrayList.get(position);
    }

    public void addFragment(Fragment fragment) {
        arrayList.add(fragment);
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }
}

MainActivity code

import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;

public class MainActivity extends AppCompatActivity {

    ViewPager2 myViewPager2;
    ViewPagerFragmentAdapter myAdapter;

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

        myViewPager2=findViewById(R.id.view_pager);
        myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager(), getLifecycle());

        // add Fragments in your ViewPagerFragmentAdapter class
        myAdapter.addFragment(new FragmentOne());
        myAdapter.addFragment(new Fragmenttwo());
        myAdapter.addFragment(new FragmentThree());

        myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

        myViewPager2.setAdapter(myAdapter);
    }
}

ОБНОВЛЕНИЕ 4

Version 1.0.0-alpha05 New features

  • Представлен ItemDecorator с поведением, совместимым с RecyclerView.
  • MarginPageTransformer введен для предоставления возможности создавать пространство между страницами (за пределами вставки страницы).
  • CompositePageTransformer введен для обеспечения возможности объединения нескольких PageTransformers

API changes

  • Метод FragmentStateAdapter#getItem переименован в FragmentStateAdapter#createFragment — предыдущее имя метода в прошлом было источником ошибок.
  • Значение OFFSCREEN_PAGE_LIMIT_DEFAULT изменилось с 0 на -1. Нет необходимости в изменении кода клиента, если используется OFFSCREEN_PAGE_LIMIT_DEFAULTconstant.

ОБРАЗЕЦ КОДА

Activity code

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;

public class MainActivity extends AppCompatActivity {

    ViewPager2 myViewPager2;
    ViewPagerFragmentAdapter myAdapter;
    private ArrayList<Fragment> arrayList = new ArrayList<>();

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

        myViewPager2 = findViewById(R.id.myViewPager2);

        // add Fragments in your ViewPagerFragmentAdapter class
        arrayList.add(new FragmentOne());
        arrayList.add(new Fragmenttwo());
        arrayList.add(new FragmentThree());

        myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager(), getLifecycle());
        // set Orientation in your ViewPager2
        myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);

        myViewPager2.setAdapter(myAdapter);

        myViewPager2.setPageTransformer(new MarginPageTransformer(1500));


    }
}

ViewPagerFragmentAdapter

import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class ViewPagerFragmentAdapter extends FragmentStateAdapter {

    private ArrayList<Fragment> arrayList = new ArrayList<>();


    public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position) {
            case 0:
                return new FragmentOne();
            case 1:
                return new Fragmenttwo();
            case 2:
                return new FragmentThree();

        }
        return null;
    }

    @Override
    public int getItemCount() {
        return 3;
    }
}

Кажется, я должен сделать запись в блоге :D

Pratik Butani 12.02.2019 07:28

Круто, у меня не было времени для блога, но обязательно сделаю.

Pratik Butani 12.03.2019 10:43

@PratikButani у нас есть новые обновления для ViewPager2 с использованием Version 1.0.0-alpha02

AskNilesh 16.03.2019 07:00

Каждый раз, когда я читаю ваш комментарий, это снова вдохновляет меня писать в блог :D

Pratik Butani 16.03.2019 07:17
Раннее введение ViewPager2 от @BirjuVachhani, проверьте это, может быть полезно
Ravi 29.03.2019 11:37

Хорошее подробное описание для ViewPager2, но, пожалуйста, внесите изменения, эта версия ViewPager2 выпущена только для Android X. поэтому вам нужно добавить зависимость от Android X для успешной компиляции проекта.

Ramkesh Yadav 14.05.2019 16:36

Можно ли использовать PagerTitleStrip или PagerTabStrip с ViewPager2 или может есть альтернатива?

guillaume 15.05.2019 12:07

Привет @NileshRathod. В обновлении 4 есть ли способ использовать объект ArrayList<Fragment> для динамического создания фрагментов в ViewPagerFragmentAdapter?

mrisek 21.06.2019 08:19

@mrisek да, это возможно, пожалуйста, свяжитесь со мной, я пришлю вам пример кода

AskNilesh 21.06.2019 08:24

как автоматически скользить с индикатором?

Mahmoud Ayman 25.06.2019 10:09

@MahmoudAyman проверьте этот пост stackoverflow.com/a/45518240/7666442

AskNilesh 26.06.2019 08:44

Как уничтожить представления, такие как функция PagerAdapterdestroyItem, в пользовательском RecyclerView.Adapter?

Phani Rithvij 24.12.2019 13:20

Я попытался переопределить onViewRecycled, так как override fun onViewRecycled(holder: ViewHolder) { (holder.itemView as ViewGroup).removeAllViews() super.onViewRecycled(holder) } сработало.

Phani Rithvij 24.12.2019 13:26

@PhaniRithvij нет необходимости уничтожать элемент внутри RecyclerView.Adapter, почему вы хотите уничтожить элемент внутри него?

AskNilesh 24.12.2019 13:37

Потому что я получаю несколько представлений, наложенных друг на друга. Как я делаю holder.view.addView(view_c) в onBindViewHolder

Phani Rithvij 24.12.2019 13:50

Разве список фрагментов, который у вас есть в ViewPagerFragmentAdapter, не предотвратит переработку?

Saurabh 28.01.2020 23:54

Как обернуть высоту Android ViewPager2 до высоты текущего элемента?

Chirag Patel 03.03.2020 06:54

Я думаю, вы все еще используете старый адаптер, поскольку новый адаптер — FragmentStatePagerAdapter.

Amir Dora. 24.03.2020 09:08

@AmirDora нет, я не обновлял свой ответ после 25 ноября 2019 г.

AskNilesh 24.03.2020 09:10

Обновите его, поскольку новую функцию жизненного цикла BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT можно использовать только с FragmentStatePagerAdapter.

Amir Dora. 24.03.2020 09:18

почему getItem для возврата фрагмента больше не существует?

Nanda Z 08.04.2020 10:14

Привет @NileshRathod, можно создать ViewPager2 внутри фрагмента, который может размещать другие фрагменты вместо использования Activity?

Ally 18.04.2020 07:37

@Али да, это возможно

AskNilesh 18.04.2020 08:03

У меня возникли проблемы с внедрением viewPage2 в мой фрагмент, который в основном является homeFrag, дайте мне отладить мой код и понять, что мне не хватает. Спасибо @NileshRathod

Ally 18.04.2020 11:00

@Ali Я использую viewpager с TabLayout во фрагменте, я не могу провести пальцем по экрану. он застрял на 1-м фрагменте, как я могу решить эту проблему

iamkdblue 02.05.2020 22:26

@kdblue вы пытались установить пользовательский ввод viewpager.is Enabled = true

Ally 04.05.2020 02:55

@Ali Я решил, что это происходило из-за макета обновления смахивания.

iamkdblue 04.05.2020 12:08

@iamkdblue Я также столкнулся с той же проблемой, но я хочу сохранить макет обновления смахивания, у вас есть идеи, как это исправить?

Aldor 09.07.2020 11:58

@Ali viewpager.isUserInoutEnabled у меня не работает

Aldor 09.07.2020 12:14

@Nilesh Rathod У меня также есть viewpager2 и tablyout внутри фрагмента, но он не прокручивается.

Aldor 09.07.2020 12:25

@Aldor, не могли бы вы поделиться своим кодом, позвольте мне проверить ваш код

AskNilesh 09.07.2020 12:36

@Aldor, какую версию артефакта вы используете в зависимости от вашего приложения build.gradle?

Ally 09.07.2020 12:57

@Nilesh Rathod вот ссылка на мой вопрос. У меня там тоже есть код. stackoverflow.com/questions/62810129/…

Aldor 09.07.2020 14:22

Реализация @Ali "androidx.viewpager2:viewpager2:1.0.0"

Aldor 09.07.2020 14:24

@NileshRathod, вы создали массив фрагментов, но нигде их не вызываете?

input 20.08.2020 02:51

хороший и подробный пост, но можете ли вы добавить, как обрабатывать жизненные циклы в viewpager2, так как я не могу найти никакого решения..?

Mohit Yadav 06.07.2021 17:31

Все ответы здесь, большое спасибо, я бы хотел, чтобы команда документации Google могла понять, как должно выглядеть руководство по миграции API.

m'hd semps 16.12.2021 17:21

На самом деле теперь есть официальный репозиторий примеров для ViewPager2 (ссылка ниже)

Репо содержит следующие образцы (цитата из файла readme репо ниже)

Samples

  • ViewPager2 with Views - shows how to set up a ViewPager2 with Views as pages
  • ViewPager2 with Fragments - shows how to set up a ViewPager2 with Fragments as pages
  • ViewPager2 with a Mutable Collection (Views) - demonstrates usage of ViewPager2 with Views as pages and mutations in a page adapter
  • ViewPager2 with a Mutable Collection (Fragments) - demonstrates usage of ViewPager2 with Fragments as pages, and mutations in a page adapter
  • ViewPager2 with a TabLayout (Views) - shows how to set up a ViewPager2 with Views as pages, and link it to a TabLayout

Простой пример ViewPager2 с фрагментами в Kotlin

Activity_main.xml

<?xml version = "1.0" encoding = "utf-8"?>
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    xmlns:tools = "http://schemas.android.com/tools"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    android:orientation = "vertical"
    xmlns:app = "http://schemas.android.com/apk/res-auto"
    tools:context = ".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id = "@+id/pager2_container"
        android:layout_width = "match_parent"
        android:layout_height = "0dp"
        android:layout_weight = "1"/>

</LinearLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        val viewPager2 = findViewById<ViewPager2>(R.id.pager2_container)

        val fragmentList = arrayListOf(
            FirstFragment.newInstance(),
            SecondFragment.newInstance(),
            ThirdFragment.newInstance()
        )
        viewPager2.adapter = ViewPagerAdapter(this, fragmentList)
   }
}

Первый фрагмент.kt (SecondFragment.kt и ThirdFragment.kt похожи на FirstFragment.kt)

class FirstFragment : Fragment() {

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

    companion object{
        fun newInstance() = FirstFragment()
    }
}

ViewPagerAdapter.kt

class ViewPagerAdapter(fa:FragmentActivity, private val fragments:ArrayList<Fragment>): FragmentStateAdapter(fa) {

    override fun getItemCount(): Int = fragments.size

    override fun createFragment(position: Int): Fragment = fragments[position]

}

Некоторые другие полезные ресурсы:

Как мы можем использовать этот тип реализации с ViewBinding?

Maulik Dodia 31.12.2020 21:31

@MaulikDodia ничего особенного не требуется, используйте как обычно, см. документы: developer.android.com/topic/libraries/view-binding

user158 04.01.2021 01:47

Я дам ссылку на док. Спасибо!

Maulik Dodia 05.01.2021 14:52

Вот мое решение (Android Studio 3.6):

В приложении/build.gradle:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { transitive = true; }
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.1.0-beta01'
    implementation 'org.altbeacon:android-beacon-library:2.16.3'
    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
    implementation 'androidx.viewpager2:viewpager2:1.0.0-beta05'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation "androidx.core:core-ktx:+"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

Здесь моя активность:

import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;

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

import com.myproject.android.R;
import com.myproject.android.adapter.CustomFragmentStateAdapter;
import com.myproject.android.ui.fragment.BluetoothPageFragment;
import com.myproject.android.ui.fragment.QrPageFragment;

public class QRBluetoothSwipeActivity extends AppCompatActivity {
    private ViewPager2 myViewPager2;
    private CustomFragmentStateAdapter myAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        // Make sure this is before calling super.onCreate
        setTheme(R.style.AppTheme); // show splash screen
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qr_bluetooth_swipe_activity);
        init();
    }

    private void init() {
        List<Fragment> fragmentList = new ArrayList<Fragment>();
        QrPageFragment m1 = new QrPageFragment();
        BluetoothPageFragment m2 = new BluetoothPageFragment();
        myViewPager2 = findViewById(R.id.viewPager2);
        fragmentList.add(m2);
        fragmentList.add(m1);
        myAdapter = new CustomFragmentStateAdapter(this, fragmentList);
        myViewPager2.setAdapter(myAdapter);
    }
}

Здесь макет:

<?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 = ".ui.actviity.SplashDelayActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id = "@+id/viewPager2"
        android:layout_width = "match_parent"
        android:layout_height = "match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Вот мой Кустомфрагментстатеадаптер

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

import org.jetbrains.annotations.NotNull;

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

public class CustomFragmentStateAdapter extends FragmentStateAdapter {
    private List<Fragment> listFragment = new ArrayList<>();

    public CustomFragmentStateAdapter(FragmentActivity fa, List<Fragment> list) {
        super(fa);
        listFragment = list;
    }

    @NotNull
    @Override
    public Fragment createFragment(int position) {
        return listFragment.get(position);
    }

    @Override
    public int getItemCount() {
        return 2;
    }
}

А вот и мои фрагменты:

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

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

import com.myproject.android.R;

public class BluetoothPageFragment extends Fragment {

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

}

и второй фрагмент:

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

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

import com.myproject.android.R;

public class QrPageFragment extends Fragment {

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

}

В результате теперь я использую androidx.viewpager2.widget.ViewPager2 со своими собственными фрагментами.

И это работа!!!

Хороший.

P.S. Другая реализация:

public class CustomFragmentStateAdapter extends FragmentStateAdapter {
    private ArrayList<Fragment> arrayList = new ArrayList<>();

    public CustomFragmentStateAdapter (FragmentActivity fa) {
        super(fa);
    }

    public void addFragment(Fragment fragment) {
        arrayList.add(fragment);
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        // return your fragment that corresponds to this 'position'
        return arrayList.get(position);
    }
}

И используйте так (в действии):

 myViewPager2 = findViewById(R.id.viewPager2);
        myAdapter = new CustomFragmentStateAdapter (this);
        myAdapter.addFragment(new QrPageFragment());
        myAdapter.addFragment(new BluetoothPageFragment());
        myViewPager2.setAdapter(myAdapter);

Я использовал его с recyclerview изображений внутри FragmentPagerAdapter, и он слишком медленный, как свинья, бегущая с лошадьми.

Gilberto Ibarra 26.02.2020 08:27

Это нет правильно. Обратный вызов createFragmentFragmentStateAdapter должен (как говорит метод) create a Fragment. А не дать тот, который уже создан и валяется. Используя свой подход, вы портите работу FragmentManager's.

Bartek Lipinski 11.03.2020 17:46

@BartekLipinski. Итак, как я «могу динамически изменять коллекцию фрагментов во время выполнения, и ViewPager2 будет правильно отображать измененную коллекцию». как говорит документация?

ramaral 08.05.2020 16:03

Вот что я сделал, чтобы реализовать ViewPager2 с TabLayout с полным примером 3 фрагментов:

Макет содержит ViewPager2 с TabLayout:

 <LinearLayout
        xmlns:android = "http://schemas.android.com/apk/res/android"
        android:layout_width = "0dp"
        android:layout_height = "0dp"
        android:orientation = "vertical"
        app:layout_constraintBottom_toBottomOf = "parent"
        app:layout_constraintEnd_toEndOf = "parent"
        app:layout_constraintStart_toStartOf = "parent"
        app:layout_constraintTop_toBottomOf = "@+id/include3">

        <com.google.android.material.tabs.TabLayout
            android:id = "@+id/tab_layout"
            android:layout_width = "match_parent"
            android:background = "@color/colorPrimary"
            app:tabTextColor = "@color/tab_dismiss_color"
            app:tabSelectedTextColor = "@color/green"
            android:layout_height = "wrap_content" />

        <View
            android:layout_width = "match_parent"
            android:layout_height = "1dp"
            android:layout_gravity = "bottom"
            android:background = "#e4e4e4" />

        <androidx.viewpager2.widget.ViewPager2
            android:id = "@+id/pager"
            android:layout_width = "match_parent"
            android:layout_height = "0dp"
            android:layout_weight = "1" />
    </LinearLayout>

запустите ViewPager2 и установите имя вкладки:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_report);
        ButterKnife.bind(this);
        actionBarTitleId.setText(R.string.reports);

        viewPager.setAdapter(new ViewPagerAdapter(this));

        new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
            switch (position) {
                case 0:
                    tab.setText(R.string.financial_duty_str);
                    break;
                case 1:
                    tab.setText(R.string.financial_unpaid_str);
                    break;
                case 2:
                    tab.setText(R.string.financial_paid_str);
                    break;

            }
        }).attach();

    }

Адаптер ViewPager2:

public class ViewPagerAdapter extends FragmentStateAdapter {


    public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position) {
            case 0:
                return new FinancialFragment();
            case 1:
                return new FinancialUnPaidFragment();
            case 2:
                return new FinancialPaidFragment();
            default:
                return null;

        }
    }

    @Override
    public int getItemCount() {
        return 3;
    }

используется зависимость:

implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.google.android.material:material:1.1.0-alpha10'

Как вы решили переключение между вкладками? Например, когда я выбрал 1, загружаются 1 и 2. Как-то перескакивает с 1 на 2...

Josef 13.12.2021 21:01

Как им пользоваться объяснил очень доходчиво. Позвольте мне дать небольшие, но очень важные советы и подробности о ViewPager2, особенно если это внутри фрагмента о том, как предотвратить утечку памяти.

  1. Не используйте конструктор, который принимает фрагмент, особенно если вы используете TabLayout.

    public FragmentStateAdapter(@NonNull Fragment fragment) {
        this(fragment.getChildFragmentManager(), fragment.getLifecycle());
    }
    

потому что есть риск утечки памяти, как описано здесь

вместо этого используйте FragmentManager и LifeCycle. И не отправляйте lifeCycle фрагмента в качестве аргумента, используйте жизненный цикл viewLifeCycleOwner, потому что viewLifeCycleOwner представляет жизненный цикл фрагмента view.

 class NavigableFragmentStateAdapter(
    fragmentManager: FragmentManager,
    lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {


}

и установить переходник с onCreateView

viewPager.adapter =
            NavigableFragmentStateAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
  1. Если вы используете TabLayout, когда ViewPager2 находится внутри фрагмента, не забудьте отключить TabLayout и установить адаптер ViewPager2 как

отсоединить TabLayoutMediator, поскольку он вызывает утечку памяти, когда он находится во фрагменте

https://stackoverflow.com/questions/61779776/leak-canary-detects-memory-leaks-for-tablayout-with-viewpager2

ViewPager2 внутри утечки фрагмента после замены фрагмента, в котором он находится, с помощью навигации по компоненту навигации

TabLayoutMediator(tabLayout, viewPager2, tabConfigurationStrategy).detach()     
viewPager2.adapter = null

внутри onDestroyView метод Fragment

  1. Если вы хотите использовать страницы ViewPager как navHostFragment с компонентами навигации для возврата к дочерним фрагментам, зарегистрируйте FragmentTransactionCallback в FragmentStateAdapter как

    private val fragmentTransactionCallback =
        object : FragmentStateAdapter.FragmentTransactionCallback() {
            override fun onFragmentMaxLifecyclePreUpdated(
                fragment: Fragment,
                maxLifecycleState: Lifecycle.State
            ) = if (maxLifecycleState == Lifecycle.State.RESUMED) {
    
                // This fragment is becoming the active Fragment - set it to
                // the primary navigation fragment in the OnPostEventListener
                OnPostEventListener {
                    fragment.parentFragmentManager.commitNow {
                        setPrimaryNavigationFragment(fragment)
                    }
                }
            } else {
                super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
            }
        }
    
    init {
        // Add a FragmentTransactionCallback to handle changing
        // the primary navigation fragment
        registerFragmentTransactionCallback(fragmentTransactionCallback)
    }
    

Кроме того, если вы хотите увидеть несколько примеров использования ViewPager2 с компонентами навигации, модулями динамических функций и комбинированием с BottomNavigationView, вы можете ознакомиться с учебными пособиями в этом репозитории github..

Сначала я создал пейджер просмотра, а затем перешел на пейджер просмотра 2, используя инструкции, приведенные в: https://developer.android.com/training/animation/vp2-миграция

Это правильная реализация!

typealias FragmentBuilder = () -> Fragment

class MyAdapter(
    fragmentManager: FragmentManager, 
    lifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, lifecycle) {

    private val fragmentBuilders = mutableListOf<FragmentBuilder>()

    fun add(fragmentBuilder: FragmentBuilder) {
        fragmentBuilders.add(fragmentBuilder)
    }

    /**
     * Dynamic replacement of fragments
     */
    fun set(position: Int, fragmentBuilder: FragmentBuilder) {
        fragmentBuilders[position] = fragmentBuilder
    }

    override fun getItemCount() = fragmentBuilders.size

    override fun createFragment(position: Int) = fragmentBuilders[position].invoke()

}

Даже не благодарите)

можете ли вы помочь мне с реализацией жизненного цикла, так как я не могу найти какой-либо ресурс, касающийся обработки жизненных циклов.....

Mohit Yadav 06.07.2021 17:33

вот реализация версии kotlin ответа @sushildlh в Kotlin.
в этом коде я реализую viewpager2 с recyclerview для просмотра изображений. также я использую привязку просмотра

food_details.xml

<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 = ".presentation.recipeitem.RecipeDetailsFragment">


    <androidx.viewpager2.widget.ViewPager2
        android:id = "@+id/viewPager2"
        android:layout_width = "0dp"
        android:layout_height = "300dp"
        app:layout_constraintEnd_toEndOf = "parent"
        app:layout_constraintStart_toStartOf = "parent"
        app:layout_constraintTop_toTopOf = "parent"
        tools:src = "@tools:sample/avatars" />
...

фрагмент, который раздувает этот xml

@AndroidEntryPoint
class RecipeDetailsFragment : Fragment(R.layout.fragment_recipe_details) {

private val viewModel: RecipeItemViewModel by viewModels()
private val viewBinding: FragmentRecipeDetailsBinding by viewBinding()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    bindRecipeData(//object of data that is to be desplayed)
}

private fun bindRecipeData(recipeDetailedInfo: RecipeDetailedInfo?) {
    recipeDetailedInfo?.let {
        with(viewBinding) {
            viewPager2.adapter = ViewPagerAdapter(it.images)
            viewPager2.setPageTransformer(ZoomOutPageTransformer())
            lifecycleScope.launchWhenCreated {
                delay(500)
                viewPager2.setCurrentItem(if (it.images.size >= 2) 1 else 0, true)
            }
        }
    }
}....

во фрагменте я создаю объект адаптера и отправляю напрямую список строк, содержащих URL-адреса изображений

вот адаптер viewpager, который в основном является обычным адаптером recyclerview

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.blogspot.soyamr.recipes2.databinding.ImageviewBinding
import com.squareup.picasso.Picasso

class ViewPagerAdapter(private val images: List<String>) :
    RecyclerView.Adapter<ViewPagerAdapter.ImageViewHolder>() {


    class ImageViewHolder(private val imageViewBinding: ImageviewBinding) :
        RecyclerView.ViewHolder(imageViewBinding.root) {
        fun bind(imageLink: String) {
            Picasso.get().load(imageLink).into(imageViewBinding.root)
        }

        companion object {
            fun from(parent: ViewGroup): ImageViewHolder {
                val itemBinding =
                    ImageviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
                return ImageViewHolder(itemBinding)
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
        return ImageViewHolder.from(parent)
    }

    override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
        holder.bind(images[position])
    }

    override fun getItemCount(): Int = images.size

}

в recyclerview я раздуваю этот imageview.xml

<?xml version = "1.0" encoding = "utf-8"?>
<com.google.android.material.imageview.ShapeableImageView xmlns:android = "http://schemas.android.com/apk/res/android"
    android:layout_width = "match_parent" android:layout_height = "match_parent"
    android:scaleType = "centerCrop"
    android:theme = "@style/roundedImageView"
    />

вы используете свое сложное представление 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:id = "@+id/mainConstraintLayout"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent">

    <androidx.viewpager2.widget.ViewPager2
        android:id = "@+id/mainViewPager"
        android:layout_width = "match_parent"
        android:layout_height = "0dp"
        app:layout_constraintBottom_toBottomOf = "parent"
        app:layout_constraintEnd_toEndOf = "parent"
        app:layout_constraintStart_toStartOf = "parent"
        app:layout_constraintTop_toBottomOf = "@+id/mainTabLayout">

    </androidx.viewpager2.widget.ViewPager2>

    <com.google.android.material.tabs.TabLayout
        android:id = "@+id/mainTabLayout"
        android:layout_width = "match_parent"
        android:layout_height = "@dimen/toolbar_height"
        android:background = "@color/brown_normal"
        app:layout_constraintEnd_toEndOf = "parent"
        app:layout_constraintStart_toStartOf = "parent"
        app:layout_constraintTop_toTopOf = "parent"
        app:tabIndicatorColor = "@color/yellow_light"
        app:tabIndicatorHeight = "3dp"
        app:tabSelectedTextColor = "@color/yellow_light"
        app:tabTextColor = "@color/yellow_normal"
        tools:ignore = "SpeakableTextPresentCheck" />
</androidx.constraintlayout.widget.ConstraintLayout>

Адаптер:

public class MainToolsAdapter extends FragmentStateAdapter
{
    // The quantity of tab is fixed
    private static final int FRAGMENT_COUNT = 2;
    // Titles for each tab    
    private final String[] titles = new String[FRAGMENT_COUNT];

    public MainToolsAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, Context context)
    {
        super(fragmentManager, lifecycle);
        // Load titles for tab from resourses
        titles[0] = context.getResources().getString(R.string.tab_1);
        titles[1] = context.getResources().getString(R.string.tab_2);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position)
    {
        // Create fragments according to position
        if (position == 0)
        {
            return new FragmentTabOne();
        }
        return new FragmentTabTwo();
    }

    @Override
    public int getItemCount()
    {
        return FRAGMENT_COUNT;
    }

    public String getItemTitle(int position)
    {
        if (position == 0)
        {
            return titles[0];
        }
        return titles[1];
    }
}

Основное действие OnCreate:

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

    // Create adapter for ViewPager
    mainToolsAdapter = new MainToolsAdapter(getSupportFragmentManager(), getLifecycle(), this);

    // Set adapter to the ViewPager
    viewPager = findViewById(R.id.mainViewPager);
    viewPager.setAdapter(mainToolsAdapter);

    tabLayout = findViewById(R.id.mainTabLayout);

    // Create the TabLayoutMediator to asociate ViewPager2 to TabLayout
    TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.TabConfigurationStrategy()
    {
        @Override
        public void onConfigureTab(@NonNull TabLayout.Tab tab, int position)
        {
            // When a tab is created this is called, then we can set tab properties, in this case the text
            tab.setText(mainToolsAdapter.getItemTitle(position));
        }
    });
    // After configure we need to realice attach then Tab and ViewPager2 are asociated
    tabLayoutMediator.attach();
}

градиент:

plugins {
    id 'com.android.application'
}

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.xxxx.yyyy"
        minSdk 19
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies
{
    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')

    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    testImplementation 'junit:junit:4.13.2'

    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

Это довольно много кода без пояснений! Некоторые комментарии и/или вспомогательный текст были бы полезны, особенно для того, чтобы помочь людям понять и найти собственные решения.

Andrew 23.10.2021 12:01

@andrew Я добавил больше комментариев в код для большей ясности

EverCpp 24.10.2021 05:47

На всякий случай можно прослушать события ViewPager2.OnPageChangeCallback:

private final ViewPager2.OnPageChangeCallback onPageChangeListener = new ViewPager2.OnPageChangeCallback() {

    /**
     * This method will be invoked when the current page is scrolled, either as part
     * of a programmatically initiated smooth scroll or a user initiated touch scroll.
     * @param position             Position index of the first page currently being displayed.
     *                             Page position+1 will be visible if positionOffset is nonzero.
     * @param positionOffset       Value from [0, 1) indicating the offset from the page at position.
     * @param positionOffsetPixels Value in pixels indicating the offset from position.
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        super.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }

    /**
     * This method will be invoked when a new page becomes selected.
     * Animation is not necessarily complete.
     * @param position             Position index of the first page currently being displayed.
     *                             Page position+1 will be visible if positionOffset is nonzero.
     */
    @Override
    public void onPageSelected (int position) {
        super.onPageSelected(position);
        if (position == 2) { // SomeFragment
            Log.d(LOG_TAG, "ViewPager2.onPageSelected( " + position + " )");
            SomePagerAdapter adapter = (SomePagerAdapter) viewpager.getAdapter();
            if (adapter != null) {
                SomeFragment fragment = (SomeFragment) adapter.getItem(position);
                fragment.onLateInit();
            }
        }
    }
};

Для применения с ViewPager2.registerOnPageChangeCallback():

viewpager.registerOnPageChangeCallback(this.onPageChangeListener);

FragmentStateAdapter должен содержать ArrayList<Fragment> mItems, чтобы можно было получить доступ к экземплярам, ​​которые создаются заранее. SomeFragment предоставляет метод public void onLateInit(), потому что @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState), возможно, нельзя использовать (зависит от Fragment).

public Fragment getItem(int position) {
    return this.mItems.get(position);
}

Подобно этому, можно решить проблему, связанную с тем, что Fragment создается задолго до того, как можно будет правильно инициализировать его представление, например. с данными, введенными в предыдущий Fragment. Это может быть не оптимально для большого количества Fragment (...), но для некоторых это работает довольно хорошо.

Другие вопросы по теме