如何在 Android 中使用 TabLayout 和 ViewPager2

Posted

技术标签:

【中文标题】如何在 Android 中使用 TabLayout 和 ViewPager2【英文标题】:How to use TabLayout with ViewPager2 in Android 【发布时间】:2019-08-17 17:28:49 【问题描述】:

我想将com.google.android.material.tabs.TabLayout 组件与Android 的新ViewPager 实现androidx.viewpager2.widget.ViewPager2 一起使用。但是,TabLayout 提供的setupWithViewPager(..) 方法只支持旧的ViewPager 实现。有没有办法轻松地将TabLayout 绑定到ViewPager2 组件?

【问题讨论】:

也许这个来自 Google 的 ViewPager2 示例会有所帮助:github.com/android/views-widgets-samples/tree/master/ViewPager2 【参考方案1】:

您的布局已准备好,片段已准备好,适配器已准备好。 你现在只需要设置一个事件监听器来

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。

【讨论】:

【参考方案2】:

您必须使用 this TabLayoutMediator 模仿 tabLayout.setupWithViewPager() 并设置 ViewPager2Tablayout。否则,您将不得不编写自己的适配器来结合双方。

它的代码在 Kotlin 中会是这样的

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 studio 默认为我导入了1.0.0 版本,它没有这个。 如何在 TabLayout 上设置点(如轮播),而不是选项卡中的数字? viewPager.setCurrentItem(tab.position, true) 是不必要的,会导致选择第二个选项卡并始终滑动页面! @ericn 你是对的兄弟,设置当前项目是自己完成的。【参考方案3】:

很简单:

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_
    android:layout_
    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_
    android:layout_
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/tabLayout" />

【讨论】:

【参考方案4】:

确保使用 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()

【讨论】:

【参考方案5】:

如果您使用 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();
  

【讨论】:

谢谢!我喜欢随机的代码片段,没有明确的方向来放置它们,就像下一个人一样,但这个答案对我来说是缺失的部分。 好的。只要你开心。我很高兴。关于全程辅导,我们可以安排上课。给我留言!【参考方案6】:

如果您的标签标题来自string.xml。 您可以将fragmenttitle 放在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)
        
    

【讨论】:

【参考方案7】:

我在实现 'com.google.android.material:material:1.2.1' 上运行我的,也没有使用 tabLayoutMediator。对于初学者,https://developer.android.com/jetpack/androidx/migrate/class-mappings 已针对 androidX 中的 TabLayout 进行了更改,因此请确保在导入语句中使用 com.google.android.material.tabs.TabLayout。 下面是我实现解决方案的其余部分: viewPager和TabLayout的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_
    android:layout_
    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_
        android:layout_/>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager"
        app:layout_anchor="@id/tabs"
        app:layout_anchorGravity="bottom"
        android:layout_
        android:layout_/>

</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);
    

我也在不同的类中使用了片段适配器设置,我将上下文传递给不同选项卡的构造函数

【讨论】:

【参考方案8】:

没有 hack、没有扩展、没有 TabLayoutMediator

我在implementation 'com.google.android.material:material:1.2.0-alpha02' 上并执行以下操作而不需要 TabLayoutMediator。 相反,我使用here 描述的方法将 TabLayout 与 ViewPager2 链接起来。我还向 github here 添加了一个工作示例。我想我已经将解决​​方案最小化为一个最小的工作示例。我将解释重要的部分。

将元素添加到模板中

首先,我们需要将 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_
    android:layout_
    tools:context=".MainActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_
        android:layout_>

        <LinearLayout
            android:orientation="vertical"
            android:layout_
            android:layout_>

            <com.google.android.material.tabs.TabLayout
                android:id="@+id/tabLayout"
                android:layout_
                android:layout_/>
            <androidx.viewpager2.widget.ViewPager2
                android:id="@+id/pager"
                android:layout_
                android:layout_/>

        </LinearLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

将适配器连接到 viewpager

因此适配器负责为活动提供正确的片段。 您必须扩展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);

我已将片段添加到我的浏览器中。 (因为我在适配器中对 Fragments 进行了硬编码,所以您应该使用列表和类似“addFragment”方法之类的东西)

标签布局

然后用

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));
    
);

【讨论】:

不错的解决方案!按预期工作。 干得好。欣赏 GitHub 上的项目。谢谢! 感谢您的回答,您能告诉我在选项卡之间滑动会导致片段被重新创建吗?【参考方案9】:

使用TabLayoutViewPager2autoRefresh 的对象初始化TabLayoutMediator 对象——布尔类型和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 )

RECREATES 如果notifyDataSetChanged 被调用到viewpager adapter,则tabLayout 的所有选项卡。

使用TabLayoutMediator.java的内容

【讨论】:

【参考方案10】:

更新

检查这个Create swipe views with tabs using ViewPager2

这是更新的答案如何在 Android 中使用 TabLayout 和 ViewPager2

现在我们不需要从 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_
        android:layout_>

    <com.google.android.material.appbar.AppBarLayout
            android:layout_
            android:layout_
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_
                android:layout_
                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_
                android:layout_/>
    </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_
            android:layout_
    />


</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()

输出

【讨论】:

任何阅读本文的人都应该仔细检查他们应用程序的材料库版本。这个版本对我有用,但是android studio默认导入了1.0.0版本,没有这个 @anshsachdeva 相同,我在 com.google.android.material:material:1.0.0-alpha08、com.google.android.material:material:1.0.0-alpha09 中找不到 TabLayoutMediator 类。 @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 @androiddeveloper 是的,请查看更新后的答案【参考方案11】:

您可以使用 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 组件。

以上是关于如何在 Android 中使用 TabLayout 和 ViewPager2的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Android 代码中的 TabLayout 更改选定的选项卡文本颜色?

一起Talk Android吧(第三百七十六回:如何使用TabLayout)

在android设计支持TabLayout中更改标签文本的字体

如何在 Android 中以编程方式创建 TabLayout

如何在 CollapsingToolbarLayout 中使用带有 Toolbar 的 TabLayout?

如何在Android中左对齐TabLayout的标签文本