Я хочу использовать com.google.android.material.tabs.TabLayout
компонент с новой ViewPager
реализацией androidx.viewpager2.widget.ViewPager2
Android. Однако метод setupWithViewPager(..)
, предоставленный TabLayout
, поддерживает только старую реализацию ViewPager
. Есть ли способ легко привязать TabLayout
к компоненту ViewPager2
?
Как использовать TabLayout с ViewPager2 в Android
Ответы (9)
Вы должны использовать это TabLayoutMediator
, которое имитирует tabLayout.setupWithViewPager()
и устанавливает ViewPager2
с Tablayout
. В противном случае вам придется написать свой собственный адаптер, который объединит обе стороны.
Это код в котлине будет выглядеть так:
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = tabTitles[position]
}.attach()
TabLayoutMediator(tabLayout, viewPager2) { tab, position -> viewPager2.setCurrentItem(tab.position, true) if (position == 1) tab.setText("Test2") if (position == 0) tab.setText("Test1") }.attach()
- person taranjeetsapra; 13.04.2019
TabLayoutMediator
присутствует в implementation 'com.google.android.material:material:1.1.0-alpha08'
, но студия Android по умолчанию импортировала версию 1.0.0
, которой не было.
- person ansh sachdeva; 24.08.2019
viewPager.setCurrentItem(tab.position, true)
не нужен и вызывает выбор 2-й вкладки, а страницы всегда пролистываются!
- person ericn; 15.05.2020
viewPager.setCurrentItem(tab.position, true)
, будут отображаться вкладки, но у нас будет недостаточно контроля над фрагментом (например, мы не будем фильтровать список в RecyclerView
). Итак, это следует добавить.
- person CoolMind; 03.02.2021
onCreateView
, убедитесь, что viewPager != null
или получите NPE.
- person CoolMind; 03.02.2021
ОБНОВИТЬ
проверьте это Создавайте пролистывающие представления с вкладками с помощью 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.OnConfigureTabCallback
в TabLayoutMediator.TabConfigurationStrategy
- person Almighty; 08.09.2019
AppViewPagerAdapter
?
- person crmepham; 08.09.2019
AppViewPagerAdapter
, подобный этому github.com/RathodNilesh14/TabLayout-with-viewpager2-/blob/
- person AskNilesh; 09.09.2019
viewPager.setCurrentItem(tab.position, true)
. Это дает больше контроля над фрагментами.
- person CoolMind; 03.02.2021
Ни хаков, ни расширений, ни 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));
}
});
Инициализируйте объект TabLayoutMediator
с помощью объекта TabLayout
, ViewPager2
, autoRefresh
- логического типа и объекта OnConfigurationChangeCallback
.
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager2, true, new TabLayoutMediator.OnConfigureTabCallback() {
@Override
public void onConfigureTab(TabLayout.Tab tab, int position) {
// position of the current tab and that tab
}
});
Наконец, просто вызовите attach()
к объекту TabLayoutMediator
, чтобы подключить макет вкладки к окну просмотра: -
tabLayoutMediator.attach();
autoRefresh
- ключ, если установлен в true
- (по умолчанию установлено значение true)
RECREATES
все вкладкиtabLayout
, еслиnotifyDataSetChanged
вызывается в окне просмотраadapter
.
Используйте содержимое 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"))
setupWithViewPager(ViewPager2, List)
в наличии - у меня есть только setupWithViewPager(ViewPager)
- person Bartek Pacia; 20.01.2020
Если вы кодируете на 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();
}
}
Я использую свою реализацию 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);
}
}
Я также использовал настройку адаптера фрагмента в другом классе, где я передал контекст конструктору для разных вкладок
Если заголовок вашей вкладки происходит от 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)
}
}
}
Убедитесь, что вы используете 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()