(三十三)TabLayout

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、概述

我们在应用 ViewPage 的时候,经常会使用自定义控件或是 TabPageIndicator 来与其配合,达到很漂亮的效果。但是 TabPageIndicator 是第三方的,而且比较老了,当然了现在很多大神都已经开始自己写TabPageIndicator来满足自己的需求。

在2015年的 google 大会上,google 发布了新的 Android Support Design 库,里面包含了几个新的控件,其中就有一个 TabLayout,它就可以完成 TabPageIndicator 的效果,而且还是官方的,最好的是它可以兼容到2.2以上版本(包括2.2)。

二、TabLayout 的使用

1.Demo

效果:
这里写图片描述

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.xiaoyue.tablayout.MainActivity">

   <android.support.design.widget.TabLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <android.support.design.widget.TabItem
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="选项卡"/>
       <android.support.design.widget.TabItem
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="选项卡"/>
       <android.support.design.widget.TabItem
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="选项卡"/>
   </android.support.design.widget.TabLayout>

</android.support.constraint.ConstraintLayout>

直接在布局文件中引用即可。

2.属性设置

1.Tab的模式:(默认是 fixed,固定的,标签很多时候会被挤压,不能滑动。)
app:tabMode=”scrollable”

2.改变选中字体的颜色 (觉得选中的颜色不好看 )
app:tabSelectedTextColor=”@android:color/holo_orange_light”

3.改变未选中字体的颜色
app:tabTextColor=”@color/colorPrimary”

4.改变指示器下标的颜色
app:tabIndicatorColor=”@android:color/holo_orange_light”

5.改变整个 TabLayout 的颜色
app:tabBackground=”color”

6.改变 TabLayout 内部字体大小>app:tabTextAppearance=”@android:style/TextAppearance.Holo.Large”//设置文字的外貌

7.改变指示器下标的高度
app:tabIndicatorHeight=”15dp”
注:不想要下标可以设置高度为 0。

8.设置 Tab 内部的子控件的 Padding:
app:tabPadding=”xxdp”
app:tabPaddingTop=”xxdp”
app:tabPaddingStart=”xxdp”
app:tabPaddingEnd=”xxdp”
app:tabPaddingBottom=”xxdp”

9.设置整个 TabLayout 的 Padding:
app:paddingEnd=”xxdp”
app:paddingStart=”xxdp”

内容的显示模式
app:tabGravity=”center”//居中,如果是fill,则是充满

10.Tab的宽度限制
设置最大的tab宽度:
app:tabMaxWidth=”xxdp”
设置最小的tab宽度:
app:tabMinWidth=”xxdp”

11.Tab的“Margin”
TabLayout开始位置的偏移量:
app:tabContentStart=”100dp”

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.xiaoyue.tablayout.MainActivity">

   <android.support.design.widget.TabLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:tabMode="scrollable"
       app:tabSelectedTextColor="#00FFFF"
       app:tabTextColor="@color/colorAccent"
       app:tabIndicatorColor="@color/colorPrimary"
       app:tabIndicatorHeight="15dp">

       <android.support.design.widget.TabItem
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"

        ... 多个 TabItem

       <android.support.design.widget.TabItem
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="选项卡"/>
   </android.support.design.widget.TabLayout>

</android.support.constraint.ConstraintLayout>

效果:
这里写图片描述

3.添加图标

setIcon(int resId)

MainActivity :

public class MainActivity extends AppCompatActivity {

    TabLayout tabLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tabLayout = findViewById(R.id.tab_layout);

        tabLayout.getTabAt(0).setIcon(R.mipmap.ic_launcher);
    }
}

TabLayout 通过 getTabAt 获取到要修改的 Tab,然后调用 setIcon 方法进行设置图标。

效果:
这里写图片描述

注:这里图标是在文字上方,采用的 design 包版本不同,可能会有不同的显示效果。

要是不想要这种的,也可以通过 setCustomView 方法使用自己定义 Tab 的样式。

MainActivity :

public class MainActivity extends AppCompatActivity {

    private final String[] titels = { "选项卡1", "选项卡2", "选项卡3" };

    TabLayout tabLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tabLayout = findViewById(R.id.tab_layout);

        setupTabIcons();
    }

    private void setupTabIcons() {
        tabLayout.getTabAt(0).setCustomView(getTabView(0));
        tabLayout.getTabAt(1).setCustomView(getTabView(1));
        tabLayout.getTabAt(2).setCustomView(getTabView(2));
    }

    public View getTabView(int position) {
        View view = LayoutInflater.from(this).inflate(R.layout.item_tab, null);
        TextView txt_title = (TextView) view.findViewById(R.id.txt_title);
        txt_title.setText(titels[position]);
        ImageView img_title = (ImageView) view.findViewById(R.id.img_title);
        img_title.setImageResource(R.mipmap.ic_launcher);
        return view;
    }
}

