Google官方在14年 I/O大會上推出了全新的設計語言——Material Design,
Material Design讓Android界面在體驗上更加鮮艷和簡潔,正在逐步成為APP設計的趨勢.
而與Material Design相關的是一系列實現(xiàn)Material Design效果的控件庫Android Design Support Library,從今天開始將一一介紹他們的用法.
這一系列控件庫在使用時都需要在gradle中添加依賴:
compile 'com.android.support:design:25.3.1'
一扇谣、TabLayout介紹
官方地址:TabLayout
需要翻墻
像上面這種選項卡切換的效果惨恭,幾乎所有應用中都有涉及到,這種效果實現(xiàn)方式很多渊季,TabHost實現(xiàn)、RadioGroup+RadioButton實現(xiàn)罚渐、ViewPager+PagerTabStrip或者種種第三方庫(Viewpageindicator...)來實現(xiàn)却汉、HorizontalScrollView+LinearLayout動態(tài)實現(xiàn)......
而在Android Design Support Library中就有這樣一個控件:TabLayout,繼承自HorizontalScrollView荷并,通過TabLayout我們可以很輕松的實現(xiàn)這種效果.
二合砂、TabLayout簡單使用
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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>
</LinearLayout>
Activity中:
public class MainActivity extends AppCompatActivity {
private TabLayout mTablayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTablayout = (TabLayout) findViewById(R.id.tablayout);
mTablayout.addTab(mTablayout.newTab().setText("A"));
mTablayout.addTab(mTablayout.newTab().setText("B"));
mTablayout.addTab(mTablayout.newTab().setText("C"));
}
}
效果圖:
簡單的選項卡效果就實現(xiàn)了,當然源织,你也可以在布局文件中寫死翩伪,像下面這樣:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="C" />
</android.support.design.widget.TabLayout>
</LinearLayout>
效果和上面是一樣的,不過一般沒人這么使用.
三雀鹃、TabLayout的常用屬性
app:tabIndicatorColor="" 指示器顏色
app:tabIndicatorHeight="" 指示器高度幻工,設置為0就是沒有指示器
app:tabTextColor="" Tab文本默認顏色
app:tabSelectedTextColor="" Tab文本被選中后的顏色
app:tabTextAppearance="" 為Tab文本設置樣式,一般是需要為Tab加圖標時使用
app:tabMode="" 只有兩個值:fixed黎茎、scrollable
fixed用于標題欄少的情況囊颅,每個Tab可以平分屏幕寬度
scrollable用于標題欄多出屏幕的情況,如果標題欄少的時候用很難看,占不滿屏幕
app:tabBackground="" TabLayout背景踢代,和android:background=""效果一樣
app:tabGravity="" 對齊方式: 居中顯示center盲憎、fill填滿
還有一些很少使用的:
從左邊開始偏移距離,tabMode值必須為scrollable才會生效
app:tabContentStart="150dp"
選項卡寬度限制
app:tabMaxWidth="" 最大寬度
app:tabMinWidth="" 最小寬度
Tab內邊距
app:tabPaddingStart=""
app:tabPaddingBottom=""
app:tabPaddingEnd=""
app:tabPaddingTop=""
app:tabPadding=""
三、TabLayout方法
Tablayout.newTab() 創(chuàng)建標簽
Tablayout.addTab() 添加標簽
Tablayout.removeTab() 刪除標簽
Tablayout.removeTabAt() 通過索引刪除標簽
Tablayout.removeAllTabs() 刪除全部標簽
因為TabLayout繼承自HorizontalScrollView胳挎,所以可以直接添加View
addView ()
添加監(jiān)聽器饼疙,取消監(jiān)聽器,清楚所有的監(jiān)聽器
Tablayout.addOnTabSelectedListener()
Tablayout.removeOnTabSelectedListener()
Tablayout.clearOnTabSelectedListeners()
Tablayout.getSelectedTabPosition() 獲取當前選中的Tab位置
Tablayout.getTabAt() 根據(jù)索引獲取Tab
Tablayout.getTabCount() 獲取Tab總數(shù)
對應tabGravity屬性
Tablayout.getTabGravity()
Tablayout.setTabGravity()
對應tabMode屬性
Tablayout.getTabMode()
Tablayout.setTabMode()
對應tabTextColor屬性
Tablayout.setTabTextColors()
Tablayout.getTabTextColors()
和ViewPager關聯(lián)慕爬,隨著ViewPager滑動而滑動
TabLayout和ViewPager配合使用是最常見的運用方式
兩者關聯(lián)后:
1)TabLayout之前創(chuàng)建的Tab并不能正常顯示窑眯,但可以在關聯(lián)后通過 getTabAt() 得到標簽之后進行修改
但其實沒有必要,我們一般由2)和3)決定Tab的數(shù)量和內容
2)TabLayout的Tab數(shù)量由ViewPager分頁數(shù)量決定
3)TabLayout的Tab內容由ViewPager的Adapter中 getPagerTitle() 方法返回的內容決定
Tablayout.setupWithViewPager()
TabLayout中的內部類:Tab医窿,表示TabLayout中的每一個標簽
Tab的方法
boolean is Selected() Tab是否被選中
void setSelected() 設置Tab為被選中狀態(tài)
setText () 設置Tab文本內容
getText () 獲取Tab文本內容
getIcon () 獲取Tab的圖標
setIcon () 為Tab添加圖標
setCustomView() 設置用戶自定義的Tab磅甩,參數(shù)為資源id或者View對象
getPosition() 獲取當前位置
四、TabLayout搭配ViewPager使用
1姥卢、MainActivity布局文件的兩種方式
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
app:tabMode="scrollable"
app:tabBackground="@android:color/holo_blue_dark"
app:tabIndicatorColor="@android:color/white"
app:tabTextColor="@android:color/black"
app:tabSelectedTextColor="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.view.ViewPager>
</LinearLayout>
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
app:tabMode="scrollable"
app:tabBackground="@android:color/holo_blue_dark"
app:tabIndicatorColor="@android:color/white"
app:tabTextColor="@android:color/black"
app:tabSelectedTextColor="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<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>
2卷要、MainActivity.java
public class MainActivity extends AppCompatActivity {
private TabLayout mTablayout;
private ViewPager mViewPager;
private List<String> mDatas;
private List<Fragment> fragments;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initFragments();
mTablayout = (TabLayout) findViewById(R.id.tablayout);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mDatas, fragments));
// mTablayout.setupWithViewPager(mViewPager);
//注意:我的布局文件中ViewPager和TabLayout 是嵌套的,所以不需要這一步
// 如果沒有嵌套独榴,則需要在ViewPager設置Adapter之后加上這一步
//另外僧叉,只能是ViewPager嵌套Tablayout,反了會報錯的棺榔,因為Tablayout中只能嵌套TabItem
}
private void initData() {
mDatas = new ArrayList<>();
for (int i = 'A'; i < 'I'; i++) {
mDatas.add("" + (char) i);
}
}
private void initFragments() {
fragments = new ArrayList<>();
for (int i = 0; i < mDatas.size(); i++) {
MyFragment fragment = MyFragment.newInstance(mDatas.get(i));
fragments.add(fragment);
}
}
}
3瓶堕、MyPagerAdapter.java
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private List<String> mDatas;
private List<Fragment> mFragments;
public MyPagerAdapter(FragmentManager fm, List<String> mDatas, List<Fragment> fragments) {
super(fm);
this.mDatas = mDatas;
this.mFragments = fragments;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mDatas.size()==mFragments.size()?mFragments.size():0;
}
/**
* 重寫此方法,返回TabLayout的內容
*/
@Override
public CharSequence getPageTitle(int position) {
return mDatas.get(position);
}
}
4掷豺、MyFragment.java
public class MyFragment extends Fragment {
public static MyFragment newInstance(String data) {
Bundle args = new Bundle();
args.putString("key", data);
MyFragment fragment = new MyFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, container, false);
TextView tv = (TextView) view.findViewById(R.id.tv);
tv.setText("Fragment------" + getArguments().getString("key"));
return view;
}
}
5捞烟、Fragment布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
tools:text="別看了,我就是一個TextView" />
</RelativeLayout>
5当船、效果圖
五题画、TabLayout之自定義Tab布局
我們的Tab上有時候不僅僅只有文字,還會有圖標德频,那么這時候我們的getPageTitle()方法就不應該僅僅返回一個文本了.
方式1:通過SpannableString為Tab添加圖標
SpannableString基本上與String差不多苍息,也是用來存儲字符串,但它們倆的特殊就在于SpannableString有一個SetSpan()方法壹置,能給這些存儲的String添加各種格式或者稱樣式(Span)竞思,將原來的String以不同的樣式顯示出來,比如在原來String上加下劃線钞护、加背景色盖喷、改變字體顏色、用圖片把指定的文字給替換掉难咕,等等.
MainActivity中的修改
private int[] imageRes = {
R.mipmap.a,R.mipmap.b, R.mipmap.c,R.mipmap.d,
R.mipmap.e, R.mipmap.f,R.mipmap.g,R.mipmap.h
};
mViewPager.setAdapter(new MyPagerAdapter(MainActivity.this,getSupportFragmentManager(), mDatas, fragments,imageRes));
添加了一個裝有圖片資源的數(shù)組课梳,然后通過構造傳給MyPagerAdapter距辆,當然,其實標題mDatas和圖標資源imageRes你直接聲明在MyPagerAdapter中也是可以的.
MyPagerAdapter中的修改
private int[] imageRes;
private Context mContext;
public MyPagerAdapter(Context context, FragmentManager fm, List<String> mDatas, List<Fragment> fragments, int[] imageRes) {
super(fm);
this.mContext = context;
this.mDatas = mDatas;
this.mFragments = fragments;
this.imageRes = imageRes;
}
/**
* 通過SpannableString 為Tab添加各種樣式暮刃,這里就添加了一個圖標
*/
@Override
public CharSequence getPageTitle(int position) {
SpannableString sb = new SpannableString(" "+mDatas.get(position));
Drawable image = mContext.getResources().getDrawable(imageRes[position]);
image.setBounds(0,0,image.getIntrinsicWidth(), image.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb;
}
運行之后你會發(fā)現(xiàn)效果沒變跨算,這是因為TabLayout創(chuàng)建的tab默認樣式textAllCaps屬性為true,這阻止了ImageSpan被渲染出來椭懊,可以通過下面的樣式文件定義來改變:
在res/values/styles中定義我們自己的樣式诸蚕,把textAllCaps屬性設為false
<style name="MyTabTextAppearance" parent="TextAppearance.Design.Tab">
<item name="textAllCaps">false</item>
<!--<item name="android:textSize">20sp</item>-->
</style>
TabLayout中Tab的文字大小無法在布局文件中更改,也是在這里更改的
在布局文件中為TabLayout引用我們自己的樣式
app:tabTextAppearance="@style/MyTabTextAppearance"
效果圖
方式2:通過Tab的setCustomView()方法自定義Tab布局
上面已經(jīng)介紹了氧猬,Tab有一個方法背犯,setCustomView(),參數(shù)為一個資源id或者一個View對象盅抚,我們通過這個方法來自定義Tab的布局.
1)MainActivity布局文件
<?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"
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"
app:tabBackground="@android:color/holo_blue_dark"
app:tabIndicatorColor="@android:color/white"
app:tabMode="scrollable"
app:tabSelectedTextColor="@android:color/white"
app:tabTextAppearance="@style/MyTabTextAppearance"
app:tabTextColor="@android:color/black" />
<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>
2)MainActivity.java
public class MainActivity extends AppCompatActivity {
private TabLayout mTablayout;
private ViewPager mViewPager;
private List<String> mDatas;
private List<Fragment> fragments;
private MyPagerAdapter mAdapter;
private int[] imageRes = {
R.mipmap.a,R.mipmap.b, R.mipmap.c,R.mipmap.d,
R.mipmap.e, R.mipmap.f,R.mipmap.g,R.mipmap.h
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initFragments();
mTablayout = (TabLayout) findViewById(R.id.tablayout);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mAdapter = new MyPagerAdapter(MainActivity.this,getSupportFragmentManager(), mDatas, fragments,imageRes);
mViewPager.setAdapter(mAdapter);
mTablayout.setupWithViewPager(mViewPager);
/**
* 為每一個Tab設置自定義布局
*/
for (int i = 0; i <mTablayout.getTabCount() ; i++) {
TabLayout.Tab tab = mTablayout.getTabAt(i);
tab.setCustomView(mAdapter.getTabView(i));
}
}
private void initData() {
mDatas = new ArrayList<>();
for (int i = 'A'; i < 'I'; i++) {
mDatas.add("" + (char) i);
}
}
private void initFragments() {
fragments = new ArrayList<>();
for (int i = 0; i < mDatas.size(); i++) {
MyFragment fragment = MyFragment.newInstance(mDatas.get(i));
fragments.add(fragment);
}
}
}
3)MyPagerAdapter.java
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private List<String> mDatas;
private List<Fragment> mFragments;
private int[] imageRes;
private Context mContext;
public MyPagerAdapter(Context context, FragmentManager fm, List<String> mDatas, List<Fragment> fragments, int[] imageRes) {
super(fm);
this.mContext = context;
this.mDatas = mDatas;
this.mFragments = fragments;
this.imageRes = imageRes;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mDatas.size()==mFragments.size()?mFragments.size():0;
}
/**
* 要使用我們自定義的布局媳板,這里返回null
*/
@Override
public CharSequence getPageTitle(int position) {
return null;
}
/**
* 定義一個方法,來返回Tab的內容
*/
public View getTabView(int position){
View view = LayoutInflater.from(mContext).inflate(R.layout.tab_layout, null);
TextView tv= (TextView) view.findViewById(R.id.tv);
ImageView iv = (ImageView) view.findViewById(R.id.iv);
tv.setText(mDatas.get(position));
iv.setImageResource(imageRes[position]);
return view;
}
}
4)每一個Tab的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:src="@mipmap/a" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
tools:text="別看了泉哈,我就是一個TextView" />
</LinearLayout>
其它的文件沒有發(fā)生改變
5)效果圖
ok, 效果還好,但是文字顏色的選擇器效果沒了破讨,這種情況我們可以在每一個Tab的布局文件為TextView的textColor設置一個選擇器就行了
android:textColor="@drawable/selector_tab_textcolor"
在drawable目錄下新建一個選擇器selector_tab_textcolor.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/white" android:state_selected="true"/>
<item android:color="@color/black" />
</selector>
但是圖片要實現(xiàn)選擇器的效果就不好辦了丛晦,Tab數(shù)量太多,針對每一個tab的圖片都需要一個selector....
另外我們也注意到:setCustomView()為Tab設置自定義布局的參數(shù)不僅僅可以是View對象提陶,也可以是資源Id.像下面這樣:
int[] tabs = {R.layout.tab_one,
R.layout.tab_two,
R.layout.tab_three,
R.layout.tab_four};//有多少tab烫沙,就添加多少tab的布局文件
for (int i = 0; i < mTablayout.getTabCount(); i++) {
mTabLayout.getTabAt(i).setCustomView(tabs[i]);
}
這種做法適合選項卡比較少而且固定的情況.
六、關于TabLayout指示器長度的更改
TabLayout的指示器(Indicator)可以更改顏色隙笆、高度锌蓄,但是你會發(fā)現(xiàn)沒有更改長度的屬性或者方法,這時我們查看源碼撑柔,發(fā)現(xiàn)被private 了瘸爽,所以只能通過反射來做了.
private final SlidingTabStrip mTabStrip;
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 ll_tab = null;
try {
ll_tab = (LinearLayout) tabStrip.get(tabs);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
int left = (int) (Resources.getSystem().getDisplayMetrics().density * leftDip);
int right = (int) (Resources.getSystem().getDisplayMetrics().density * rightDip);
for (int i = 0; i < ll_tab.getChildCount(); i++) {
View child = ll_tab.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();
}
}
注意:tabMode必須是fixed
1)調用setIndicator(mTabLayout, 20, 20);方法即可,這兩個20根據(jù)實際需求來定.
2)關于背景不正常問題铅忿,不要使用TabLayout自帶的app:tabBackground=""剪决;
使用android:background=" "就可以了
ok,TabLayout的使用就寫到這里檀训,如果你的項目中有類似效果要實現(xiàn)柑潦,不妨試試TabLayout.