TabLayout高端用法(一)

TabLayout提供了一个水平的布局用来展示Tabs,很多应用都有这样的设计,典型的有网易新闻,简书,知乎等。TabLayout就可以很好的完成这一职责,当然也或许各家应用的实现方式不尽相同,这里介绍下TabLayout的用法。

首先TabLayout一般都是配合Viewpager使用的,Viewpager里的Fragment随着顶部的Tab一起联动,这种场景再熟悉不过了。在没有TabLayout的日子里关于这种设计一般都是自己实现的。

  • 先来个简单通俗的代码:
<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/toolbar_tab"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom"
        android:background="#ffffff"
        android:fillViewport="false"
        app:tabMode="fixed"
        app:layout_scrollFlags="scroll"
        app:tabIndicatorColor="#057523"
        app:tabIndicatorHeight="2.0dp"
        app:tabSelectedTextColor="#057523"
        app:tabTextColor="#ced0d3">

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="a" />

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="b" />

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="c" />

    </android.support.design.widget.TabLayout>

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

上面代码的运行效果如下:

E7C96D0A-4692-40B3-B1D7-08361A7793E7-75356-000684F41CA2CA41.gif
  • 为了使用TabLayout,我们要让Activity继承自AppCompatActivity,但有时候你项目里的BaseActivity却是继承自FragmentActivity的,这就尴尬了。其实没关系的, AppCompatActivity 也是extends FragmentActivity的。可以把BaseActivity extends AppCompatActivity。如果不想这么做也可以,可以指定当前Activity的theme为

android:theme="@style/Theme.AppCompat"

然后build.gradle文件在dependencies里加上

compile 'com.android.support:design:25.0.0'

然后基本上就不会有什么问题了。

下面来解析下TabLayout的一些基本属性:

app:tabIndicatorColor :指示条的颜色
app:tabIndicatorHeight :指示条的高度
app:tabSelectedTextColor : tab被选中时的字体颜色
app:tabTextColor : tab未被选中时的字体颜色
app:tabMode="scrollable" : 默认是fixed:固定的,标签很多时候会被挤压,不能滑动。

重要的属性基本就这些,其他简单的属性可以自己去摸索,这里选中和未选中的字体颜色,可以根据自己的设计自行修改,同样指示条的高度颜色也可以随意修改。

  • 但假如我的设计里不需要指示条怎么办,好像没发现隐藏的API,那也很简单。有两个思路:
    1:把指示条高度设为0:

app:tabIndicatorHeight="0dp"

2:把指示条的颜色设为透明:

app:tabIndicatorColor="@color/transparent"

效果如下:

40FAEFFF-FF76-43D1-8DD9-98177B630AE4-75356-00068525B87F337E.gif
  • TabItem

    在高版本的design库里已经有了TabItem,TabItem是作为TabLayout的子View而配合使用的,点进去发现其实代码很简单,就是个自定义View。

public final class TabItem extends View {
    final CharSequence mText;
    final Drawable mIcon;
    final int mCustomLayout;

    public TabItem(Context context) {
        this(context, null);
    }

    public TabItem(Context context, AttributeSet attrs) {
        super(context, attrs);

        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                R.styleable.TabItem);
        mText = a.getText(R.styleable.TabItem_android_text);
        mIcon = a.getDrawable(R.styleable.TabItem_android_icon);
        mCustomLayout = a.getResourceId(R.styleable.TabItem_android_layout, 0);
        a.recycle();
    }
}

所以当我们的需求能够明确知道Tab的个数时,可以在xml里直接添加TabItem。但是但是,心细的你不知道有没有发现问题,我在上面的代码中,tab明明设置的小写,但是运行出来确是大写:

 <android.support.design.widget.TabItem
       android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="a" />

事先申明我可没在代码里重新设置文本,就是这么操蛋。好在天无绝人之路,找到了一个属性叫app:tabTextAppearance,这是Tablayout的属性。TabItem代码简单到几乎没有什么属性可供设置,什么字体大小,颜色貌似都设置不了。
所以我们自己写了个样式,然后酱写:

app:tabTextAppearance="@style/MyTabLayoutTextAppearance"

MyTabLayoutTextAppearance里的代码如下:

