Android TabLayout使用詳解

通常在ViewPager的上方佩捞,我們都會放一個標簽指示器與ViewPager進行聯(lián)動灶伊。以前援制,我們大多使用的是GitHub上的開源框架PagerSlidingTabTrip赠摇。而現(xiàn)在超燃,我們可以使用Android自帶的控件TabLayout來實現(xiàn)這個效果了区拳,而且TabLayout更為強大,因為Tab標簽可以使用自定義View意乓。

本文通過TabLayout+ViewPager來展示新浪微博中的三種狀態(tài)微博樱调,分別是廣場微博、好友微博以及我的微博届良。

TabLayout的基本使用

在應用的build.gradle中添加support:design支持庫

compile "com.android.support:design:24.1.1"

創(chuàng)建activity_weibo_timeline.xml文件笆凌,在布局文件中添加TabLayout及ViewPager:

layout="@layout/toolbar"/>

android:id="@+id/timeline_tablayout"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:id="@+id/timeline_viewpager"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

布局中toolbar可以忽略,想了解關(guān)于Toolbar的知識士葫,可以參見:《Toobar使用詳解》乞而。

創(chuàng)建顯示微博的Fragment。

布局文件fragment_weibo_timelien.xml內(nèi)容如下:

android:id="@+id/timeline_rv"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

通過RecyclerView展示微博列表慢显。

WeiboTimelineFragment.java內(nèi)容如下:

public class WeiboTimelineFragment extends BaseFragment {

privatestaticfinalString?ARG_TIMELINE_TYPE?="ARG_TIMELINE_TYPE";

@BindView(R.id.timeline_content_tv)

TextView?mContentTV;

privateintmType;

publicstaticWeiboTimelineFragmentnewInstance(inttype){

Bundle?args?=newBundle();

args.putInt(ARG_TIMELINE_TYPE,?type);

WeiboTimelineFragment?fragment?=newWeiboTimelineFragment();

fragment.setArguments(args);

returnfragment;

}

@Override

publicvoidonCreate(Bundle?savedInstanceState){

super.onCreate(savedInstanceState);

mType?=?getArguments().getInt(ARG_TIMELINE_TYPE);

}

@Override

protectedintgetLayoutResId(){

returnR.layout.fragment_weibo_timeline;

}

@Override

protectedvoidinitView(View?view,?Bundle?savedInstanceState){

//?展示微博邏輯

}

}

Fragment根據(jù)傳入的type爪模,來展示不同類型的微博列表欠啤。WeiboTimelineFragment繼承的BaseFragment主要根據(jù)子類getLayoutResId()返回值創(chuàng)建了Fragment的顯示視圖,綁定了ButterKnife屋灌。這部分有TabLayout無關(guān)洁段,有興趣的可以看源碼。

創(chuàng)建ViewPager的適配器WeiboTimelineAdapter:

public class WeiboTimelineAdapter extends FragmentPagerAdapter {

privatestaticfinalintPAGE_COUNT?=3;

privateContext?mContext;

publicWeiboTimelineAdapter(Context?context,?FragmentManager?fm){

super(fm);

this.mContext?=?context;

}

@Override

publicFragmentgetItem(intposition){

inttype;

switch(position)?{

case0:

type?=?Constants.TYPE_TIMELINE_PUBLIC;

break;

case1:

type?=?Constants.TYPE_TIMELINE_FRIEND;

break;

case2:

type?=?Constants.TYPE_TIMELINE_MINE;

break;

default:

type?=?Constants.TYPE_TIMELINE_PUBLIC;

break;

}

returnWeiboTimelineFragment.newInstance(type);

}

@Override

publicintgetCount(){

returnPAGE_COUNT;

}

@Override

publicCharSequencegetPageTitle(intposition){

switch(position)?{

case0:

return"廣場";

case1:

return"好友";

case2:

return"我";

default:

return"微博";

}

}

}

其中声滥,getItem()以及getCount()是必須實現(xiàn)的眉撵,分別返回每個page顯示的Fragment和page的數(shù)量。而getPageTitle()的返回值落塑,則會用來作為標簽的顯示內(nèi)容纽疟。

在WeiboTimelineActivity中,將TabLayout憾赁、ViewPager以及Adapter關(guān)聯(lián)起來污朽。

