Material Design 控制元件知識梳理(8) TabLayout

澤毛發表於2017-12-21

一、概述

前面我們介紹了BottomNavigationBarTabLayout和它的作用類似,都是方便使用者進行頁面之間的切換。

二、TabLayout的基本用法

下面,我們先演示一遍TabLayout的基本用法 第一步:引入依賴:

compile 'com.android.support:design:25.3.1'
複製程式碼

第二步:定義佈局,這裡使用了最常用的TabLayout+ViewPager的結構:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.lizejun.repotransition.TabLayoutActivity">
    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="scrollable"
        app:tabGravity="center"/>
    <android.support.v4.view.ViewPager
        android:id="@+id/vp_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
複製程式碼

第三步:定義ViewPagerAdapter,這裡需要重寫getPageTitle方法,它就對應的是TabLayout當中的文案:

public class ViewPagerAdapter extends PagerAdapter {

    private List<String> mTitles;

    public ViewPagerAdapter(List<String> title) {
        mTitles = title;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ViewGroup itemView = (ViewGroup) LayoutInflater.from(container.getContext()).inflate(R.layout.item_view_pager, container, false);
        container.addView(itemView);
        TextView tv = (TextView) itemView.findViewById(R.id.tv_title);
        tv.setText(mTitles.get(position));
        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public int getCount() {
        return mTitles.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles.get(position);
    }
}
複製程式碼

第四步:給ViewPager設定資料,並把ViewPagerTabLayout關聯起來:

public class TabLayoutActivity extends AppCompatActivity {

    private static final String[] TITLE_SHORT = new String[] {
            "深圳","南京","內蒙古"
    };

    private static final String[] TITLE_LONG = new String[] {
            "深圳","南京","內蒙古呼和浩特","廣西壯族自治區","上海","北京","天津"
    };

    private ViewPager mViewPager;
    private TabLayout mTabLayout;
    private ViewPagerAdapter mViewPagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);
        initViewPager();
        initTabLayout();
    }

    private void initViewPager() {
        mViewPager = (ViewPager) findViewById(R.id.vp_content);
        List<String> titles = new ArrayList<>();
        Collections.addAll(titles, TITLE_LONG);
        mViewPagerAdapter = new ViewPagerAdapter(titles);
        mViewPager.setAdapter(mViewPagerAdapter);
    }

    private void initTabLayout() {
        mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
        mTabLayout.setupWithViewPager(mViewPager);
    }

}
複製程式碼

最終的效果為:

Material Design 控制元件知識梳理(8)   TabLayout

三、TabLayout相關設定

3.1 TabItem的外觀設定

對於每個TabItem的外觀,TabLayout我們比較常用的屬性有:

  • 未選中TabItem的文字顏色:app:tabTextColor
  • 選中TabItem的文字顏色app:tabSelectedTextColor
  • 底部滑動條的顏色:app:tabIndicatorColor
  • 底部滑動條的高度:app:tabIndicatorHeight
  • TabItem的背景:app:tabBackground

我們在第二節的基礎上進行修改:

<android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="scrollable"
        app:tabGravity="center"
        app:tabTextColor="@android:color/darker_gray"
        app:tabSelectedTextColor="@android:color/holo_orange_dark"
        app:tabIndicatorColor="@android:color/holo_orange_dark"
        app:tabIndicatorHeight="2dp"
        app:tabBackground="@android:color/transparent" />
複製程式碼

最終展現的效果為:

Material Design 控制元件知識梳理(8)   TabLayout

3.2 TabItem的寬高相關設定

有兩個關於寬度的屬性比較重要:tabMinWidthtabMaxWidth,它允許我們這是每個TabItem的最小和最大寬度。 此外,我們還可以設定TabItem四個維度的Padding

3.3 tabModetabGravity

上面只是決定了單個TabItem的外觀,而TabItemTabLayout當中的擺放規則需要由這兩個屬性來決定,它們可選的值分別為:

  • tabModefixed或者scrollable
  • tabGravityfill或者center

這兩個屬性分別通過:app:tabModeapp:tabGravity來設定,下面,我們就來解釋這幾個值的含義。

3.3.1 tabMode=fixed

在這種模式下,每個TabItem的寬度都是相等的,並且所有的TabItem加起來的寬度不會大於TabLayout的寬度,最終展現的結果還需要依賴於tabGravity屬性:

  • tabGravity=fill 要求所有TabItem加起來的寬度等於TabLayout的寬度,也就是下面的效果
    Material Design 控制元件知識梳理(8)   TabLayout
  • tabGravity=center 不要求TabItem加起來的寬度等於TabLayout的寬度,會根據每個TabItem的屬性,計算出TabItem的寬度,然後取它們中的最大值乘上TabItem的個數,當這一數值小於TabLayout的寬度時,會將每個TabItem的寬度設定為這個最大值,並將它們放置在整個TabLayout的中間,而如果這一數值大於TabLayout的寬度,那麼會將它們在TabLayout中均勻排列。
    Material Design 控制元件知識梳理(8)   TabLayout

3.3.2 tabMode=scrollable

tabMode=fixed模式下,TabItem的位置是固定的,並且它們加起來的寬度不會大於TabLayout的寬度,而在scrollable模式下則沒有這一限制,只需要計算好每個TabItem的寬度,然後把它們排列在TabLayout中就好了,可以通過拖動的方式檢視更多的TabItem,並且當tabMode=scrollable時,tabGravity其實並沒有作用,下面是tabMode=scrollable的事例:

Material Design 控制元件知識梳理(8)   TabLayout
在這種模式下,每個TabItem的寬度預設情況下是根據文字的寬度計算出來的,如果我們希望進行相應的限制,那麼可以修改tabMinWidth或者tabMaxWidth屬性。

3.4 設定監聽

mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {

            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                Log.d("onTabSelected", "position=" + tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                Log.d("onTabUnselected", "position=" + tab.getPosition());

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                Log.d("onTabReselected", "position=" + tab.getPosition());

            }
});
複製程式碼
  • onTabSelected,某個TabItem從未選中狀態變為選中狀態時回撥
  • onTabUnselected,某個TabItem從選中變為未選中時回撥
  • onTabReselected,某個TabItem已經處於選中狀態,但是它又被再次點選了,那麼回撥這個函式。

四、TabLayout + ViewPager

使用TabLayout時,一般都是採用ViewPager來管理多個子介面,而TabLayout也十分貼心地提供了方法讓它和ViewPager的滾動關聯起來,當點選TabLayout中的Item時,會切換到ViewPager對應的介面,而如果滑動了ViewPager,那麼TabLayout也會切換到對應的TabItem。 我們所需要的只是下面這句設定:

mTabLayout.setupWithViewPager(mViewPager);
複製程式碼

為了能讓TabLayout知道TabItem的標題,需要重寫ViewPager所對應的PagerAdaptergetPageTitle的方法:

@Override
public CharSequence getPageTitle(int position) {
    return mTitles.get(position);
}
複製程式碼

雖然TabLayout並沒有提供切換到某個具體位置TabItem的方法,但是其實這一過程這完全可以通過mViewPager.setCurrentItem(int item)來實現。


更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章