TabLayout的用法进行总结和归纳

Google官方在14年Google I/O上推出了全新的设计语言——Material Design。一并推出了一系列实现Material Design效果的控件库——Android Design Support Library。其中,有TabLayout, NavigationView,Floating labels for editing text,Floating Action Button,Snackbar, CoordinatorLayout, CollapsingToolbarLayout等等控件。在今后的学习中,我将一一介绍它们的特点和用法。

我们可以使用三方开源的PagerSlidingTabStrip去实现,或者viewpagerindicator,我一般都偏向前者。现在我们可以使用Design support library库的TabLayout去实现了。

Demo地址https://github.com/setasmallgoal/TabLayoutDemo

Demo首页图:

这里写图片描述

最终的效果图:

这里写图片描述

一、TabLayout基本使用 。

1.在应用的Build.gradle中添加support.design支持库。

implementation 'com.google.android.material:material:1.0.0'

2.TabLayout第一种使用–Tab固定模式
这里写图片描述
1)创建布局文件activity_short_tab,在布局文件中添加TabLayout及ViewPager。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.linwei.lw.tablayoutdemo.ui.activity.MainActivity">

    <android.support.design.widget.TabLayout
        android:id="@+id/tab"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorColor="@color/colorAccent"
        app:tabIndicatorHeight="2dp"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@color/colorAccent">
    </android.support.design.widget.TabLayout>

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

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

2)定义一个FragmentFactivity的Fragment工厂类,专门生产Fragment对象(用了工厂类,方便Fragment使用,提高了性能,在项目中很常见,记得在android5.0的Demo中也是怎么写的,可能有些读者觉得没必要,也可以每次去创建Fragment,并添加到ViewPager中也可)。

package com.linwei.lw.tablayoutdemo.ui.fragment;

import java.util.HashMap;

/**
 * Fragment工厂类。
 * Created by LW on 2017/4/20.
 */

public class FragmentFactory {
    private static HashMap<Integer, BaseFragment> mBaseFragments = new HashMap<Integer, BaseFragment>();

    public static BaseFragment createFragment(int pos) {
        BaseFragment baseFragment = mBaseFragments.get(pos);

        if (baseFragment == null) {
            switch (pos) {
                case 0:
                    baseFragment = new TopLineFragment();//头条
                    break;
                case 1:
                    baseFragment = new NewsFragment();//要闻
                    break;
                case 2:
                    baseFragment = new EntertainmentFragment();//娱乐
                    break;
                case 3:
                    baseFragment = new SportsFragment();//体育
                    break;
                case 4:
                    baseFragment = new FinanceFragment();//财经
                    break;
                case 5:
                    baseFragment = new ScienceFragment();//科技
                    break;
                case 6:
                    baseFragment = new ModeFragment();//时尚
                    break;
                case 7:
                    baseFragment = new VideoFragment();//视频
                    break;
                case 8:
                    baseFragment = new  DirectSeedingFragment();//直播
                    break;

            }
            mBaseFragments.put(pos, baseFragment);
        }
        return baseFragment;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

3)定义一个Fragment的父类BaseFragment,我就不解释了看代码。

package com.linwei.lw.tablayoutdemo.ui.fragment;


import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * Created by LW on 2017/4/20.
 */

public abstract class BaseFragment extends Fragment {

    protected Context mContent;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContent = getContext();//上下文。
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return initView();//初始化布局。
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        loadData();//初始化数据。
    }

    protected abstract void loadData();

    protected abstract View initView();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

4)BaseFragment的实现子类TopLineFragment,这里我就只列出一个子类代码,其他子类代码也是一样的。

package com.linwei.lw.tablayoutdemo.ui.fragment;

import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Created by LW on 2017/4/21.
 */
public class TopLineFragment extends BaseFragment {
    @Override
    protected void loadData() {
        Toast.makeText(mContent,"Fragment头条加载数据",Toast.LENGTH_SHORT).show();
    }

    @Override
    protected View initView() {
        TextView mView = new TextView(mContent);
        mView.setText("Fragment头条");
        mView.setGravity(Gravity.CENTER);
        mView.setTextSize(18);
        mView.setTextColor(Color.BLACK);
        return mView;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

5)arrays.xml中第一显示数据。我了方便使用,通过字符串数组定义起来。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="tab_short_Title">
        <item>头条</item>
        <item>要闻</item>
        <item>娱乐</item>
        <item>体育</item>
    </string-array>

    <string-array name="tab_long_Title">
        <item>头条</item>
        <item>要闻</item>
        <item>娱乐</item>
        <item>体育</item>
        <item>财经</item>
        <item>科技</item>
        <item>时尚</item>
        <item>视频</item>
        <item>直播</item>
    </string-array>
</resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

6)ShortTabActivity类,实现TabLayout和ViewPager的业务逻辑。

package com.linwei.lw.tablayoutdemo.ui.activity;

import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;

import com.linwei.lw.tablayoutdemo.R;
import com.linwei.lw.tablayoutdemo.Utils.CommentUtils;
import com.linwei.lw.tablayoutdemo.ui.fragment.BaseFragment;
import com.linwei.lw.tablayoutdemo.ui.fragment.FragmentFactory;

