TabLayout之自定義樣式

本篇主要介紹TabLayout在開發(fā)中一些常見的用法,不習(xí)慣簡書代碼風(fēng)格的,也可以在 Android_Note查看.

TabLayout的默認(rèn)樣式:


    app:theme="@style/Widget.Design.TabLayout"

從系統(tǒng)定義的該樣式繼續(xù)深入:


    <style name="Widget.Design.TabLayout" parent="Base.Widget.Design.TabLayout">
        <item name="tabGravity">fill</item>
        <item name="tabMode">fixed</item>
    </style>

    <style name="Base.Widget.Design.TabLayout" parent="android:Widget">
        <item name="tabMaxWidth">264dp</item>
        <item name="tabIndicatorColor">?attr/colorAccent</item>
        <item name="tabIndicatorHeight">2dp</item>
        <item name="tabPaddingStart">12dp</item>
        <item name="tabPaddingEnd">12dp</item>
        <item name="tabBackground">?attr/selectableItemBackground</item>
        <item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
        <item name="tabSelectedTextColor">?android:textColorPrimary</item>
    </style>

接著,看看系統(tǒng)定義Tab文本的樣式(注意textAllcaps這個屬性):


    <style name="TextAppearance.Design.Tab" parent="TextAppearance.AppCompat.Button">
        <item name="android:textSize">14dp</item>
        <item name="android:textColor">?android:textColorSecondary</item>
        <item name="textAllCaps">true</item>
    </style>

從系統(tǒng)定義TabLayout的默認(rèn)樣式可以看出,我們可以改變TabLayout對應(yīng)的系統(tǒng)樣式的屬性值來適配我們自己的需求.

TabLayout的基本用法

TabLayout獨(dú)立使用使用時,可以xml布局中靜態(tài)添加tab個數(shù)及其樣式,也可以動態(tài)添加Tab的個數(shù)及其樣式,如:


     <android.support.design.widget.TabLayout
        android:id="@+id/tablayout"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

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

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:icon="@mipmap/ic_launcher"/>
    </android.support.design.widget.TabLayout>

這里寫圖片描述

或者:


    <android.support.design.widget.TabLayout
        android:id="@+id/tablayout"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>


    private int[] images = new int[]{
                    R.drawable.ic_account_balance_wallet_black,
                    R.drawable.ic_android_black,
                    R.drawable.ic_account_box_black};
    private String[] tabs = new String[]{"小說", "電影", "相聲"};
    TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);
    tabLayout.addTab(tabLayout.newTab().setIcon(images[0]).setText(tabs[0]),true);
    tabLayout.addTab(tabLayout.newTab().setIcon(images[1]).setText(tabs[1]),false);
    tabLayout.addTab(tabLayout.newTab().setIcon(images[2]).setText(tabs[2]),false);

這里寫圖片描述

TabLayout在實(shí)際開發(fā)中最多的是與ViewPager聯(lián)合使用,實(shí)現(xiàn)TabLayout與ViewPager的聯(lián)動:


    <android.support.design.widget.TabLayout
        android:id="@+id/tablayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        app:tabGravity="fill"
        app:tabIndicatorColor="@android:color/holo_orange_dark"
        app:tabIndicatorHeight="2dp"
        app:tabMode="fixed"
        app:tabSelectedTextColor="@android:color/holo_orange_dark"
        app:tabTextAppearance="@style/CustomTabTextAppearanceStyle"
        app:tabTextColor="@android:color/white"
        app:theme="@style/Widget.Design.TabLayout"/>

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


    TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);
    ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
    viewPager.setAdapter(new TabPagerAdapter(getSupportFragmentManager()));
    tabLayout.setupWithViewPager(viewPager);
這里寫圖片描述

值得注意的是:

在TabPagerAdapter中需要實(shí)現(xiàn)getPagerTitle()否則,TabLayout的Tab將不顯示,先看TabLayout#setupWithPager()源碼,發(fā)現(xiàn)Tab的添加是在populateFromPagerAdapter()中實(shí)現(xiàn),實(shí)現(xiàn)源碼如下,可以看出該方法調(diào)用了PagerAdpater#getPagerTitle()為Tab設(shè)置文本信息,如果我們自定義的Adapter沒有實(shí)現(xiàn)getPagerTitle()將會導(dǎo)致Tab不顯示文本信息.


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

另外, 我們發(fā)現(xiàn)getPagerTitle()方法的返回值CharSequence而不是String,那么Tab的文本信息的設(shè)置將變得更加靈活,比如設(shè)置一個SpanableString,將圖片和文本設(shè)置Tab的文本.


        @Override
        public CharSequence getPageTitle(int position) {
            Drawable image = TablayoutActivity.this.getResources().getDrawable(images[position]);
            image.setBounds(0, 0, image.getIntrinsicWidth()/2, image.getIntrinsicHeight()/2);
            ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
            SpannableString ss = new SpannableString(" "+tabs[position]);
            ss.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            return ss;
        }   

但是Tab卻沒將image顯示出來,從上面提到的TabLayout的系統(tǒng)默認(rèn)樣式中我們發(fā)現(xiàn): <item name="textAllCaps">true</item>,這會阻止ImageSpan渲染出來,我們只需要將textAllCaps改為false即可,如下定義,再次運(yùn)行,成功顯示


    <style name="CustomTabTextAppearanceStyle" parent="TextAppearance.Design.Tab">
        <item name="textAllCaps">false</item>
    </style>
