TabLayout提供了一個(gè)水平的布局用來(lái)展示Tabs,很多應(yīng)用都有這樣的設(shè)計(jì),典型的有網(wǎng)易新聞,簡(jiǎn)書(shū),知乎等。TabLayout就可以很好的完成這一職責(zé),當(dāng)然也或許各家應(yīng)用的實(shí)現(xiàn)方式不盡相同,這里介紹下TabLayout的用法。
首先TabLayout一般都是配合Viewpager使用的,Viewpager里的Fragment隨著頂部的Tab一起聯(lián)動(dòng),這種場(chǎng)景再熟悉不過(guò)了。在沒(méi)有TabLayout的日子里關(guān)于這種設(shè)計(jì)一般都是自己實(shí)現(xiàn)的。
- 先來(lái)個(gè)簡(jiǎn)單通俗的代碼:
<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>
上面代碼的運(yùn)行效果如下:

- 為了使用TabLayout,我們要讓Activity繼承自AppCompatActivity,但有時(shí)候你項(xiàng)目里的BaseActivity卻是繼承自FragmentActivity的,這就尷尬了。其實(shí)沒(méi)關(guān)系的, AppCompatActivity 也是extends FragmentActivity的??梢园袯aseActivity extends AppCompatActivity。如果不想這么做也可以,可以指定當(dāng)前Activity的theme為
android:theme="@style/Theme.AppCompat"
然后build.gradle文件在dependencies里加上
compile 'com.android.support:design:25.0.0'
然后基本上就不會(huì)有什么問(wèn)題了。
下面來(lái)解析下TabLayout的一些基本屬性:
app:tabIndicatorColor :指示條的顏色
app:tabIndicatorHeight :指示條的高度
app:tabSelectedTextColor : tab被選中時(shí)的字體顏色
app:tabTextColor : tab未被選中時(shí)的字體顏色
app:tabMode="scrollable" : 默認(rèn)是fixed:固定的,標(biāo)簽很多時(shí)候會(huì)被擠壓,不能滑動(dòng)。
重要的屬性基本就這些,其他簡(jiǎn)單的屬性可以自己去摸索,這里選中和未選中的字體顏色,可以根據(jù)自己的設(shè)計(jì)自行修改,同樣指示條的高度顏色也可以隨意修改。
- 但假如我的設(shè)計(jì)里不需要指示條怎么辦,好像沒(méi)發(fā)現(xiàn)隱藏的API,那也很簡(jiǎn)單。有兩個(gè)思路:
1:把指示條高度設(shè)為0:
app:tabIndicatorHeight="0dp"
2:把指示條的顏色設(shè)為透明:
app:tabIndicatorColor="@color/transparent"
效果如下:

-
TabItem
在高版本的design庫(kù)里已經(jīng)有了TabItem,TabItem是作為T(mén)abLayout的子View而配合使用的,點(diǎn)進(jìn)去發(fā)現(xiàn)其實(shí)代碼很簡(jiǎn)單,就是個(gè)自定義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();
}
}
所以當(dāng)我們的需求能夠明確知道Tab的個(gè)數(shù)時(shí),可以在xml里直接添加TabItem。但是但是,心細(xì)的你不知道有沒(méi)有發(fā)現(xiàn)問(wèn)題,我在上面的代碼中,tab明明設(shè)置的小寫(xiě),但是運(yùn)行出來(lái)確是大寫(xiě):
<android.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="a" />
事先申明我可沒(méi)在代碼里重新設(shè)置文本,就是這么操蛋。好在天無(wú)絕人之路,找到了一個(gè)屬性叫app:tabTextAppearance,這是Tablayout的屬性。TabItem代碼簡(jiǎn)單到幾乎沒(méi)有什么屬性可供設(shè)置,什么字體大小,顏色貌似都設(shè)置不了。
所以我們自己寫(xiě)了個(gè)樣式,然后醬寫(xiě):
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屬性就是控制字體大小寫(xiě)的,TabLayout里默認(rèn)是true,我們手動(dòng)改成false即可,我們順便設(shè)置了下字體。
但是但是,問(wèn)題又來(lái)了,我設(shè)置的字體大小貌似沒(méi)什么卵用,無(wú)論我怎么調(diào)節(jié)字體大小就是不變。呵呵,還是要從tabTextAppearance這個(gè)屬性來(lái)著手。
下面我們把代碼改成這樣:
app:tabTextAppearance="@android:style/TextAppearance.Holo.Large"
這下好了,字體的大小寫(xiě)解決了,字體大小也解決了。這樣的屬性我們找到了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" />


分別設(shè)置字體為大中小,說(shuō)實(shí)話,這玩意真的不太好用,跟其他控件比起來(lái),這個(gè)屬性設(shè)置有點(diǎn)繞。
- 不要用文本了,改成icon吧,wtf,TabItem根本沒(méi)有這樣的屬性啊,TabLayout貌似也沒(méi)有啊。怎么搞?TabLayout沒(méi)有明確地提供向Tab中設(shè)置圖標(biāo)的途徑,但是很多事情總可以另辟蹊徑。我們知道,Tab是使用adapter中的getPageTitle()方法做其顯示的內(nèi)容,這個(gè)方法返回類(lèi)型為CharSequence。于是,我們可以在PagerAdapter中重寫(xiě)getPageTitle()方法,創(chuàng)建一個(gè)SpannableString,而將圖標(biāo)放置在ImageSpan中,設(shè)置在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;
}
好了,運(yùn)行起來(lái),效果有了。

- 要不改成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效果應(yīng)該很easy了,就不演示了,效果如下。

- 圖片在左邊?要不放右邊吧,不不不,放上面,算了算了,放下面吧。到底放哪???
如果需求太奇葩,常規(guī)手段或者奇技淫巧都無(wú)法滿(mǎn)足需求的話,就只有最后一招了:自定義。前面說(shuō)過(guò)了TabItem本質(zhì)上也是View,我們可以根據(jù)自己的實(shí)際需求來(lái)重寫(xiě)這個(gè)View。
icon在右邊:

icon在上邊:

可以發(fā)現(xiàn)通過(guò)自定義View的方式我們可以隨意擺放文本和icon的位置,無(wú)所謂上下左右,處理起來(lái)都是一樣的。甚至一個(gè)tab想放兩個(gè)icon或者兩個(gè)文本什么的都不在話下。一不下心展開(kāi)講,說(shuō)的有點(diǎn)多了,這里就不再介紹如何自定義TabItem了,放在下篇講,說(shuō)了這么多好像也沒(méi)上Tablayout和ViewPager的代碼,也放在下一篇??傮w來(lái)講Tablayout的坑還是蠻多的,很多API都沒(méi)提供,或者提供了但留了很多坑,這很google,一方面給你一個(gè)很常用的控件,一方面這個(gè)控件又留了很多坑,最后這個(gè)控件帶給你無(wú)限想象和發(fā)揮,根據(jù)自己的想法,動(dòng)手去實(shí)現(xiàn)吧。
完整代碼:GitHub