public class ShortTabActivity extends AppCompatActivity {
    private TabLayout mTab;
    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_short_tab);
        initView();
        initData();
    }

    private void initData() {
        ShortPagerAdapter adapter = new ShortPagerAdapter(getSupportFragmentManager());
        mViewPager.setAdapter(adapter);
        mTab.setupWithViewPager(mViewPager);

    }

    private void initView() {
        mTab = (TabLayout) findViewById(R.id.tab);
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
    }

    private class ShortPagerAdapter extends FragmentPagerAdapter {
        public String[] mTilte;

        public ShortPagerAdapter(FragmentManager fm) {
            super(fm);
            mTilte = getResources().getStringArray(R.array.tab_short_Title);
        }

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

        @Override
        public BaseFragment getItem(int position) {
            BaseFragment fragment = FragmentFactory.createFragment(position);
            return fragment;
        }

        @Override
        public int getCount() {
            return CommentUtils.TAB_SHORT_COUNT;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

3.TabLayout第二种使用–Tab滚动模式

这里写图片描述

1)Tab滚动模式和Tab固定模式差不多,只是Apdater中的getCount的个数和app:tabMode发生改变,这里我就不从新写了,把发生改变写出来,如果想看Demo,下面有地址。

1.1)TabMode属性从fill改为scrollable

这里写图片描述

1.2)适配器中getCount个数。

这里写图片描述

二、TabLayout高级使用(只列出改变代码内容) 。

4.TabLayout第三种使用–Tab图片显示

这里写图片描述
1)实现Tab图片显示,只需要改变ImagePagerAdapter适配器中getpageTitle方法,下面是关键代码。

1.1)把图片的id放在数组中,方便对图片使用 。

这里写图片描述
1.2)getPageTitle()方法,用于Tab显示方式定义,是显示图片关键的代码。

@Override
public CharSequence getPageTitle(int position) {
      Drawable drawable = ContextCompat.getDrawable(MainActivity.this,  mTitleImage[position]);
      drawable.setBounds(0,0,drawable.getIntrinsicWidth()/2, drawable.getIntrinsicHeight()/2);
      ImageSpan imageSpan = new ImageSpan(drawable,ImageSpan.ALIGN_BOTTOM);
      SpannableString spannableString = new SpannableString(" ");
      spannableString.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
      return spannableString;
      //return mTilte[position];
}

1.3)apader适配器显示个数。

这里写图片描述
1.4)此时,设置完之后运行,发现Tab上并没有显示图标,而是什么也没有了。这是因为TabLayout的textAllCaps属性默认值是true,会阻止ImageSpan的渲染,我们只需要将其重写为false即可。

1.4.1)对TabLayout抽取出样式,并对样式调用。

这里写图片描述
1.4.2)对textAllCaps设置和TabLayout样式定义。

这里写图片描述

5.TabLayout第四种使用–Tab图片和文字显示

这里写图片描述

1)获取图片和文字资源。

这里写图片描述

2)对Tab图片和文字设置。

这里写图片描述

6.TabLayout第五种使用–自定义View

这里写图片描述
1)对适配器CustomAdapter中getPageTitle返回null

这里写图片描述
2)在适配器中定义一个方法getTabView(int position),只要对自定义布局调用和数据设置。

这里写图片描述

3.对TabLayout中获取每个标签,并调用指定getTabView,通过setCustom设置给当前Tab。

这里写图片描述

三、TabLayout源码分析。

上面我们使用了自定义View作为Tab,但是有个问题上没有处理,就是Tab在选中时候的高亮状态。如果使用文本Tab,我们可以设置Style中的tabSelectedTextColor来实现,那么想一想TabLayout是在什么时机使用这个资源值的?我们是否也可以在此时机改变自定义View的显示来标记选中。

上面我们使用了自定义View作为Tab,但是有个问题上没有处理,就是Tab在选中时候的高亮状态。如果使用文本Tab,我们可以设置Style中的tabSelectedTextColor来实现,那么想一想TabLayout是在什么时机使用这个资源值的?我们是否也可以在此时机改变自定义View的显示来标记选中。

这里写图片描述

可以看到,给ViewPager添加,注意是添加而不是设置,说明现在ViewPager可以添加任意个页面改变监听器了。与PagerSlidingTabTrip的设置方式相比较,这种方式更好了,看来代码与时俱进还是很重要的,使用TabLayout还是值得的。

好了,放弃题外话,继续关注TabLayoutOnPageChangeListener的实现,关注onPageSelected()方法:

这里写图片描述

当ViewPager的页面改变时会调用selectTab()方法:

这里写图片描述

当新的选中Tab与当前不同时,调用了setSelectedTabView()方法:

这里写图片描述

最后,重新设置了每个TabView的选中状态。也就说,当选中某个Tab时,我们对应的自定义View的setSelected()方法就会调用。所以,当需要根据Tab是否选中更新自定义View的状态时,可以重写setSelected()方法:

这里写图片描述

好了,分析完毕,原谅我这个标题党,我只想简单分析如何根据Tab是否选中更新自定义View的状态。对于源码感兴趣的,可以自行前往研究…

四、app:tabMode和app: tabGravity比较

Android Material Design 中的TabLayout有两个比较有用的属性 app:tabMode、app:tabGravity。

(1)app:tabMode有两个值:fixed和scrollable。

(2)app:tabGravity有两个值:fill和center。

比较常用的是app:tabMode设置值scrollable,以及app:tabGravity设置值center。
比如,当app:tabMode设置值scrollable表示此TabLayout中当子view超出屏幕边界时候,将提供滑动以便滑出不可见的那些子view。
而app:tabGravity设置值center,在有些情况下,比如TabLayout中子view较少需要居中显示时候的情景。
现在给出一个例子加以说明。
1)tabGravity=”fill”和tabMode=”fixed”

这里写图片描述

2)tabGravity=”center”和tabMode=”fixed”

这里写图片描述

3)tabGravity=”fill”和tabMode=”scrollable”

这里写图片描述

4)tabGravity=”cente”和tabMode=”scrollable”

这里写图片描述

最后,附上源码地址:https://github.com/setasmallgoal/TabLayoutDemo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章