Material Design(一)—— TabLayout的使用

Material Design – TabLayout使用
1. 基本使用及最常見用法
2. 在Tablayout顯示圖標 及 源碼解析
3. 自定義佈局

1. 基本使用及最常見用法

1) 在build.gradle 添加依賴

implementation 'com.android.support:design:27.1.1'

2) 編寫佈局

<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/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </android.support.design.widget.TabLayout>


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

    </android.support.v4.view.ViewPager>
</LinearLayout>

3) 編寫Fragment及Activity

Fragment

public class TestFragment extends Fragment {

    public static final String FRAGMENT_TYPE = "fragment_type";
    private String mContent;

    public static Fragment newInstance(String content) {
        Fragment fragment = new TestFragment();
        Bundle bundle = new Bundle();
        bundle.putString(FRAGMENT_TYPE, content);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContent = getArguments().getString(FRAGMENT_TYPE);
    }

    private static final String TAG = "TestFragment";

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView: "+container);
        //這裏的佈局只是一個簡單的FrameLayout,後面手動添加了一個TextView
        ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.fragment_test, container, false);
        TextView textView = new TextView(getContext());
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.CENTER;
        textView.setText(mContent);
        textView.setLayoutParams(layoutParams);
        layout.addView(textView);

        return layout;
    }
}

PagerAdapter類:注意這裏getPageTitle返回的即爲TabLayout的標題,至於爲什麼後面再講

public class MyPagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> mFragments;
    private String[] mTitles;
    public MyPagerAdapter(FragmentManager fm, List<Fragment> fragments, String[] titles) {
        super(fm);
        this.mFragments = fragments;
        this.mTitles = titles;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

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

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }
}

Activity:這裏簡單的創建了4個TestFragment,關鍵的一句在於mTabLayout.setupWithViewPager(mViewPager)將TabLayout與ViewPager綁定

public class MainActivity extends AppCompatActivity {


    private String[] mTitles = {"日報","主題","專欄","熱門"};

    private List<Fragment> mFragments;
    private TabLayout mTabLayout;
    private ViewPager mViewPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTabLayout = findViewById(R.id.tabLayout);

        mViewPager = findViewById(R.id.viewPager);

        mFragments = new ArrayList<>();
        mFragments.add(TestFragment.newInstance(mTitles[0]));
        mFragments.add(TestFragment.newInstance(mTitles[1]));
        mFragments.add(TestFragment.newInstance(mTitles[2]));
        mFragments.add(TestFragment.newInstance(mTitles[3]));

        mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mFragments, mTitles));
        mViewPager.setOffscreenPageLimit(2);

        mTabLayout.setupWithViewPager(mViewPager);

    }
}

4)運行結果
這裏寫圖片描述
5)自定義TabLayout屬性

tabBackground:標籤的背景;
tabMode:fixed, 固定標籤,tab均分,爲默認值;scrollable,可滾動標籤。
tabTextColor:標籤字體顏色;
tabSelectedTextColor:標籤選中字體顏色;
tabIndicatorColor:底部滑動的線條的顏色,默認是colorAccent;
tabIndicatorHeight:底部滑動線條Indicator的高度。

遺憾的是無法調節滑動線條的長度,網上有人使用反射獲取屬性去調節其高度。

2. 在Tablayout顯示圖標

想要在TabLayout上顯示圖標,這時候就需要使用到TabItem了,TabItem是用來配合作爲TabLayout的子View使用的。
在佈局文件TabLayout標籤下添加TabItem

     <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon4"
            android:text="日報"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon3"
            android:text="主題"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon2"
            android:text="專欄"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon1"
            android:text="熱門"/>
    </android.support.design.widget.TabLayout>

這時候再點擊運行,
你會,咦。。。
怎麼沒有圖標出現
不急不急
mTabLayout.setupWithViewPager(mViewPager)這一行註釋掉
替換爲以下兩行代碼

mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));

這時圖標就顯示出來了。

效果如圖

源碼解析

