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的內容就是這些了,如果幫助到了你,請點個贊吧,如果有什麼不懂或新思路,也可以留言交流哦!