Использование ViewPager2 в Android

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

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

Я ищу подсказку, а не пример.

13 ответов

Решение

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

ViewPager2

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

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

Изменения API

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

ОБРАЗЕЦ КОДА

расположение

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

деятельность

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

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

теперь нам нужно использовать ViewPager2.OnPageChangeCallback() чтобы получить событие Swipe 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);
        }
    });

мы можем установить ориентацию, используя myViewPager2.setOrientation()

ОБРАЗЕЦ КОДА

За HORIZONTAL Orientation использование

myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);

За VERTICAL Orientation использование

myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

Мы можем использовать notifyDataSetChanged так же, как мы используем в 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);
        }
    });

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

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

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

Образцы

  • ViewPager2 с представлениями - показывает, как настроить ViewPager2 с представлениями в виде страниц.
  • ViewPager2 с фрагментами - показывает, как настроить ViewPager2 с фрагментами в качестве страниц.
  • ViewPager2 с изменяемой коллекцией (представления) - демонстрирует использование ViewPager2 с представлениями в качестве страниц и изменениями в адаптере страницы.
  • ViewPager2 с изменяемой коллекцией (фрагменты) - демонстрирует использование ViewPager2 с фрагментами в качестве страниц и мутациями в адаптере страницы.
  • ViewPager2 с TabLayout (Views) - показывает, как настроить ViewPager2 с представлениями в виде страниц и связать его с 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)
   }
}

FirstFragment.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]

}

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

Использование ViewPager2 в Android

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

Изменения API

FragmentStateAdapter заменяет FragmentStatePagerAdapter

RecyclerView.Adapter заменяет PagerAdapter

registerOnPageChangeCallback заменяет addPageChangeListener

Проще говоря, они делают так, чтобы адаптер View Pager работал как Recycle View Adapter.

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

Вот пример 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 с TabLayout с полным Examble из 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'

На всякий случай можно послушать 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);

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

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

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

Как его использовать, объяснено очень четко. Позвольте мне дать небольшие, но очень важные советы и подробности о 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, поскольку он вызывает утечку памяти, когда находится во фрагменте

/questions/54834633/viewpager2-vnutri-fragmenta-utekaet-posle-zamenyi-fragmenta-v-kotorom-on-nahodit

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

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

внутри onDestroyView метод фрагмента

  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.

вот реализация котлинской версии ответа @sushildlh в Котлине.
в этом коде я реализую 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 класс 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
{
    private static final int FRAGMENT_COUNT = 2;

    private final String[] titles = new String[FRAGMENT_COUNT];

    public MainToolsAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, Context context)
    {
        super(fragmentManager, lifecycle);
        titles[0] = context.getResources().getString(R.string.tab_basics);
        titles[1] = context.getResources().getString(R.string.tab_combat);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position)
    {
        if(position == 0)
        {
            return new FragmentBasics();
        }
        return new FragmentCombat();
    }

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

    // Dialog launcher
    mainToolsAdapter = new MainToolsAdapter(getSupportFragmentManager(), getLifecycle(), this);

    viewPager = findViewById(R.id.mainViewPager);
    viewPager.setAdapter(mainToolsAdapter);

    tabLayout = findViewById(R.id.mainTabLayout);

    TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.TabConfigurationStrategy()
    {
        @Override
        public void onConfigureTab(@NonNull TabLayout.Tab tab, int position)
        {
            tab.setText(mainToolsAdapter.getItemTitle(position));
        }
    });
    tabLayoutMediator.attach();
}

gradle:

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

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

В app/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>

Вот мой CustomFragmentStateAdapter

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 с моими нестандартными фрагментами.

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

Приятно.

PS Еще одна реализация:

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

В Kotlin FragmentStateAdapter с ViewPager2 Основная причина миграции заключается в том, что ViewPager2 получает активную поддержку разработки, а ViewPager — нет. Однако ViewPager2 также предлагает несколько других конкретных преимуществ. FragmentStateAdapter в ViewPager2 немного отличается от ViewPager следующим образом:1] Вместо реализации getCount() реализуйте getItemCount()2] Вместо реализации getItem(int position) реализуйте createFragment(int position)3] Сигнатура конструктора отличается добавив аргумент жизненного цикла, который также должен быть включен в super. Поэтому замените свой ViewPagerFragmentAdapter ниже