public class WeiboTimelineActivity extends BaseActvity {

@BindView(R.id.timeline_tablayout)

TabLayout?mTabLayout;

@BindView(R.id.timeline_viewpager)

ViewPager?mViewPager;

@Override

protected?int?getContentViewId()?{

returnR.layout.activity_weibo_timeline;

}

@Override

protected?String?getToolbarTitle()?{

return"微博狀態(tài)";

}

@Override

protected?void?onCreate(@Nullable?Bundle?savedInstanceState)?{

super.onCreate(savedInstanceState);

mViewPager.setAdapter(newWeiboTimelineAdapter(mActvity,mActvity.getSupportFragmentManager()));

mTabLayout.setupWithViewPager(mViewPager);

}

}

BaseActivity與BaseFragment的作用大致相同。至此龙考,最基本的TabLayout+ViewPager的實例實現(xiàn)完畢了蟆肆。就是這么簡單,運行之后效果如下:

Picture

TabLayout使用進階

修改TabLayout的風格

上面我們創(chuàng)建的TabLayout的下面的標識線是粉色的晦款,這個顏色采用的是應用的Meterial Design主題中的強調(diào)類型顏色炎功。示例中的主題跟顏色如下:

@color/colorPrimary? ? @color/colorPrimaryDark? ? @color/colorAccent

#3F51B5

#303F9F

#FF4081

很多情況,我們需要更改標示線的顏色缓溅,以及標題的顏色大小蛇损,整個TabLayout的背景顏色等等,實現(xiàn)的方式就是自定義我們的TabLayout Style:

@dimen/tab_max_width? ? ?attr/colorAccent? ? 2dp? ? 12dp? ? 12dp? ? ?attr/selectableItemBackground? ? @style/MyCustomTabTextAppearance? ? ?android:textColorPrimary14sp? ? ?android:textColorSecondary? ? true

可以看到坛怪,很多屬性我們都可以更改淤齐,之后只要應用這個Style即可。

在Tab上顯示圖標

TabLayout沒有明確地提供向Tab中設(shè)置圖標的途徑袜匿,但是很多事情總可以另辟蹊徑更啄。我們知道,Tab是使用adapter中的getPageTitle()方法做其顯示的內(nèi)容居灯,這個方法返回類型為CharSequence祭务。于是,我們可以創(chuàng)建一個SpannableString怪嫌,而將圖標放置在ImageSpan中待牵,設(shè)置在SpannableString中:

public CharSequence getPageTitle(int position) {

Drawable drawable;

switch (position) {

case 0:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_public);

break;

case 1:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_friend);

break;

case 2:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_mine);

break;

default:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_public);

break;

}

drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);

SpannableString spannableString = new SpannableString(" ");

spannableString.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

returnspannableString;

}

此時,設(shè)置完之后運行喇勋,發(fā)現(xiàn)Tab上并沒有顯示圖標缨该,而是什么也沒有了。這是因為TabLayout的textAllCaps屬性默認值是true川背,會阻止ImageSpan的渲染贰拿,我們只需要將其重寫為false即可蛤袒。

下面是運行效果:

Picture

在Tab上顯示圖文

上面我們成功地在Tab上顯示了圖標,是不是想同時顯示文本+圖標了膨更?有了上面顯示圖標的途徑妙真,是不是在SpannableString中添加文本就可以了:

public CharSequence getPageTitle(int position) {

Drawable drawable;

String title;

switch (position) {

case 0:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_public);

title = "廣場";

break;

case 1:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_friend);

title = "好友";

break;

case 2:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_mine);

title = "我";

break;

default:

drawable = ContextCompat.getDrawable(mContext, R.drawable.icon_weibo_timeline_public);

title = "微博";

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);

returnspannableString;

}

運行效果如下:

Picture

在Tab上顯示自定義View

無論是顯示文本、圖標荚守,亦或者圖文珍德,并不能保證可以滿足我們的需求、我們的野心矗漾。只有能在Tab上顯示任何我們想顯示的內(nèi)容锈候,才能打動我們,從而感嘆“好的敞贡,就用你來顯示Tab了泵琳!”√芤郏可喜可賀的是获列,TabLayout中的Tab是支持設(shè)置自定義View的。

