Tablayout的使用

一、前言:

Tablayout继承自HorizontalScrollView,用作页面切换指示器,因使用简便功能强大而广泛使用在App中。

github代码直通车

先上效果图:分别为设置tab属性、去掉指示线、设置指示线长度、设置图标tab、超出屏幕滚动tab

图片.png

二、使用:

1、TabLayout的一些基本属性:

app:tabIndicatorColor :指示线的颜色
app:tabIndicatorHeight :指示线的高度
app:tabSelectedTextColor : tab选中时的字体颜色
app:tabTextColor:tab未被选中时的字体颜色
app:tabMode="scrollable" : 默认是fixed,固定的;scrollable:可滚动的

2、高度设置为0:

给tabIndicatorHeight属性设置0dp,或者给tabSelectedTextColor属性设置透明,就不显示指示线了。

app:tabIndicatorHeight ="0dp"

还可以设置成透明色:

app:tabIndicatorColor="@color/transparent"

3、设置默认图标:

图片.png

still easy,Tablayout自带了setIcon()方法设置图标资源,不过这中效果很别扭,脸被拉长了。不服,就自己造一个啊,造就造!
tabLayout.getTabAt(i).setText(titles[i]).setIcon(pics[i]);

4、自己造tab样式(推荐这种方式):

具体的详细使用查看: 四、自定义tabLayout布局(推荐这个)

图片.png

1、创建图标和文字布局(下方还可以定义下划线):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="48dp"
    android:gravity="center">

    <ImageView
        android:id="@+id/imageview"
        android:layout_gravity="center"
        android:layout_width="24dp"
        android:layout_height="24dp" />

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="14sp"
        android:layout_marginLeft="8dp"/>
</LinearLayout>

2、这里是图标居左,那我要改为图标上下左右呢?都自定义view了,你上天都行。

    /**
     * 设置自定义位置图标
     */
    private void setCustomIcon() {
        //创建多少个tab
        tabLayout2 = (TabLayout) findViewById(R.id.tablayout2);
        for(int i=0;i<titles.length;i++){
            tabLayout2.addTab(tabLayout2.newTab());
        }

        for(int i=0;i<titles.length;i++){
            //给每个tab设置自定义布局
            tabLayout2.getTabAt(i).setCustomView(makeTabView(i));
        }
    }

    /**
     * 引入布局设置图标和标题(引入布局的使用)
     * @param position
     * @return
     */
    private View makeTabView(int position){
        View tabView = LayoutInflater.from(this).inflate(R.layout.tab_text_icon,null);
        TextView textView = tabView.findViewById(R.id.textview);
        ImageView imageView = tabView.findViewById(R.id.imageview);
        textView.setText(titles[position]);
        imageView.setImageResource(pics[position]);

        return tabView;
    }

5、tab数量太多,超出屏幕,就像今日头条的分类一样,那就挤在一起了啊。不怕,我们有app:tabMode="scrollable" 属性,让Tablayout变得可滚动,可超出屏幕。

图片.png

布局引入:

    <android.support.design.widget.TabLayout
        android:id="@+id/tablayout3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabSelectedTextColor="@color/green"
        android:layout_marginTop="20dp"
        app:tabMode="scrollable"
        android:background="@color/white"/>

三、自己写的demo(推荐这个):

图片.png

注意:下划线颜色设置透明色,就可以使用自定义的下划线颜色

//下划线颜色设置透明色,就会隐藏掉
 app:tabIndicatorColor = "@color/transparent"

1、TabLayoutMediator 找不到

TabLayoutMediator 是一个非常新的类,它是在 Android Jetpack 库的 1.2.0 版本中引入的,并且只与 ViewPager2 兼容。

如果您想使用 TabLayoutMediator 类来将 TabLayout 和 ViewPager2 集成在一起,您需要确保使用了最新版本的 Android Jetpack 库。
同时,您需要在项目的构建脚本中添加以下依赖项:

dependencies {
    // ...
    implementation 'com.google.android.material:material:1.3.0' // 替换为最新版本
}

然后,在代码中,您可以按照以下方式使用 TabLayoutMediator:

2、activity

package com.kana.moonlight


import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.kana.moonlight.databinding.ActivityMainBinding
import com.kana.moonlight.databinding.SearchTabItemBinding