爲什麼要替換爲另外兩句代碼呢?這兩句其實就是setUpWithViewPager的核心代碼,
那這兩句與setUpWithViewPager有什麼不同呢?與讓我們到源碼裏面看看吧

    private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,
            boolean implicitSetup) {
        if (mViewPager != null) {
            // If we've already been setup with a ViewPager, remove us from it
            if (mPageChangeListener != null) {
                mViewPager.removeOnPageChangeListener(mPageChangeListener);
            }
            if (mAdapterChangeListener != null) {
                mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
            }
        }

        if (mCurrentVpSelectedListener != null) {
            // If we already have a tab selected listener for the ViewPager, remove it
            removeOnTabSelectedListener(mCurrentVpSelectedListener);
            mCurrentVpSelectedListener = null;
        }

        if (viewPager != null) {
            mViewPager = viewPager;

            // Add our custom OnPageChangeListener to the ViewPager
            if (mPageChangeListener == null) {
                mPageChangeListener = new TabLayoutOnPageChangeListener(this);
            }
            mPageChangeListener.reset();
            viewPager.addOnPageChangeListener(mPageChangeListener);

            // Now we'll add a tab selected listener to set ViewPager's current item
            mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
            addOnTabSelectedListener(mCurrentVpSelectedListener);

            final PagerAdapter adapter = viewPager.getAdapter();
            if (adapter != null) {
                // Now we'll populate ourselves from the pager adapter, adding an observer if
                // autoRefresh is enabled
                setPagerAdapter(adapter, autoRefresh);
            }

            // Add a listener so that we're notified of any adapter changes
            if (mAdapterChangeListener == null) {
                mAdapterChangeListener = new AdapterChangeListener();
            }
            mAdapterChangeListener.setAutoRefresh(autoRefresh);
            viewPager.addOnAdapterChangeListener(mAdapterChangeListener);

            // Now update the scroll position to match the ViewPager's current item
            setScrollPosition(viewPager.getCurrentItem(), 0f, true);
        } else {
            // We've been given a null ViewPager so we need to clear out the internal state,
            // listeners and observers
            mViewPager = null;
            setPagerAdapter(null, false);
        }

        mSetupViewPagerImplicitly = implicitSetup;
    }

可以看到函數裏先移除了之前的監聽器,然後在將新的監聽器添加上去,
這是與我們的做法一致的,那麼到底哪裏不一樣呢?讓我們看到調用的setPagerAdapter(adapter, autoRefresh);方法內調用的populateFromPagerAdapter();

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

在這個方法內,他首先將所有tab清除掉了,然後再用for循環一個一個加回來,tab個數取自PagerAdapter,並且title取自PagerAdapter的getPageTitle()方法,這也就說明了前面的疑問,爲什麼getPageTitle返回的即爲TabLayout的標題。

然後他這裏沒有重新設置圖標,只設置了一下標題,這也就是爲什麼只調用setUpWithViewPager不顯示圖標的原因了。

如果你想問下有沒有什麼其他方法?那肯定是有的啊。

我們可以照常調用setUpWithViewPager

不用在佈局文件中添加TabItem,而是在代碼中設置添加TabItem,記住如果調用了setuPWithViewPager,那麼只有在調用它之後的TabItem圖標設置纔會有效,添加如下代碼重新設置圖標,這裏可以不用設置文字

mTabLayout.getTabAt(0).setText(mTitles[0]).setIcon(R.drawable.icon1);
mTabLayout.getTabAt(1).setText(mTitles[1]).setIcon(R.drawable.icon2);
mTabLayout.getTabAt(2).setText(mTitles[2]).setIcon(R.drawable.icon3);
mTabLayout.getTabAt(3).setText(mTitles[3]).setIcon(R.drawable.icon4);

如果不使用setupWithViewPager呢?

那就手動調用mTabLayout.addTab(mTabLayout.newTab().setText().setIcon())
代碼中創建TabItem並配合

mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));

來完成Tablayout與ViewPager的聯動

其他操作

顯示圖標其實還有其他一些騷操作,我們可以看回自己的PagerAdapter類的代碼,getPageTitle()方法其實返回的是CharSequence類實例,熟悉繼承關係的知道其實String 和 SpannableString 都是繼承自CharSequence的,用過SpannableString的可能會知道,SpannableString 是可以實現圖標和文字的組合顯示的,那我們就可以返回一個包含圖標和文字的SpannableString,只調用setupWithViewPager實現圖標和文字的顯示,感興趣的可以看我之前關於SpannableString的一篇博客Android – SpannableString 實現富文本效果用法全解析,講解了SpannableString的所有用法。

3.自定義佈局

這時候問題又來了,如果我們的UI不是這種上面圖標,下面文字的形式呢?我們的圖標可能在上邊,在下邊,在左邊,在右邊,甚至不止圖標和文字,還要有其他元素。

沒問題,沒問題,自定義佈局通通滿足你。

xml中設置

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout="@layout/custom_container1"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout="@layout/custom_container2"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon3"
            android:text="專欄"/>

        <android.support.design.widget.TabItem
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:icon="@drawable/icon4"
            android:text="熱門"/>
    </android.support.design.widget.TabLayout>

如代碼中所示,前面兩個TabItem使用了自定義佈局。

custom_container1佈局代碼如下,custom_container2也是類似的

<?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:orientation="horizontal">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="1"
        android:text="日報" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/icon1"/>

</LinearLayout>

如果你無法顯示圖標,那就再仔細看看這篇文章的第二部分吧,答案都在裏面了。

代碼中創建自定義TabItem

mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.custom_container1));
mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.custom_container2));

效果如下

關於TabLayout的內容就是這些了,如果幫助到了你,請點個贊吧,如果有什麼不懂或新思路,也可以留言交流哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章