Котлин ПейджерАдаптер

      class SurveyPagerAdapter(
private val fragmentList: MutableList< Fragment>,
fragment: FragmentManager,
lifecycle: Lifecycle) : FragmentStateAdapter(fragment,lifecycle){
override fun getItemCount(): Int = fragmentList.size
override fun createFragment(position: Int): Fragment {
    return fragmentList[position]
}
fun getFragmentName(position: Int) = fragmentList[position]
fun addFragment(fragment: Fragment) {
    fragmentList.add(fragment)
    notifyDataSetChanged()
}}

Kotlin Activity *Просто добавьте метод в onCreate() *

      private fun setUpViewPager(){

    val fragmentList : MutableList< Fragment> = mutableListOf()
    fragmentList.add(SurveyPageOneFragment.newInstance(true))
    fragmentList.add(SurveyPageTwoFragment.newInstance(true))
    fragmentList.add(SurveyPageThreeFragment.newInstance(true))
    fragmentList.add(SurveyPageFourFragment.newInstance(true))
    fragmentList.add(SurveyPageFiveFragment.newInstance(true))

    surveyPagerAdapter = SurveyPagerAdapter(fragmentList,supportFragmentManager,lifecycle)
    binding.viewPager.adapter = surveyPagerAdapter
    binding.dotsIndicator.attachTo(binding.viewPager)

}

Котлин Фрагмент

      class SurveyPageOneFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    arguments?.let {
    }
}
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.fragment_survey_page_one, container, false)
}
companion object {
    @JvmStatic
    fun newInstance(isMyBoolean: Boolean) = SurveyPageOneFragment().apply {
        val fragment = SurveyPageOneFragment()
        arguments = Bundle().apply {
            putBoolean("REPLACE WITH A STRING CONSTANT", isMyBoolean)
        }
        fragment.arguments
        return fragment
    }
}}

xml-код в viewPager2

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

Библиотека точечного индикатора//kotlin dot indicator implementation 'com.tbuonomo:dotsindicator:4.3'

      <com.tbuonomo.viewpagerdotsindicator.DotsIndicator
                android:id="@+id/dots_indicator"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:dotsColor="@color/colorSurveyEditGreenLight"
                app:dotsCornerRadius="8dp"
                app:dotsSize="16dp"
                app:dotsSpacing="4dp"
                app:dotsWidthFactor="2.5"
                app:selectedDotColor="@color/white"
                app:progressMode="true"
                />

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

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

}

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

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

Я лично использовал ViewPager2 внутри фрагмента. Вот как я это делаю. Пример кода на GitHub [https://github.com/codebyjames/Example-Using-ViewPager2-Slide-Page-Adapter]

Первый в onCreate

              // pager adapter
        val pagerAdapter = ScreenSlidePageAdapter(this@ManagerFragment)
        viewPager.adapter = pagerAdapter
        viewPager.setPageTransformer(ZoomOutPageTransformer())

Адаптер для ViewPager

      class ScreenSlidePageAdapter(val fragment: Fragment): FragmentStateAdapter(fragment) {

val fragments = listOf(WalkThroughFragment(), PermissionsFragment(), DatastoreFragment())

override fun getItemCount(): Int {
    return fragments.size
}

override fun createFragment(position: Int): Fragment {
    return fragments[position]
}

}

Преобразование для ViewPager2 — опционально

      class ZoomOutPageTransformer : ViewPager2.PageTransformer {

override fun transformPage(view: View, position: Float) {
    view.apply {
        val pageWidth = width
        val pageHeight = height
        when {
            position < -1 -> { // [-Infinity,-1)
                // This page is way off-screen to the left.
                alpha = 0f
            }
            position <= 1 -> { // [-1,1]
                // Modify the default slide transition to shrink the page as well
                val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position))
                val vertMargin = pageHeight * (1 - scaleFactor) / 2
                val horzMargin = pageWidth * (1 - scaleFactor) / 2
                translationX = if (position < 0) {
                    horzMargin - vertMargin / 2
                } else {
                    horzMargin + vertMargin / 2
                }

                // Scale the page down (between MIN_SCALE and 1)
                scaleX = scaleFactor
                scaleY = scaleFactor

                // Fade the page relative to its size.
                alpha = (MIN_ALPHA +
                        (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))
            }
            else -> { // (1,+Infinity]
                // This page is way off-screen to the right.
                alpha = 0f
            }
        }
    }
}

}

Мой макет ManagerFragment (который содержит ViewPager2)

      <androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Другие вопросы по тегам