Material Design(一)—— TabLayout的使用

Material Design – TabLayout使用
1. 基本使用及最常见用法
2. 在Tablayout显示图标 及 源码解析
3. 自定义布局

1. 基本使用及最常见用法

1) 在build.gradle 添加依赖

implementation 'com.android.support:design:27.1.1'

2) 编写布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        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>

3) 编写Fragment及Activity

Fragment

public class TestFragment extends Fragment {

    public static final String FRAGMENT_TYPE = "fragment_type";
    private String mContent;

    public static Fragment newInstance(String content) {
        Fragment fragment = new TestFragment();
        Bundle bundle = new Bundle();
        bundle.putString(FRAGMENT_TYPE, content);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContent = getArguments().getString(FRAGMENT_TYPE);
    }

    private static final String TAG = "TestFragment";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: "+container);
        //这里的布局只是一个简单的FrameLayout,后面手动添加了一个TextView
        ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.fragment_test, container, false);
        TextView textView = new TextView(getContext());
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.CENTER;
        textView.setText(mContent);
        textView.setLayoutParams(layoutParams);
        layout.addView(textView);

        return layout;
    }
}

PagerAdapter类:注意这里getPageTitle返回的即为TabLayout的标题,至于为什么后面再讲

public class MyPagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> mFragments;
    private String[] mTitles;
    public MyPagerAdapter(FragmentManager fm, List<Fragment> fragments, String[] titles) {
        super(fm);
        this.mFragments = fragments;
        this.mTitles = titles;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }
}

Activity:这里简单的创建了4个TestFragment,关键的一句在于mTabLayout.setupWithViewPager(mViewPager)将TabLayout与ViewPager绑定

public class MainActivity extends AppCompatActivity {


    private String[] mTitles = {"日报","主题","专栏","热门"};

    private List<Fragment> mFragments;
    private TabLayout mTabLayout;
    private ViewPager mViewPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTabLayout = findViewById(R.id.tabLayout);

        mViewPager = findViewById(R.id.viewPager);

        mFragments = new ArrayList<>();
        mFragments.add(TestFragment.newInstance(mTitles[0]));
        mFragments.add(TestFragment.newInstance(mTitles[1]));
        mFragments.add(TestFragment.newInstance(mTitles[2]));
        mFragments.add(TestFragment.newInstance(mTitles[3]));

        mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mFragments, mTitles));
        mViewPager.setOffscreenPageLimit(2);

        mTabLayout.setupWithViewPager(mViewPager);

    }
}

4)运行结果
这里写图片描述
5)自定义TabLayout属性

tabBackground:标签的背景;
tabMode:fixed, 固定标签,tab均分,为默认值;scrollable,可滚动标签。
tabTextColor:标签字体颜色;
tabSelectedTextColor:标签选中字体颜色;
tabIndicatorColor:底部滑动的线条的颜色,默认是colorAccent;
tabIndicatorHeight:底部滑动线条Indicator的高度。

遗憾的是无法调节滑动线条的长度,网上有人使用反射获取属性去调节其高度。

2. 在Tablayout显示图标

想要在TabLayout上显示图标,这时候就需要使用到TabItem了,TabItem是用来配合作为TabLayout的子View使用的。
在布局文件TabLayout标签下添加TabItem

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

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon4"
            android:text="日报"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon3"
            android:text="主题"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon2"
            android:text="专栏"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon1"
            android:text="热门"/>
    </android.support.design.widget.TabLayout>

这时候再点击运行,
你会,咦。。。
怎么没有图标出现
不急不急
mTabLayout.setupWithViewPager(mViewPager)这一行注释掉
替换为以下两行代码

mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));

这时图标就显示出来了。

效果如图

源码解析