首先蛔垢,先定義我們Tab上顯示的View布局view_weibo_timeline_tab.xml:

android:id="@+id/timeline_tab_icon_iv"

android:layout_width="32dp"

android:layout_height="32dp"

android:layout_centerHorizontal="true"/>

這里為了介紹击孩,就簡單的顯示個圖標了。緊接著鹏漆,編寫對應的TimelineTabView.java:

public class TimelineTabView extends FrameLayout {

@BindView(R.id.timeline_tab_icon_iv)

ImageView?mIconIV;

publicTimelineTabView(Context?context){

super(context);

init(context);

}

publicTimelineTabView(Context?context,?AttributeSet?attrs){

super(context,?attrs);

init(context);

}

privatevoidinit(Context?context){

View.inflate(context,?R.layout.view_weibo_timeline_tab,this);

ButterKnife.bind(this,this);

}

publicvoidsetData(inticonResId){

mIconIV.setImageResource(iconResId);

}

}

很簡單溯壶,對外提供一個設(shè)置圖標的方法。有了自定義View甫男,只需要設(shè)置到TabLayout中的每個Tab上即可。在WeiboTimelineActivity.java的onCreate()方法中進行設(shè)置:

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mTimelineAdapter?=newWeiboTimelineAdapter(mActvity,?mActvity.getSupportFragmentManager());

mViewPager.setAdapter(mTimelineAdapter);

mTabLayout.setupWithViewPager(mViewPager);

for(inti?=0;?i?<?mTabLayout.getTabCount();?i++)?{

mTabLayout.getTabAt(i).setCustomView(mTimelineAdapter.getTabView(i));

}

}

OK验烧,此時TabLayout上的Tab就會顯示我們自定義的View視圖了板驳,因為只是設(shè)置了個圖標,運行效果與在Tab上顯示圖標一樣碍拆。

TabLayout源碼分析

上面我們使用了自定義View作為Tab若治,但是有個問題上沒有處理,就是Tab在選中時候的高亮狀態(tài)感混。如果使用文本Tab端幼,我們可以設(shè)置Style中的tabSelectedTextColor來實現(xiàn),那么想一想TabLayout是在什么時機使用這個資源值的弧满?我們是否也可以在此時機改變自定義View的顯示來標記選中婆跑。

用過PagerSlidingTabTrip的同學可能知道,其原理是引用了ViewPager的OnPageChangeListener來進行聯(lián)動的庭呜,使用的是viewpager.setOnPageChangeListener()滑进,從而使得在你應用中如果想監(jiān)聽ViewPager的頁面狀態(tài)改變犀忱,需要使用PagerSlidingTabTrip的setOnPageChangeListener(),也就是說PagerSlidingTabTrip占用了ViewPager的頁面狀態(tài)監(jiān)聽扶关。

TabLayout與ViewPager聯(lián)動也肯定得監(jiān)聽ViewPager的頁面改變阴汇,查看關(guān)聯(lián)ViewPager與TabLayout的內(nèi)部實現(xiàn),即TabLayout的setupWithViewPager()方法:

private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,

boolean implicitSetup) {

// Add our custom OnPageChangeListener to the ViewPager

if (mPageChangeListener == null) {

mPageChangeListener = new TabLayoutOnPageChangeListener(this);

}

mPageChangeListener.reset();

viewPager.addOnPageChangeListener(mPageChangeListener);

}

可以看到节槐,給ViewPager添加搀庶,注意是添加而不是設(shè)置,說明現(xiàn)在ViewPager可以添加任意個頁面改變監(jiān)聽器了铜异。與PagerSlidingTabTrip的設(shè)置方式相比較哥倔,這種方式更好了,看來代碼與時俱進還是很重要的熙掺,使用TabLayout還是值得的未斑。

好了,放棄題外話币绩,繼續(xù)關(guān)注TabLayoutOnPageChangeListener的實現(xiàn)蜡秽,關(guān)注onPageSelected()方法:

@Override

