Использование 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()
чтобы получить событие SwipeViewPager2
ОБРАЗЕЦ КОДА
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]
}
Некоторые другие полезные ресурсы:
- Справочник по API:https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2
Обучение: https://developer.android.com/training/animation/screen-slide-2
Примечания к выпуску: https://developer.android.com/jetpack/androidx/releases/viewpager2
Средняя статья GDE: Изучение ViewPager2
Использование 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, особенно если он находится внутри фрагмента, о том, как предотвратить утечку памяти.
Не используйте конструктор, который принимает фрагмент, особенно если вы используете
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)
- Если вы используете
TabLayout
когда ViewPager2 находится внутри фрагмента, не забудьте отключить TabLayout и установить адаптер ViewPager2 как
отсоединить TabLayoutMediator, поскольку он вызывает утечку памяти, когда находится во фрагменте
/questions/54834633/viewpager2-vnutri-fragmenta-utekaet-posle-zamenyi-fragmenta-v-kotorom-on-nahodit
TabLayoutMediator(tabLayout, viewPager2, tabConfigurationStrategy).detach()
viewPager2.adapter = null
внутри
onDestroyView
метод фрагмента
Если вы хотите использовать страницы 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" />