Я узнал о ViewPager2 и попытался реализовать его, но не нашел подходящего примера.
Может ли кто-нибудь сказать мне, как я могу его использовать.
Я ищу правильное использование, а не пример.
Тот, кто дал близкие голоса за recommend or find a book, tool, software library, tutorial or other off-site resource Пожалуйста, внимательно прочитайте, что я не ищу, например.
Есть официальный образец: github.com/googlesamples/android-viewpager2
ценный вопрос, выглядит как блог: D
@NikunjParadia Ответ Нилеша Ратода очень ценен и также выглядит как сообщение в блоге. Вопрос как раз простой.
Вот статья о реализации т.лы/3qgj
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 да, вы можете использовать Fragment с ViewPager2, пожалуйста, проверьте мой ответ выше, я добавил пример кода для How use Fragment with ViewPager2
Спасибо, но репозиторий удален.
@CoolMind Какой репозиторий удален?
Вот репо -> github.com/android/views-widgets-samples/tree/master/ViewPager2
Проверить : Переход с ViewPager на ViewPager2
Проверить : Создание прокрутки с вкладками с помощью ViewPager2
Проверьте мой ответ, если вы хотите реализовать карусель с помощью View Pager2
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
Из Документов
New features
API changes
FragmentStateAdapter заменяет FragmentStatePagerAdapterRecyclerView.Adapter заменяет PagerAdapterregisterOnPageChangeCallback заменяет addPageChangeListenerОБРАЗЕЦ КОДА
add the latest
dependenciesforViewPager2
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 ofViewPager2
ОБРАЗЕЦ КОДА
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
notifyDataSetChangedsame as we are using inRecyclerView.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
ViewPagerFragmentAdapterclass which extendsFragmentStateAdapter
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);
}
}
для получения дополнительной информации проверьте это
Новые возможности
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
New features
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);
}
}
ItemDecorator с поведением, совместимым с RecyclerView.MarginPageTransformer введен для предоставления возможности создавать пространство между страницами (за пределами вставки страницы).CompositePageTransformer введен для обеспечения возможности объединения нескольких PageTransformersAPI 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
Круто, у меня не было времени для блога, но обязательно сделаю.
@PratikButani у нас есть новые обновления для ViewPager2 с использованием Version 1.0.0-alpha02
Каждый раз, когда я читаю ваш комментарий, это снова вдохновляет меня писать в блог :D
Хорошее подробное описание для ViewPager2, но, пожалуйста, внесите изменения, эта версия ViewPager2 выпущена только для Android X. поэтому вам нужно добавить зависимость от Android X для успешной компиляции проекта.
Можно ли использовать PagerTitleStrip или PagerTabStrip с ViewPager2 или может есть альтернатива?
Привет @NileshRathod. В обновлении 4 есть ли способ использовать объект ArrayList<Fragment> для динамического создания фрагментов в ViewPagerFragmentAdapter?
@mrisek да, это возможно, пожалуйста, свяжитесь со мной, я пришлю вам пример кода
как автоматически скользить с индикатором?
@MahmoudAyman проверьте этот пост stackoverflow.com/a/45518240/7666442
Как уничтожить представления, такие как функция PagerAdapterdestroyItem, в пользовательском RecyclerView.Adapter?
Я попытался переопределить onViewRecycled, так как override fun onViewRecycled(holder: ViewHolder) { (holder.itemView as ViewGroup).removeAllViews() super.onViewRecycled(holder) } сработало.
@PhaniRithvij нет необходимости уничтожать элемент внутри RecyclerView.Adapter, почему вы хотите уничтожить элемент внутри него?
Потому что я получаю несколько представлений, наложенных друг на друга. Как я делаю holder.view.addView(view_c) в onBindViewHolder
Разве список фрагментов, который у вас есть в ViewPagerFragmentAdapter, не предотвратит переработку?
Как обернуть высоту Android ViewPager2 до высоты текущего элемента?
Я думаю, вы все еще используете старый адаптер, поскольку новый адаптер — FragmentStatePagerAdapter.
@AmirDora нет, я не обновлял свой ответ после 25 ноября 2019 г.
Обновите его, поскольку новую функцию жизненного цикла BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT можно использовать только с FragmentStatePagerAdapter.
почему getItem для возврата фрагмента больше не существует?
Привет @NileshRathod, можно создать ViewPager2 внутри фрагмента, который может размещать другие фрагменты вместо использования Activity?
@Али да, это возможно
У меня возникли проблемы с внедрением viewPage2 в мой фрагмент, который в основном является homeFrag, дайте мне отладить мой код и понять, что мне не хватает. Спасибо @NileshRathod
@Ali Я использую viewpager с TabLayout во фрагменте, я не могу провести пальцем по экрану. он застрял на 1-м фрагменте, как я могу решить эту проблему
@kdblue вы пытались установить пользовательский ввод viewpager.is Enabled = true
@Ali Я решил, что это происходило из-за макета обновления смахивания.
@iamkdblue Я также столкнулся с той же проблемой, но я хочу сохранить макет обновления смахивания, у вас есть идеи, как это исправить?
@Ali viewpager.isUserInoutEnabled у меня не работает
@Nilesh Rathod У меня также есть viewpager2 и tablyout внутри фрагмента, но он не прокручивается.
@Aldor, не могли бы вы поделиться своим кодом, позвольте мне проверить ваш код
@Aldor, какую версию артефакта вы используете в зависимости от вашего приложения build.gradle?
@Nilesh Rathod вот ссылка на мой вопрос. У меня там тоже есть код. stackoverflow.com/questions/62810129/…
Реализация @Ali "androidx.viewpager2:viewpager2:1.0.0"
@NileshRathod, вы создали массив фрагментов, но нигде их не вызываете?
хороший и подробный пост, но можете ли вы добавить, как обрабатывать жизненные циклы в viewpager2, так как я не могу найти никакого решения..?
Все ответы здесь, большое спасибо, я бы хотел, чтобы команда документации Google могла понять, как должно выглядеть руководство по миграции API.
На самом деле теперь есть официальный репозиторий примеров для 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]
}
Некоторые другие полезные ресурсы:
Обучение: https://developer.android.com/training/animation/screen-slide-2
Примечания к выпуску: https://developer.android.com/jetpack/androidx/releases/viewpager2
Средняя статья GDE: Изучение ViewPager2
Как мы можем использовать этот тип реализации с ViewBinding?
@MaulikDodia ничего особенного не требуется, используйте как обычно, см. документы: developer.android.com/topic/libraries/view-binding
Я дам ссылку на док. Спасибо!
Вот мое решение (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, и он слишком медленный, как свинья, бегущая с лошадьми.
Это нет правильно. Обратный вызов createFragmentFragmentStateAdapter должен (как говорит метод) create a Fragment. А не дать тот, который уже создан и валяется. Используя свой подход, вы портите работу FragmentManager's.
@BartekLipinski. Итак, как я «могу динамически изменять коллекцию фрагментов во время выполнения, и ViewPager2 будет правильно отображать измененную коллекцию». как говорит документация?
Вот что я сделал, чтобы реализовать 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...
Как им пользоваться объяснил очень доходчиво. Позвольте мне дать небольшие, но очень важные советы и подробности о 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, поскольку он вызывает утечку памяти, когда он находится во фрагменте
TabLayoutMediator(tabLayout, viewPager2, tabConfigurationStrategy).detach()
viewPager2.adapter = null
внутри onDestroyView метод Fragment
Если вы хотите использовать страницы 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()
}
Даже не благодарите)
можете ли вы помочь мне с реализацией жизненного цикла, так как я не могу найти какой-либо ресурс, касающийся обработки жизненных циклов.....
вот реализация версии 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 Я добавил больше комментариев в код для большей ясности
На всякий случай можно прослушать события 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 (...), но для некоторых это работает довольно хорошо.
Вот объяснение и пример проверки: michaelevans.org/blog/2019/02/07/hands-on-with-viewpager2