class MainActivity : AppCompatActivity() {
    val tabNames = arrayListOf<String>("测试1", "测试2", "测试3", "测试4", "测试5")
    val childFragments = arrayListOf<Fragment>()
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        childFragments.add(FirstFragment("第一个页面"))
        childFragments.add(FirstFragment("第二个页面"))
        childFragments.add(FirstFragment("第三个页面"))
        childFragments.add(FirstFragment("第四个页面"))
        childFragments.add(FirstFragment("第五个页面"))

        //初始化tab
        initTabButton()
    }

    private fun initTabButton() {
        //创建对应的tabLayout
        tabNames.forEachIndexed { _index, it ->
            binding.tablayout.addTab(binding.tablayout.newTab().setText(it))
        }
        //设置adapter
        binding.viewpager.offscreenPageLimit = 1
        binding.viewpager.adapter = fragmentAdapter


        //暂时用不到(TabLayoutMediator中已经包含左右滑动状态选中了)
        /* binding.viewpager.registerOnPageChangeCallback(object :
             ViewPager2.OnPageChangeCallback() {
             override fun onPageSelected(position: Int) {
             }
         })*/

        /**
         * 选中状态监听
         */
        binding.tablayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {
                //选中状态
                updateTabView(tab, true)
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
                //未选中状态
                updateTabView(tab, false)
            }

            override fun onTabReselected(tab: TabLayout.Tab?) {

            }
        })

        /**
         * 关联tablayout和viewpager
         */
        TabLayoutMediator(binding.tablayout, binding.viewpager, false, true) { tab, position ->
            //设置自定义布局
            tab.customView = getTabView(tabNames[position])
        }.attach()

        //选中第一个tab
        binding.tablayout.getTabAt(0)?.select()
    }

    /**
     * 创建自定义布局,并设置tabName
     */
    private fun getTabView(tab: String): View {
        val binding = DataBindingUtil.inflate<SearchTabItemBinding>(
            layoutInflater,
            R.layout.search_tab_item,
            null,
            true
        )
        //设置tabName的值
        binding.tabName.text = tab
        return binding.root
    }

    /**
     * 更新tabName字体颜色、大小等和下划线是否显示
     */
    private fun updateTabView(tab: TabLayout.Tab?, isSelect: Boolean) {
        Log.d("lyy", "updateTabView: tab=${tab?.position}, isSelectd=$isSelect")
        tab?.customView?.let {
            //tab文字
            val tabName = it.findViewById<TextView>(R.id.tab_name)
            //下划线
            val ivIndicator = it.findViewById<ImageView>(R.id.iv_indicator)

            if (isSelect) {
                //设置字体大小、加粗等
                val paint = tabName.paint
                paint.isFakeBoldText = true
                tabName.textSize = 16f
                //设置字体颜色
                tabName.setTextColor(ContextCompat.getColor(this, R.color.teal_700))
                //设置下划线可见
                ivIndicator.visibility = View.VISIBLE

            } else {
                //设置字体大小、加粗等
                val paint = tabName.paint
                paint.isFakeBoldText = false
                tabName.textSize = 14f
                //设置字体颜色
                tabName.setTextColor(ContextCompat.getColor(this, R.color.read_bg_8))
                //设置下划线不可见
                ivIndicator.visibility = View.INVISIBLE
            }
        }


    }

    /**
     * viewPager adapter
     */
    private val fragmentAdapter: FragmentStateAdapter by lazy {
        object : FragmentStateAdapter(this) {
            override fun getItemCount(): Int {
                return tabNames.size
            }

            override fun createFragment(position: Int): Fragment {
                return childFragments[position]
            }
        }
    }
}

3、activity_main

<?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">


    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="tab切换"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textSize="22sp"
        android:textColor="#f00"
        />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tablayout"
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:layout_marginTop="10dp"
        android:shadowColor="#f0000000"
        android:shadowDx="0"
        android:shadowDy="2"
        app:tabGravity="fill"
        app:tabIndicatorFullWidth="false"
        app:tabIndicatorHeight="0dp"
        app:tabMinWidth="30dp"
        app:tabMode="scrollable"
        app:tabPaddingEnd="18dp"
        app:tabPaddingStart="12dp"
        app:tabRippleColor="@android:color/transparent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_title"
        />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="2dp"
        app:layout_constraintTop_toBottomOf="@+id/tablayout"
        app:layout_constraintBottom_toBottomOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

4、FirstFragment

class FirstFragment(var tabName:String?="") : Fragment() {

