Google官方在14年Google I/O上推出了全新的設計語言——Material Design。一併推出了一系列實現Material Design效果的控件庫——Android Design Support Library。其中,有TabLayout, NavigationView,Floating labels for editing text,Floating Action Button,Snackbar, CoordinatorLayout, CollapsingToolbarLayout等等控件。在今後的學習中,我將一一介紹它們的特點和用法。
我們可以使用三方開源的PagerSlidingTabStrip去實現,或者viewpagerindicator,我一般都偏向前者。現在我們可以使用Design support library庫的TabLayout去實現了。
Demo地址:https://github.com/setasmallgoal/TabLayoutDemo
Demo首頁圖:
最終的效果圖:
一、TabLayout基本使用 。
1.在應用的Build.gradle中添加support.design支持庫。
implementation 'com.google.android.material:material:1.0.0'
2.TabLayout第一種使用–Tab固定模式。
1)創建佈局文件activity_short_tab,在佈局文件中添加TabLayout及ViewPager。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.linwei.lw.tablayoutdemo.ui.activity.MainActivity">
<android.support.design.widget.TabLayout
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/colorAccent"
app:tabIndicatorHeight="2dp"
app:tabMode="fixed"
app:tabSelectedTextColor="@color/colorAccent">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
2)定義一個FragmentFactivity的Fragment工廠類,專門生產Fragment對象(用了工廠類,方便Fragment使用,提高了性能,在項目中很常見,記得在android5.0的Demo中也是怎麼寫的,可能有些讀者覺得沒必要,也可以每次去創建Fragment,並添加到ViewPager中也可)。
package com.linwei.lw.tablayoutdemo.ui.fragment;
import java.util.HashMap;
/**
* Fragment工廠類。
* Created by LW on 2017/4/20.
*/
public class FragmentFactory {
private static HashMap<Integer, BaseFragment> mBaseFragments = new HashMap<Integer, BaseFragment>();
public static BaseFragment createFragment(int pos) {
BaseFragment baseFragment = mBaseFragments.get(pos);
if (baseFragment == null) {
switch (pos) {
case 0:
baseFragment = new TopLineFragment();//頭條
break;
case 1:
baseFragment = new NewsFragment();//要聞
break;
case 2:
baseFragment = new EntertainmentFragment();//娛樂
break;
case 3:
baseFragment = new SportsFragment();//體育
break;
case 4:
baseFragment = new FinanceFragment();//財經
break;
case 5:
baseFragment = new ScienceFragment();//科技
break;
case 6:
baseFragment = new ModeFragment();//時尚
break;
case 7:
baseFragment = new VideoFragment();//視頻
break;
case 8:
baseFragment = new DirectSeedingFragment();//直播
break;
}
mBaseFragments.put(pos, baseFragment);
}
return baseFragment;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
3)定義一個Fragment的父類BaseFragment,我就不解釋了看代碼。
package com.linwei.lw.tablayoutdemo.ui.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by LW on 2017/4/20.
*/
public abstract class BaseFragment extends Fragment {
protected Context mContent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContent = getContext();//上下文。
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return initView();//初始化佈局。
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loadData();//初始化數據。
}
protected abstract void loadData();
protected abstract View initView();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
4)BaseFragment的實現子類TopLineFragment,這裏我就只列出一個子類代碼,其他子類代碼也是一樣的。
package com.linwei.lw.tablayoutdemo.ui.fragment;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
/**
* Created by LW on 2017/4/21.
*/
public class TopLineFragment extends BaseFragment {
@Override
protected void loadData() {
Toast.makeText(mContent,"Fragment頭條加載數據",Toast.LENGTH_SHORT).show();
}
@Override
protected View initView() {
TextView mView = new TextView(mContent);
mView.setText("Fragment頭條");
mView.setGravity(Gravity.CENTER);
mView.setTextSize(18);
mView.setTextColor(Color.BLACK);
return mView;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
5)arrays.xml中第一顯示數據。我了方便使用,通過字符串數組定義起來。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="tab_short_Title">
<item>頭條</item>
<item>要聞</item>
<item>娛樂</item>
<item>體育</item>
</string-array>
<string-array name="tab_long_Title">
<item>頭條</item>
<item>要聞</item>
<item>娛樂</item>
<item>體育</item>
<item>財經</item>
<item>科技</item>
<item>時尚</item>
<item>視頻</item>
<item>直播</item>
</string-array>
</resources>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
6)ShortTabActivity類,實現TabLayout和ViewPager的業務邏輯。
package com.linwei.lw.tablayoutdemo.ui.activity;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import com.linwei.lw.tablayoutdemo.R;
import com.linwei.lw.tablayoutdemo.Utils.CommentUtils;
import com.linwei.lw.tablayoutdemo.ui.fragment.BaseFragment;
import com.linwei.lw.tablayoutdemo.ui.fragment.FragmentFactory;
public class ShortTabActivity extends AppCompatActivity {
private TabLayout mTab;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_short_tab);
initView();
initData();
}
private void initData() {
ShortPagerAdapter adapter = new ShortPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(adapter);
mTab.setupWithViewPager(mViewPager);
}
private void initView() {
mTab = (TabLayout) findViewById(R.id.tab);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
}
private class ShortPagerAdapter extends FragmentPagerAdapter {
public String[] mTilte;
public ShortPagerAdapter(FragmentManager fm) {
super(fm);
mTilte = getResources().getStringArray(R.array.tab_short_Title);
}
@Override
public CharSequence getPageTitle(int position) {
return mTilte[position];
}
@Override
public BaseFragment getItem(int position) {
BaseFragment fragment = FragmentFactory.createFragment(position);
return fragment;
}
@Override
public int getCount() {
return CommentUtils.TAB_SHORT_COUNT;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
3.TabLayout第二種使用–Tab滾動模式。
1)Tab滾動模式和Tab固定模式差不多,只是Apdater中的getCount的個數和app:tabMode發生改變,這裏我就不從新寫了,把發生改變寫出來,如果想看Demo,下面有地址。
1.1)TabMode屬性從fill改爲scrollable
1.2)適配器中getCount個數。
二、TabLayout高級使用(只列出改變代碼內容) 。
4.TabLayout第三種使用–Tab圖片顯示。
1)實現Tab圖片顯示,只需要改變ImagePagerAdapter適配器中getpageTitle方法,下面是關鍵代碼。
1.1)把圖片的id放在數組中,方便對圖片使用 。
1.2)getPageTitle()方法,用於Tab顯示方式定義,是顯示圖片關鍵的代碼。
@Override
public CharSequence getPageTitle(int position) {
Drawable drawable = ContextCompat.getDrawable(MainActivity.this, mTitleImage[position]);
drawable.setBounds(0,0,drawable.getIntrinsicWidth()/2, drawable.getIntrinsicHeight()/2);
ImageSpan imageSpan = new ImageSpan(drawable,ImageSpan.ALIGN_BOTTOM);
SpannableString spannableString = new SpannableString(" ");
spannableString.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannableString;
//return mTilte[position];
}
1.3)apader適配器顯示個數。
1.4)此時,設置完之後運行,發現Tab上並沒有顯示圖標,而是什麼也沒有了。這是因爲TabLayout的textAllCaps屬性默認值是true,會阻止ImageSpan的渲染,我們只需要將其重寫爲false即可。
1.4.1)對TabLayout抽取出樣式,並對樣式調用。
1.4.2)對textAllCaps設置和TabLayout樣式定義。
5.TabLayout第四種使用–Tab圖片和文字顯示。
1)獲取圖片和文字資源。
2)對Tab圖片和文字設置。
6.TabLayout第五種使用–自定義View。
1)對適配器CustomAdapter中getPageTitle返回null
2)在適配器中定義一個方法getTabView(int position),只要對自定義佈局調用和數據設置。
3.對TabLayout中獲取每個標籤,並調用指定getTabView,通過setCustom設置給當前Tab。
三、TabLayout源碼分析。
上面我們使用了自定義View作爲Tab,但是有個問題上沒有處理,就是Tab在選中時候的高亮狀態。如果使用文本Tab,我們可以設置Style中的tabSelectedTextColor來實現,那麼想一想TabLayout是在什麼時機使用這個資源值的?我們是否也可以在此時機改變自定義View的顯示來標記選中。
上面我們使用了自定義View作爲Tab,但是有個問題上沒有處理,就是Tab在選中時候的高亮狀態。如果使用文本Tab,我們可以設置Style中的tabSelectedTextColor來實現,那麼想一想TabLayout是在什麼時機使用這個資源值的?我們是否也可以在此時機改變自定義View的顯示來標記選中。
可以看到,給ViewPager添加,注意是添加而不是設置,說明現在ViewPager可以添加任意個頁面改變監聽器了。與PagerSlidingTabTrip的設置方式相比較,這種方式更好了,看來代碼與時俱進還是很重要的,使用TabLayout還是值得的。
好了,放棄題外話,繼續關注TabLayoutOnPageChangeListener的實現,關注onPageSelected()方法:
當ViewPager的頁面改變時會調用selectTab()方法:
當新的選中Tab與當前不同時,調用了setSelectedTabView()方法:
最後,重新設置了每個TabView的選中狀態。也就說,當選中某個Tab時,我們對應的自定義View的setSelected()方法就會調用。所以,當需要根據Tab是否選中更新自定義View的狀態時,可以重寫setSelected()方法:
好了,分析完畢,原諒我這個標題黨,我只想簡單分析如何根據Tab是否選中更新自定義View的狀態。對於源碼感興趣的,可以自行前往研究…
四、app:tabMode和app: tabGravity比較
Android Material Design 中的TabLayout有兩個比較有用的屬性 app:tabMode、app:tabGravity。
(1)app:tabMode有兩個值:fixed和scrollable。
(2)app:tabGravity有兩個值:fill和center。
比較常用的是app:tabMode設置值scrollable,以及app:tabGravity設置值center。
比如,當app:tabMode設置值scrollable表示此TabLayout中當子view超出屏幕邊界時候,將提供滑動以便滑出不可見的那些子view。
而app:tabGravity設置值center,在有些情況下,比如TabLayout中子view較少需要居中顯示時候的情景。
現在給出一個例子加以說明。
1)tabGravity=”fill”和tabMode=”fixed”
2)tabGravity=”center”和tabMode=”fixed”
3)tabGravity=”fill”和tabMode=”scrollable”
4)tabGravity=”cente”和tabMode=”scrollable”