为什么要替换为另外两句代码呢?这两句其实就是setUpWithViewPager的核心代码,
那这两句与setUpWithViewPager有什么不同呢?与让我们到源码里面看看吧

    private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,
            boolean implicitSetup) {
        if (mViewPager != null) {
            // If we've already been setup with a ViewPager, remove us from it
            if (mPageChangeListener != null) {
                mViewPager.removeOnPageChangeListener(mPageChangeListener);
            }
            if (mAdapterChangeListener != null) {
                mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
            }
        }

        if (mCurrentVpSelectedListener != null) {
            // If we already have a tab selected listener for the ViewPager, remove it
            removeOnTabSelectedListener(mCurrentVpSelectedListener);
            mCurrentVpSelectedListener = null;
        }

        if (viewPager != null) {
            mViewPager = viewPager;

            // Add our custom OnPageChangeListener to the ViewPager
            if (mPageChangeListener == null) {
                mPageChangeListener = new TabLayoutOnPageChangeListener(this);
            }
            mPageChangeListener.reset();
            viewPager.addOnPageChangeListener(mPageChangeListener);

            // Now we'll add a tab selected listener to set ViewPager's current item
            mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
            addOnTabSelectedListener(mCurrentVpSelectedListener);

            final PagerAdapter adapter = viewPager.getAdapter();
            if (adapter != null) {
                // Now we'll populate ourselves from the pager adapter, adding an observer if
                // autoRefresh is enabled
                setPagerAdapter(adapter, autoRefresh);
            }

            // Add a listener so that we're notified of any adapter changes
            if (mAdapterChangeListener == null) {
                mAdapterChangeListener = new AdapterChangeListener();
            }
            mAdapterChangeListener.setAutoRefresh(autoRefresh);
            viewPager.addOnAdapterChangeListener(mAdapterChangeListener);

            // Now update the scroll position to match the ViewPager's current item
            setScrollPosition(viewPager.getCurrentItem(), 0f, true);
        } else {
            // We've been given a null ViewPager so we need to clear out the internal state,
            // listeners and observers
            mViewPager = null;
            setPagerAdapter(null, false);
        }

        mSetupViewPagerImplicitly = implicitSetup;
    }

可以看到函数里先移除了之前的监听器,然后在将新的监听器添加上去,
这是与我们的做法一致的,那么到底哪里不一样呢?让我们看到调用的setPagerAdapter(adapter, autoRefresh);方法内调用的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));
                }
            }
        }
    }

在这个方法内,他首先将所有tab清除掉了,然后再用for循环一个一个加回来,tab个数取自PagerAdapter,并且title取自PagerAdapter的getPageTitle()方法,这也就说明了前面的疑问,为什么getPageTitle返回的即为TabLayout的标题。

然后他这里没有重新设置图标,只设置了一下标题,这也就是为什么只调用setUpWithViewPager不显示图标的原因了。

如果你想问下有没有什么其他方法?那肯定是有的啊。

我们可以照常调用setUpWithViewPager

不用在布局文件中添加TabItem,而是在代码中设置添加TabItem,记住如果调用了setuPWithViewPager,那么只有在调用它之后的TabItem图标设置才会有效,添加如下代码重新设置图标,这里可以不用设置文字

mTabLayout.getTabAt(0).setText(mTitles[0]).setIcon(R.drawable.icon1);
mTabLayout.getTabAt(1).setText(mTitles[1]).setIcon(R.drawable.icon2);
mTabLayout.getTabAt(2).setText(mTitles[2]).setIcon(R.drawable.icon3);
mTabLayout.getTabAt(3).setText(mTitles[3]).setIcon(R.drawable.icon4);

如果不使用setupWithViewPager呢?

那就手动调用mTabLayout.addTab(mTabLayout.newTab().setText().setIcon())
代码中创建TabItem并配合

mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));

来完成Tablayout与ViewPager的联动

其他操作

显示图标其实还有其他一些骚操作,我们可以看回自己的PagerAdapter类的代码,getPageTitle()方法其实返回的是CharSequence类实例,熟悉继承关系的知道其实String 和 SpannableString 都是继承自CharSequence的,用过SpannableString的可能会知道,SpannableString 是可以实现图标和文字的组合显示的,那我们就可以返回一个包含图标和文字的SpannableString,只调用setupWithViewPager实现图标和文字的显示,感兴趣的可以看我之前关于SpannableString的一篇博客Android – SpannableString 实现富文本效果用法全解析,讲解了SpannableString的所有用法。

3.自定义布局

这时候问题又来了,如果我们的UI不是这种上面图标,下面文字的形式呢?我们的图标可能在上边,在下边,在左边,在右边,甚至不止图标和文字,还要有其他元素。

没问题,没问题,自定义布局通通满足你。

xml中设置

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

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout="@layout/custom_container1"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout="@layout/custom_container2"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon3"
            android:text="专栏"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon4"
            android:text="热门"/>
    </android.support.design.widget.TabLayout>

如代码中所示,前面两个TabItem使用了自定义布局。

custom_container1布局代码如下,custom_container2也是类似的

<?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:orientation="horizontal">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="1"
        android:text="日报" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/icon1"/>

</LinearLayout>

如果你无法显示图标,那就再仔细看看这篇文章的第二部分吧,答案都在里面了。

代码中创建自定义TabItem

mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.custom_container1));
mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.custom_container2));

效果如下

关于TabLayout的内容就是这些了,如果帮助到了你,请点个赞吧,如果有什么不懂或新思路,也可以留言交流哦!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值