    private var _binding: FragmentFirstBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        _binding = FragmentFirstBinding.inflate(inflater, container, false)
        return binding.root

    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textviewFirst.text = tabName

    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

5、fragment_first

<?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=".FirstFragment">

    <TextView
        android:id="@+id/textview_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_first_fragment"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="100dp"
        />


</androidx.constraintlayout.widget.ConstraintLayout>

6、自定义 search_tab_item.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">
    <data></data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tab_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="全部"
            android:textColor="@color/black_3"
            android:textSize="16sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/iv_indicator"
            android:layout_width="20dp"
            android:layout_height="4dp"
            android:layout_marginTop="2dp"
            android:background="@drawable/btn_shap_19"
            android:visibility="invisible"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tab_name" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

7、下划线:btn_shap_19

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <gradient android:startColor="#FA731D"
       android:endColor="#FE9D04" android:angle="180" />

    <corners android:radius="@dimen/ui_dp_2"/>
</shape>

四、自定义tabLayout布局

效果图:


效果图.png
dependencies {
    // 不包含tablayout和viewPage2的绑定方法
    implementation 'com.google.android.material:material:1.0.0' 
}

使用这种方式的原因:我想升级material的版本号,发现和项目中的很多jar包冲突,资源文件报错等,改了好久,还是不行,就放弃升级material的版本号,采用手动绑定tablayout和viewPage2的关联。

注意:下划线颜色设置透明色,就可以使用自定义的下划线颜色

//下划线颜色设置透明色,就会隐藏掉
 app:tabIndicatorColor = "@color/transparent"

特别注意:由于tablayout和viewPage2没有关联,所以点击按钮不会切换,只会显示第一个页面,左右滑动没有问题,所以在此手动切换fragment的跳转。

   //获取对应的下标
   var position= tab?.position ?: 0
    //跳转对应的fragment页面
   binding.viewpager.currentItem = position

1、主页面调用:RankingActivity

/**
 * 最新榜单页面
 */
class RankingActivity : BaseActivity<NewSearchViewModel, ActivityRankingBinding>() {
    val tabNames = arrayListOf<String>("全部", "男频", "女频")
    val childFragments = arrayListOf<Fragment>()
    override fun initView(savedInstanceState: Bundle?) {

        childFragments.add(RankingFragment(1))
        childFragments.add(RankingFragment(2))
        childFragments.add(RankingFragment(3))
        //初始化tab
        initTabButton(mViewBind)
    }

    private fun initTabButton(binding: ActivityRankingBinding) {
        tabNames.forEachIndexed { _index, it ->
            //创建tab
            val tab = binding.tablayout.newTab()
            //自定义布局
            tab.setCustomView(R.layout.search_tab_item)
            tab.customView?.findViewById<TextView>(R.id.tab_name)?.text = it
            binding.tablayout.addTab(tab)
        }

        binding.viewpager.offscreenPageLimit = 1
        binding.viewpager.adapter = fragmentAdapter

        //这个方法必须注册,否则左右滑动,tab没有选中状态
        binding.viewpager.registerOnPageChangeCallback(object :
            ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                //左右滑动时调用
                binding.tablayout.getTabAt(position)?.select()
            }
        })
        //点击时调用
        binding.tablayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab?) {
                //更新按钮选中状态
                updateTabView(tab, true)
               //注意:由于tablayout和viewPage2没有关联,所以点击按钮不会切换,只会显示第一个页面,左右滑动没有问题,所以在此手动切换fragment的跳转。
                //获取对应的下标
                var position= tab?.position ?: 0
                //跳转对应的fragment页面
                binding.viewpager.currentItem = position
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
                updateTabView(tab, false)
            }

            override fun onTabReselected(tab: TabLayout.Tab?) {

            }
        })


        //默认第一个选中
        val tabFirst = binding.tablayout.getTabAt(0)
        tabFirst?.select()
        updateTabView(tabFirst, true)
    }

   /**
     * 更新tab的字体大小、颜色或者下划线显示
     */
    private fun updateTabView(tab: TabLayout.Tab?, isSelect: Boolean) {
        Log.d("home", "updateTabView: tab=${tab?.position}, isSelectd=$isSelect")
        tab?.customView.let {
            val tabName: TextView? = it?.findViewById(R.id.tab_name)
            val ivIndicator: ImageView? = it?.findViewById(R.id.iv_indicator)
         
            if (isSelect) {
                //设置字体类型、大小
                val paint = tabName?.paint
                paint?.isFakeBoldText = true
                tabName?.textSize = 18f
                //下划线显示
                ivIndicator?.visibility = View.VISIBLE
                //设置tab字体颜色
                tabName?.setTextColor(ContextCompat.getColor(this, R.color.color_FF9017))
            } else {
                //设置字体类型、大小
                val paint = tabName?.paint
                paint?.isFakeBoldText = false
                tabName?.textSize = 16f
                //下划线隐藏
                ivIndicator?.visibility = View.INVISIBLE
                //设置tab字体颜色
                tabName?.setTextColor(ContextCompat.getColor(this, R.color.black_3))
            }
        }

    }



    /**
     * viewPager adapter
     */
    private val fragmentAdapter: FragmentStateAdapter by lazy {
        object : FragmentStateAdapter(this) {
            override fun getItemCount(): Int {
                return tabNames.size
            }
            override fun createFragment(position: Int): Fragment {
                return childFragments[position]
            }
        }
    }
}