<style name="MyTabLayoutTextAppearance"parent="TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse">
<item name="android:textSize">16sp</item>
<item name="android:textAllCaps">false</item>
</style>

这里的android:textAllCaps属性就是控制字体大小写的,TabLayout里默认是true,我们手动改成false即可,我们顺便设置了下字体。

但是但是,问题又来了,我设置的字体大小貌似没什么卵用,无论我怎么调节字体大小就是不变。呵呵,还是要从tabTextAppearance这个属性来着手。
下面我们把代码改成这样:

app:tabTextAppearance="@android:style/TextAppearance.Holo.Large"

这下好了,字体的大小写解决了,字体大小也解决了。这样的属性我们找到了3组,

<style name="TextAppearance.Holo.Large" parent="TextAppearance.Large" />
<style name="TextAppearance.Holo.Medium"parent="TextAppearance.Medium"/>
<style name="TextAppearance.Holo.Small" parent="TextAppearance.Small" />

大.jpg

分别设置字体为大中小,说实话,这玩意真的不太好用,跟其他控件比起来,这个属性设置有点绕。

  • 不要用文本了,改成icon吧,wtf,TabItem根本没有这样的属性啊,TabLayout貌似也没有啊。怎么搞?TabLayout没有明确地提供向Tab中设置图标的途径,但是很多事情总可以另辟蹊径。我们知道,Tab是使用adapter中的getPageTitle()方法做其显示的内容,这个方法返回类型为CharSequence。于是,我们可以在PagerAdapter中重写getPageTitle()方法,创建一个SpannableString,而将图标放置在ImageSpan中,设置在SpannableString中:
    private int[] imageResId = {       
           R.mipmap.ic_0,       
           R.mipmap.ic_1,       
           R.mipmap.ic_2
        };

    @Override
        public CharSequence getPageTitle(int position){
            Drawable image = ContextCompat.getDrawable(MainActivity.this, imageResId[position]);
            image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
            SpannableString sb = new SpannableString(" ");
            ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
            sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            return sb;

        }

好了,运行起来,效果有了。

1B73B273D717C65EA0D26E63FEBB9C40.jpg
  • 要不改成icon+文本吧?呵呵。。。又改???
    还好还好,还是上面的方案,稍微修改下代码。在SpannableString中添加文本就可以了:
        @Override
        public CharSequence getPageTitle(int position){
            Drawable drawable = null;
            String title=null;
            switch (position) {
                case 0:
                    drawable = ContextCompat.getDrawable(MainActivity.this,R.mipmap.ic_qq_pre);
                    title = "a";
                    break;
                case 1:
                    drawable = ContextCompat.getDrawable(MainActivity.this, R.mipmap.ic_weibo_pre);
                    title = "b";
                    break;
                case 2:
                    drawable = ContextCompat.getDrawable(MainActivity.this, R.mipmap.login_weixin_icon);
                    title = "c";
                    break;
                default:
                    break;
            }
            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

            ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
            SpannableString spannableString = new SpannableString("   " + title);
            spannableString.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            return spannableString;

        }

还好还好,至于图片的select效果应该很easy了,就不演示了,效果如下。

C9E196F7ABD7AED92B5047DFA767E1E2.jpg
  • 图片在左边?要不放右边吧,不不不,放上面,算了算了,放下面吧。到底放哪???
    如果需求太奇葩,常规手段或者奇技淫巧都无法满足需求的话,就只有最后一招了:自定义。前面说过了TabItem本质上也是View,我们可以根据自己的实际需求来重写这个View。

icon在右边:


右.jpg

icon在上边:


上.jpg

可以发现通过自定义View的方式我们可以随意摆放文本和icon的位置,无所谓上下左右,处理起来都是一样的。甚至一个tab想放两个icon或者两个文本什么的都不在话下。一不下心展开讲,说的有点多了,这里就不再介绍如何自定义TabItem了,放在下篇讲,说了这么多好像也没上Tablayout和ViewPager的代码,也放在下一篇。总体来讲Tablayout的坑还是蛮多的,很多API都没提供,或者提供了但留了很多坑,这很google,一方面给你一个很常用的控件,一方面这个控件又留了很多坑,最后这个控件带给你无限想象和发挥,根据自己的想法,动手去实现吧。

完整代码:GitHub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容