Я хочу использовать компонент com.google.android.material.tabs.TabLayout с новой ViewPager реализацией Android androidx.viewpager2.widget.ViewPager2. Однако метод setupWithViewPager(..), предоставляемый TabLayout, поддерживает только старую реализацию ViewPager. Есть ли способ легко привязать TabLayout к ViewPager2 компоненту?




Вы должны использовать этоTabLayoutMediator, который имитирует tabLayout.setupWithViewPager() и устанавливает ViewPager2 с Tablayout. В противном случае вам придется писать собственный адаптер, который будет совмещать обе стороны.
Его код будет выглядеть так в Котлине
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = tabTitles[position]
}.attach()
Код для использования этого TabLayoutMediator выглядит следующим образом: TabLayoutMediator(tabLayout, viewPager2) { tab, position -> viewPager2.setCurrentItem(tab.position, true) if (position == 1) tab.setText("Test2") if (position == 0) tab.setText("Test1") }.attach()
любой, кто читает это, должен дважды проверить версию библиотеки материалов своего приложения. TabLayoutMediator присутствует в implementation 'com.google.android.material:material:1.1.0-alpha08' , но студия Android по умолчанию импортировала для меня версию 1.0.0, в которой этого не было.
Как я могу установить точки на TabLayout (например, в карусели) вместо чисел на вкладках?
viewPager.setCurrentItem(tab.position, true) не требуется и приводит к выбору 2-й вкладки и постоянному пролистыванию страниц!
@ericn, ты прав, брат, установка текущего элемента выполняется автоматически.
@IgorGanapolsky Привет, вам просто нужно app:tabBackground = "@drawable/tab_selector", и вы tab_selector должны предоставить разные возможности рисования для выбранного и невыбранного случая
@anshsachdeva Я использую последнюю версию 1.3.0-alpha02, но не могу найти класс TablayoutMediator
@ericn, если удалить viewPager.setCurrentItem(tab.position, true), вкладки будут отображаться, но у нас не будет достаточного контроля над фрагментом (например, не будет фильтровать список в RecyclerView). Значит, надо добавить.
Если вы вызываете этот метод из onCreateView, убедитесь, что viewPager != null или получите NPE.
Забыл об этом attach() и потратил впустую 20 минут.
Инициализируйте объект 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чтобы подключить tablayout к viewpager: -
tabLayoutMediator.attach();
autoRefresh - ключ, если установлено значение true -- (по умолчанию установлено значение true )
RECREATESall the tabs of thetabLayoutifnotifyDataSetChangedis called to the viewpageradapter.
Используйте содержимое TabLayoutMediator.java
check this Create swipe views with tabs using 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 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()
}
}
ОБНОВИТЬ
Если вы используете 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()
OUTPUT
любой, кто читает это, должен дважды проверить версию библиотеки материалов своего приложения. эта версия работала для меня, но студия Android по умолчанию импортировала версию 1.0.0, в которой этого не было
@anshsachdeva то же самое, я не могу найти класс TabLayoutMediator в com.google.android.material:material:1.0.0-alpha08, com.google.android.material:material:1.0.0-alpha09.
@Seyid-KananBagirov присутствует в реализации «com.google.android.material:material:1.1.0-alpha08» (обратите внимание на версию 1.1.0-alpha08, а не 1.0.0)
в 1.1.0-alpha10 они переименовали TabLayoutMediator.OnConfigureTabCallback в TabLayoutMediator.TabConfigurationStrategy
Я получаю неразрешенную ссылку для AppViewPagerAdapter?
@crmepham вам нужно создать AppViewPagerAdapter класс, подобный этому github.com/RathodNilesh14/TabLayout-with-viewpager2-/blob/…
@ Всемогущий, мне нужно просто переименовать его, и все?
@androiddeveloper да, проверьте обновленный ответ
@NileshRathod Спасибо. Почему мы должны указывать TabConfigurationStrategy? Он помечен как ненулевой, но на самом деле мне не нужно ничего делать для обратного вызова. Для меня это пусто...
также tabl;ayoutmediatar теперь не поддерживается, мы должны использовать какой-то другой метод для подключения tablayout и viewpager 2
В принятом ответе у нас также есть viewPager.setCurrentItem(tab.position, true). Это дает больше контроля над фрагментами.
новый способ подключения viewpager2 и tablayout также решает проблему, из-за которой мы получим сбой, если все еще используем старый способ подключения viewpager2 и tablayout, когда я включил ProGuard
Вы можете использовать функцию расширения 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"))
Какую зависимость мне нужно добавить, чтобы использовать это расширение?
Нет никакой зависимости, кроме Kotlin, чтобы использовать функции расширения :)
Да, знаю. Я имел в виду, какой артефакт Android KTX я должен добавить, чтобы использовать эту функцию расширения. Также у меня нет setupWithViewPager(ViewPager2, List) в наличии – у меня есть только setupWithViewPager(ViewPager)
Вы должны реализовать ViewPager2 (androidx.viewpager2:1.0.0) и компоненты matarial (com.google.android.material:material:1.2.0-alpha03) как артефакт, чтобы иметь возможность использовать эту функцию расширения.
И я также рекомендую использовать ViewPager2 вместо старого компонента ViewPager.
Да, у меня были включены эти артефакты, и эта функция расширения была недоступна. Я каким-то образом заработал, не используя эту функцию расширения. Спасибо, в любом случае.
Я нахожусь на 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>
Таким образом, адаптер отвечает за предоставление корректных фрагментов активности. Вам нужно будет расширить Фрагментстатеадаптер, что я сделал очень просто, как показано ниже (это частный класс, потому что он объявлен в моем 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);
Я добавил фрагменты в свой viewpager. (Поскольку я жестко запрограммировал фрагменты в своем адаптере, вы должны использовать список и что-то вроде метода addFragment или что-то в этом роде)
Затем с
TabLayout tabLayout = findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("Bar"));
tabLayout.addTab(tabLayout.newTab().setText("Foo"));
Я добавляю две вкладки в свой 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));
}
});
Хорошее решение! Работает как положено.
Хорошая работа. Оцените проект на GitHub. Спасибо!
Спасибо за ответ, не могли бы вы сказать мне, приведет ли перелистывание между вкладками к воссозданию фрагментов?
Я использую свою реализацию «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)
}
}
}
Если вы программируете на 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();
}
}
Спасибо за это! Я люблю случайные фрагменты кода без четкого указания, куда их поместить, так же, как и следующий парень, но этот ответ был для меня недостающей частью.
Ok. пока ты счастлив. Я счастлив. Что касается полного репетиторства, мы можем организовать класс. напишите мне сообщение!
Убедитесь, что вы используете 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()
это очень просто:
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" />
Ваши макеты готовы, фрагменты готовы и адаптеры готовы. Все, что вам нужно сейчас, это настроить прослушиватель событий на
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.
Возможно, этот пример ViewPager2 от Google поможет: github.com/android/views-widgets-samples/tree/master/ViewPager2