這里寫圖片描述

修改Indicator的長度:

從TabLayout的源碼可以看出Indicator的繪制,是在其內(nèi)部類SlidingTabStrip中繪制,而SlingTabStrip類繼承LinearLayout,源碼如下:

    @Override  
    public void draw(Canvas canvas) {  
        super.draw(canvas);  
    
        // Thick colored underline below the current selection  
        if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {  
            canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,  
                    mIndicatorRight, getHeight(), mSelectedIndicatorPaint);  
        }  
    }

在onDraw()中主要是就繪制一個Rect,并且寬度是根據(jù)mIndicatorLeft和mIndicatorRight設(shè)置的,而mIndicatorLeft等的寬度來自SlidingTabStrip的child,而Child就相當(dāng)于一個Tab,這樣我們就通過修改Child的margin來設(shè)置mIndicatorLeft的值.


    public void setIndicator(TabLayout tabs, int leftDip, int rightDip) {
        Class<?> tabLayout = tabs.getClass();
        Field tabStrip = null;
        try {
            tabStrip = tabLayout.getDeclaredField("mTabStrip");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        tabStrip.setAccessible(true);
        LinearLayout llTab = null;
        try {
            llTab = (LinearLayout) tabStrip.get(tabs);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        int left = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, leftDip, Resources.getSystem().getDisplayMetrics());
        int right = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightDip, Resources.getSystem().getDisplayMetrics());

        for (int i = 0; i < llTab.getChildCount(); i++) {
            View child = llTab.getChildAt(i);
            child.setPadding(0, 0, 0, 0);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
            params.leftMargin = left;
            params.rightMargin = right;
            child.setLayoutParams(params);
            child.invalidate();
        }
    }

然后在代碼中調(diào)用即可,但是要注意,必須要在Tablayout渲染出來后調(diào)用,我們可以選擇view.post()方法來實(shí)現(xiàn):


    tabLayout.post(new Runnable() {
            @Override
            public void run() {
                setIndicator(tabLayout, 20, 20);
            }
    });

最后得到效果圖如下:

這里寫圖片描述

自定義TabLayout的TabItem及TabItem的點(diǎn)擊事件

在TabLayout的Api是沒有提供TabItem點(diǎn)擊事件的方法,如果我們想實(shí)現(xiàn)如下效果圖,怎么辦?

這里寫圖片描述

先自定義一個TabItem:


<?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:gravity="center"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/txt_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="14sp" />

    <ImageView
        android:id="@+id/img_title"
        android:src="@drawable/indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp" />

</LinearLayout>

在自定義的Adapter中可以定義一個getTabView的方法:


    public View getTabView(int position){
        View view = LayoutInflater.from(context).inflate(R.layout.tab_item, null);
        TextView tv= (TextView) view.findViewById(R.id.textView);
        tv.setText(tabTitles[position]);
        ImageView img = (ImageView) view.findViewById(R.id.imageView);
        img.setImageResource(imageResId[position]);
        return view;
    }

重新設(shè)置點(diǎn)擊事件:


    viewPager.setAdapter(pagerAdapter);
    tabLayout.setupWithViewPager(viewPager);
    for (int i = 0; i < tabLayout.getTabCount(); i++) {
        TabLayout.Tab tab = tabLayout.getTabAt(i);
        if (tab != null) {
            tab.setCustomView(pagerAdapter.getTabView(i));
            if (tab.getCustomView() != null) {
                View tabView = (View) tab.getCustomView().getParent();
                tabView.setTag(i);
                tabView.setOnClickListener(mTabOnClickListener);
            }
        }
    }
    viewPager.setCurrentItem(1);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抗斤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖烁落,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凉蜂,死亡現(xiàn)場離奇詭異,居然都是意外死亡舆乔,警方通過查閱死者的電腦和手機(jī)磨德,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門缘回,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人典挑,你說我怎么就攤上這事酥宴。” “怎么了您觉?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵拙寡,是天一觀的道長。 經(jīng)常有香客問我顾犹,道長倒庵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任炫刷,我火速辦了婚禮擎宝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浑玛。我一直安慰自己绍申,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布顾彰。 她就那樣靜靜地躺著极阅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涨享。 梳的紋絲不亂的頭發(fā)上筋搏,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音厕隧,去河邊找鬼奔脐。 笑死,一個胖子當(dāng)著我的面吹牛吁讨,可吹牛的內(nèi)容都是我干的髓迎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼建丧,長吁一口氣:“原來是場噩夢啊……” “哼排龄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起翎朱,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤橄维,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后闭翩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挣郭,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年疗韵,在試婚紗的時候發(fā)現(xiàn)自己被綠了兑障。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蕉汪,死狀恐怖流译,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情者疤,我是刑警寧澤福澡,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站驹马,受9級特大地震影響革砸,放射性物質(zhì)發(fā)生泄漏除秀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一算利、第九天 我趴在偏房一處隱蔽的房頂上張望册踩。 院中可真熱鬧,春花似錦效拭、人聲如沸暂吉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慕的。三九已至,卻和暖如春挤渔,著一層夾襖步出監(jiān)牢的瞬間肮街,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工判导, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留低散,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓骡楼,卻偏偏與公主長得像熔号,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸟整,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

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