public void onPageSelected(final int position) {

final TabLayout tabLayout = mTabLayoutRef.get();

if (tabLayout != null && tabLayout.getSelectedTabPosition() != position

&& position < tabLayout.getTabCount()) {

// Select the tab, only updating the indicator if we're not being dragged/settled

// (since onPageScrolled will handle that).

final boolean updateIndicator = mScrollState == SCROLL_STATE_IDLE

|| (mScrollState == SCROLL_STATE_SETTLING

&& mPreviousScrollState == SCROLL_STATE_IDLE);

tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);

}

}

當ViewPager的頁面改變時會調(diào)用selectTab()方法:

void selectTab(final Tab tab, boolean updateIndicator) {

final Tab currentTab = mSelectedTab;

if(currentTab?==?tab)?{

...

}else{

...

if(newPosition?!=?Tab.INVALID_POSITION)?{

setSelectedTabView(newPosition);

}

...

}

}

當新的選中Tab與當前不同時,調(diào)用了setSelectedTabView()方法:

private void setSelectedTabView(int position) {

final int tabCount = mTabStrip.getChildCount();

if (position < tabCount) {

for (int i = 0; i < tabCount; i++) {

final View child = mTabStrip.getChildAt(i);

child.setSelected(i == position);

}

}

}

最后缆镣,重新設(shè)置了每個TabView的選中狀態(tài)芽突。也就說,當選中某個Tab時董瞻,我們對應的自定義View的setSelected()方法就會調(diào)用寞蚌。所以,當需要根據(jù)Tab是否選中更新自定義View的狀態(tài)時钠糊,可以重寫setSelected()方法:

@Override

public void setSelected(boolean selected) {

super.setSelected(selected);

// 更改文本顏色挟秤、圖標、背景等等

}

好了抄伍,分析完畢艘刚,原諒我這個標題黨,我只想簡單分析如何根據(jù)Tab是否選中更新自定義View的狀態(tài)截珍。對于源碼感興趣的攀甚,可以自行前往研究…

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市岗喉,隨后出現(xiàn)的幾起案子秋度,更是在濱河造成了極大的恐慌,老刑警劉巖钱床,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荚斯,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機鲸拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門拐格,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刑赶,你說我怎么就攤上這事捏浊。” “怎么了撞叨?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵金踪,是天一觀的道長。 經(jīng)常有香客問我牵敷,道長胡岔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任枷餐,我火速辦了婚禮靶瘸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘毛肋。我一直安慰自己怨咪,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布润匙。 她就那樣靜靜地躺著诗眨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孕讳。 梳的紋絲不亂的頭發(fā)上匠楚,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音厂财,去河邊找鬼芋簿。 笑死,一個胖子當著我的面吹牛璃饱,可吹牛的內(nèi)容都是我干的与斤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼帜平,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了梅鹦?” 一聲冷哼從身側(cè)響起裆甩,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎齐唆,沒想到半個月后嗤栓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年茉帅,在試婚紗的時候發(fā)現(xiàn)自己被綠了叨叙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡堪澎,死狀恐怖擂错,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情樱蛤,我是刑警寧澤钮呀,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站昨凡,受9級特大地震影響爽醋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜便脊,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一蚂四、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哪痰,春花似錦遂赠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乎莉,卻和暖如春送讲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惋啃。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工哼鬓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人边灭。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓异希,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绒瘦。 傳聞我的和親對象是個殘疾皇子称簿,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 原文章地址 Google官方在14年 I/O大會上推出了全新的設(shè)計語言——Material Design, Mat...
    菜菜編程閱讀 2,638評論 0 6
  • 通常在ViewPager的上方惰帽,我們都會放一個標簽指示器與ViewPager進行聯(lián)動憨降。以前,我們大多使用的是Git...
    DoAndKeep閱讀 79,583評論 36 110
  • 用兩張圖告訴你该酗,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料授药? 從這篇文章中你...
    hw1212閱讀 12,693評論 2 59
  • 《你再等等悔叽,我就要站在你面前了》上一章 “什么約會莱衩?我們是簡單的朋友關(guān)系〗颗欤”欣瑤反駁我笨蚁。 “真巧,我們也是九火∽裕”我聳...
    輕綰閱讀 1,006評論 3 9
  • 如果你沒有被文章的標題嚇到,那就來聽聽我們家的故事岔激。 01 我來自一個目前房價只有3000+的北方三線小城勒极,所以我...
    記得初心閱讀 353評論 0 1