item_tab.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <ImageView
        android:id="@+id/img_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/txt_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

item_tab.xml 可以根据需要进行自定义。

三、配合 ViewPager 使用

1.demo

效果:
这里写图片描述

MainActivity :

public class MainActivity extends AppCompatActivity {

    private final String[] titels = { "选项卡1", "选项卡2", "选项卡3" };

    private TabLayout tabLayout;
    private ViewPager viewPager;
    private MyAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tabLayout = findViewById(R.id.tab_layout);
        viewPager = findViewById(R.id.viewpager);
        adapter = new MyAdapter(getSupportFragmentManager());

        viewPager.setAdapter(adapter);
        //设置联动效果
        tabLayout.setupWithViewPager(viewPager);
        //设置标题样式(必须在 setupWithViewPager 之后)
        setupTabIcons();

    }

    /**
     * 设置标题使用自定义布局
     */
    private void setupTabIcons() {
        tabLayout.getTabAt(0).setCustomView(getTabView(0));
        tabLayout.getTabAt(1).setCustomView(getTabView(1));
        tabLayout.getTabAt(2).setCustomView(getTabView(2));
    }

    public View getTabView(int position) {
        View view = LayoutInflater.from(this).inflate(R.layout.item_tab, null);
        TextView txt_title = view.findViewById(R.id.txt_title);
        txt_title.setText(titels[position]);
        ImageView img_title = view.findViewById(R.id.img_title);
        img_title.setImageResource(R.mipmap.ic_launcher);
        return view;
    }
}

TabLayout 与 ViewPager 都有设置自身滑动的监听方法,联动就是在两个监听方法中设置另一个变化。setupWithViewPager 方法里面也是这么执行的,谷歌帮我们考虑好了。

activity_main.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_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.xiaoyue.tablayout.MainActivity">

   <android.support.design.widget.TabLayout
       android:id="@+id/tab_layout"
       android:layout_width="match_parent"
       android:layout_height="wrap_content">
   </android.support.design.widget.TabLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v4.view.ViewPager>

</LinearLayout>

布局文件下 TabLayout 下面配置的 Tab,由于 MainActivity 使用 setupWithViewPager 会失效,末尾进行说明。

MyAdapter:

public class MyAdapter extends FragmentPagerAdapter {

    private int[] drawbles = {R.drawable.a, R.drawable.b, R.drawable.c};

    public MyAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {

        ImageFragment fragment = new ImageFragment();
        Bundle bundle = new Bundle();
        bundle.putInt("id", drawbles[position]);
        fragment.setArguments(bundle);

        return fragment;
    }

    @Override
    public int getCount() {
        return drawbles.length;
    }
}

ImageFragment:

public class ImageFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        ImageView imageView = new ImageView(getContext());
        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        imageView.setLayoutParams(layoutParams);
        Bundle bundle=getArguments();
        int id = bundle.getInt("id");
        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        imageView.setImageResource(id);
        return imageView;
    }
}

2.setupWithViewPager 的坑

setupWithViewPager 有一些比较坑的地方,他在代码中重新对 TabLayout 的 Tab 进行了设置。

TabLayout 的 setupWithViewPager 调用 TabLayout 的 setPagerAdapter,调用 TabLayout 的 setPagerAdapter 调用 调用 TabLayout 的 populateFromPagerAdapter。(不同版本有所差异)

TabLayout 的 populateFromPagerAdapter:

    void populateFromPagerAdapter() {
        removeAllTabs();

        if (mPagerAdapter != null) {
            final int adapterCount = mPagerAdapter.getCount();
            for (int i = 0; i < adapterCount; i++) {
                addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
            }

            // Make sure we reflect the currently set ViewPager item
            if (mViewPager != null && adapterCount > 0) {
                final int curItem = mViewPager.getCurrentItem();
                if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
                    selectTab(getTabAt(curItem));
                }
            }
        }
    }

在 populateFromPagerAdapter 方法中,他会先移除 TabLayout 所有的 Tab,再获取 ViewPager 下 PagerAdapter 的 的 title 进行设置(默认是 null),所以使用 setupWithViewPager 会导致布局文件里面的 Tab 跟在 setupWithViewPager 之前设置的 Tab 失效。

解决方法:
1.重写 ViewPager 下 PagerAdapter 的 getPageTitle 方法。
2.在 setupWithViewPager 之后进行 Tab 的设置。

另外,有时候 ViewPager 第一次选中的不是第一个,populateFromPagerAdapter 中会对 TabLayout 进行重新选择,相当于 ViewPager 点击了两次。

版权声明:本文为qq_18983205原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_18983205/article/details/78620232