2、主布局:activity_ranking.xml

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:ignore="UseAppTint"
    >

    <data>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/cl_top"
            android:layout_width="match_parent"
            android:layout_height="@dimen/ui_dp_54"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            >

            <ImageView
                android:id="@+id/iv_back"
                android:layout_width="38dp"
                android:layout_height="28dp"
                android:layout_centerVertical="true"
                android:layout_gravity="center_vertical"
                android:src="@mipmap/back_black"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="@+id/tablayout"
                app:layout_constraintBottom_toBottomOf="@+id/tablayout"
                android:layout_marginStart="@dimen/ui_dp_5"
                android:paddingLeft="@dimen/ui_dp_10"
                android:paddingRight="@dimen/ui_dp_10"
                android:paddingVertical="@dimen/ui_dp_5"
               />
            <com.google.android.material.tabs.TabLayout
                android:id="@+id/tablayout"
                android:layout_width="0dp"
                android:layout_height="44dp"
                android:layout_marginTop="10dp"
                android:shadowColor="#f0000000"
                android:shadowDx="0"
                android:shadowDy="2"
                app:tabGravity="fill"
                app:tabIndicatorFullWidth="false"
                app:tabIndicatorHeight="4dp"
                app:tabMinWidth="30dp"
                app:tabMode="scrollable"
                app:tabPaddingEnd="12dp"
                app:tabPaddingStart="12dp"
                app:tabRippleColor="@android:color/transparent"
                app:layout_constraintStart_toEndOf="@+id/iv_back"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                android:layout_marginBottom="@dimen/ui_dp_8"
                app:tabIndicatorColor = "@color/transparent"
                />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@+id/cl_top"
            app:layout_constraintBottom_toBottomOf="parent"
            />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3、自定义布局:search_tab_item.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">
    <data></data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tab_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="全部"
            android:textColor="@color/black_3"
            android:textSize="@dimen/ui_sp_16"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/iv_indicator"
            android:layout_width="@dimen/ui_dp_20"
            android:layout_height="@dimen/ui_dp_4"
            android:layout_marginTop="@dimen/ui_dp_2"
            android:background="@drawable/btn_shap_19"
            android:visibility="invisible"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tab_name" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4、下划线:btn_shap_19

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <gradient android:startColor="#FA731D"
       android:endColor="#FE9D04" android:angle="180" />

    <corners android:radius="@dimen/ui_dp_2"/>
</shape>

5、Fragment 的页面:RankingFragment

/**
 * 排行榜页面
 * type : 1、全部;2、男频;3、女频
 */
class RankingFragment constructor(typePage: Int? = 0) :
    BaseFragment<MaleFemaleFragmentViewModel, FragmentRankingBinding>()  {
    /**
     * type : 1、全部;2、男频;3、女频
     */
    private var typePage = typePage
    override fun initView(savedInstanceState: Bundle?) {
        if (2== typePage){
            mViewBind.tvTitle.text = "男频页面"
        }else if (3== typePage){
            mViewBind.tvTitle.text = "女频页面"
        }else{
            mViewBind.tvTitle.text = "全部页面"
        }
    }
}

五、tabLayout的对齐方式

1、居中对齐

图片.png

设置以下三条属性即可

app:tabMode="fixed"
app:tabMaxWidth="0dp"
app:tabGravity="fill"

2、左侧开始排,显示不下,左右滑动

图片.png
  app:tabMode="scrollable"
 app:tabGravity="fill"
 app:tabMode="scrollable"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容