Как использовать TabLayout с ViewPager2 в Android
Я хочу использовать com.google.android.material.tabs.TabLayout
компонент с новым Android ViewPager
реализация androidx.viewpager2.widget.ViewPager2
, Тем не менее setupWithViewPager(..)
метод предоставлен TabLayout
поддерживает только старый ViewPager
реализация. Есть ли способ связать TabLayout
к ViewPager2
Компонент легко?
12 ответов
Вы должны использовать это TabLayoutMediator
это имитирует tabLayout.setupWithViewPager()
и устанавливает ViewPager2
с Tablayout
, В противном случае вам придется написать свой собственный адаптер, который объединит обе стороны.
Ни хаков, ни расширений, ни TabLayoutMediator
Я на implementation 'com.google.android.material:material:1.2.0-alpha02'
и выполните следующие действия без использования TabLayoutMediator. Вместо этого я связываю TabLayout с ViewPager2, используя метод, описанный здесь. Я также добавил сюда рабочий пример на github. Думаю, я свел решение к минимальному рабочему примеру. Я объясню важные моменты.
Добавление элементов в шаблон
Сначала нам нужно добавить в макет TabLayout и ViewPager2. Я разместил их здесь в LinearLayout и CoordinatorLayout, но вы, конечно, можете делать все, что захотите.
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Подключение адаптера к вьюпейджеру
Таким образом, адаптер отвечает за предоставление правильных фрагментов деятельности. Вам нужно будет расширить FragmentStateAdapter, что я сделал очень просто, как показано ниже (это частный класс, потому что он объявлен здесь в моем MainActivity.java):
private class ViewStateAdapter extends FragmentStateAdapter {
public ViewStateAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) {
// Hardcoded in this order, you'll want to use lists and make sure the titles match
if (position == 0) {
return new BarFragment();
}
return new FooFragment();
}
@Override
public int getItemCount() {
// Hardcoded, use lists
return 2;
}
}
Затем я могу подключить свой собственный адаптер к ViewPager, как показано ниже:
FragmentManager fm = getSupportFragmentManager();
ViewStateAdapter sa = new ViewStateAdapter(fm, getLifecycle());
final ViewPager2 pa = findViewById(R.id.pager);
pa.setAdapter(sa);
Я добавил фрагменты в свой просмотрщик. (Поскольку я жестко запрограммировал фрагменты в своем адаптере, вы должны использовать список и что-то вроде метода addFragment или что-то в этом роде)
TabLayout
Затем с
TabLayout tabLayout = findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("Bar"));
tabLayout.addTab(tabLayout.newTab().setText("Foo"));
Я добавляю две вкладки в свой TabLayout, показывая заголовки, но пока не позволяя переключаться на фрагменты.
Подключение TabLayout к адаптеру
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
pa.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
Это должно быть довольно просто. Пользователь щелкает вкладку, я получаю позицию в моем обратном вызове и просто устанавливаю текущий элемент адаптера в эту позицию.
Изменить вкладку при смахивании
Наконец, мы возвращаемся, когда пользователь проводит фрагмент, чтобы установить правильный элемент вкладки как выбранный.
pa.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
tabLayout.selectTab(tabLayout.getTabAt(position));
}
});
ОБНОВИТЬ
установите флажок Создавать пролистывающие представления с вкладками с помощью ViewPager2
Вот обновленный ответ Как использовать TabLayout с ViewPager2 в Android
Теперь нам не нужно создавать класс из TabLayoutMediator
Используйте ниже dependencies
implementation 'com.google.android.material:material:1.1.0-alpha08'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
ОБРАЗЕЦ КОДА
Макет XMl
<?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>
Деятельность
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()
}
}
ОБНОВИТЬ
Если вы используете implementation 'com.google.android.material:material:1.1.0-alpha10'
затем используйте код ниже
TabLayoutMediator(tabs, viewpage,
TabLayoutMediator.TabConfigurationStrategy { tab, position ->
when (position) {
0 -> { tab.text = "TAB ONE"}
1 -> { tab.text = "TAB TWO"}
}
}).attach()
ВЫХОД
Инициализировать TabLayoutMediator
объект с объектом TabLayout
, ViewPager2
, autoRefresh
- логический тип и объект OnConfigurationChangeCallback
,
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager2, true, new TabLayoutMediator.OnConfigureTabCallback() {
@Override
public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
// position of the current tab and that tab
}
});
Наконец, просто позвоните attach()
к TabLayoutMediator
Объект для соединения табуляции с viewpager: -
tabLayoutMediator.attach();
autoRefresh
- ключ, если установлено true
- (по умолчанию установлено значение true)
RECREATES
все вкладкиtabLayout
еслиnotifyDataSetChanged
вызывается в viewpageradapter
,
Используйте содержимое TabLayoutMediator.java
Вы можете использовать функцию расширения Kotlin:
fun TabLayout.setupWithViewPager(viewPager: ViewPager2, labels: List<String>) {
if (labels.size != viewPager.adapter?.itemCount)
throw Exception("The size of list and the tab count should be equal!")
TabLayoutMediator(this, viewPager,
TabLayoutMediator.TabConfigurationStrategy { tab, position ->
tab.text = labels[position]
}).attach()
}
И назовите это:
tabLayout.setupWithViewPager(viewPager, listOf("Tab A", "Tab B"))
это очень просто:
код Java:
tabLayout=findViewById(R.id.tabLayout);
viewPager=findViewById(R.id.viewPager);
viewPager.setAdapter(new ViewPagerAdapter(this));
new TabLayoutMediator(tabLayout, viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
if (position==0){
tab.setText(R.string.photos);
tab.view.setBackground(getResources().getDrawable(R.drawable.ic_rectangle_1345));
}
else {
tab.setText(R.string.videos);
}
}
}).attach();
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
tab.view.setBackground(getResources().getDrawable(R.drawable.ic_rectangle_1345));
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
tab.view.setBackgroundColor(Color.TRANSPARENT);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
адаптер:
public class ViewPagerAdapter extends FragmentStateAdapter {
public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
@NonNull
@Override
public Fragment createFragment(int position) {
if (position==0) {
return new PhotosFragment();
}
else {
return new VideosFragment();
}
}
@Override
public int getItemCount() {
return 2;
}}
XML:
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/tool_bar_bg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/collection_bar" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
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/tabLayout" />
Если вы кодируете на JAVA. Вы можете использовать следующий адаптер для viewPager2:
public class ViewPagerAdapter extends FragmentStateAdapter {
private static final int CARD_ITEM_SIZE = 10;
public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
@NonNull @Override public Fragment createFragment(int position) {
return CardFragment.newInstance(position);
}
@Override public int getItemCount() {
return CARD_ITEM_SIZE;
}
}
И в mainActivity вам нужно иметь что-то, встроенное в следующее:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.view_pager);
tabLayout = findViewById(R.id.tabs);
viewPager.setAdapter(new ViewPagerAdapter(this));
new TabLayoutMediator(tabLayout, viewPager,
new TabLayoutMediator.TabConfigurationStrategy() {
@Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
tab.setText("Tab " + (position + 1));
}
}).attach();
}
}
Готовы ваши макеты, готовы фрагменты и готовы адаптеры. Все, что вам нужно сейчас, это настроить прослушиватель событий на
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
Это должно связать макет вкладки с вашим ViewPager2.
Если название вашей вкладки происходит от
string.xml
.
Вы можете поставить
fragment
с участием
title
вместе в
List
затем установите заголовок
TabLayoutMediator
. Это поможет вам легко изменить порядок, удалить или добавить новый фрагмент
class MyFragment : Fragment() {
override fun onCreateView(...): View? {
...
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = pagerAdapter.getTabTitle(position)
}.attach()
}
private inner class PagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
val pages = listOf(
Pair(HomeFragment.newInstance(), R.string.home),
Pair(GraphFragment.newInstance(), R.string.graph),
Pair(SettingFragment.newInstance(), R.string.setting),
)
override fun createFragment(position: Int): Fragment {
return pages[position].first
}
override fun getItemCount(): Int {
return pages.count()
}
fun getTabTitle(position: Int): String {
return getString(pages[position].second)
}
}
}
Я использую свою реализацию com.google.android.material:material:1.2.1 и не использую tabLayoutMediator. Для начала, https://developer.android.com/jetpack/androidx/migrate/class-mappings изменились для TabLayout в androidX, поэтому обязательно используйте com.google.android.material.tabs.TabLayout в своем операторе импорта. Вот остальная часть моей реализации решения: объявление макета Xml для viewPager и TabLayout
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/tan_background"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<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"/>
</LinearLayout>
и файл активности
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the content of the activity to use the activity_main.xml layout file
setContentView(layout.activity_main);
// Find the view pager that will allow the user to swipe between fragments
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
SimpleFragmentPagerAdapter adapter = new SimpleFragmentPagerAdapter(this,getSupportFragmentManager());
// Set the adapter onto the view pager
viewPager.setAdapter(adapter);
// Find the tab layout that shows the tabs
TabLayout tabLayout = findViewById(R.id.tabs);
// Connect the tab layout with the view pager. This will
// 1. Update the tab layout when the view pager is swiped
// 2. Update the view pager when a tab is selected
// 3. Set the tab layout's tab names with the view pager's adapter's titles
// by calling onPageTitle()
tabLayout.setupWithViewPager(viewPager);
}
}
Я также использовал настройку адаптера фрагмента в другом классе, где я передал контекст конструктору для разных вкладок
Вы можете черпать вдохновение из моего примера проекта для создания пользовательских интерфейсов с помощью Viewpager2. я использую
TabLayoutMediator
на всех примерах, Простой и очень полезный. https://github.com/gabriel-TheCode/OnboardingViewPagerExamples .
Убедитесь, что вы используете TabLayoutMediator
after your set adapter to view_pager2
TabLayoutMediator(tab_layout, view_pager2) { tab, position ->
tab.text = fragmentList[position].second // here u can modify you text..
}.attach()