TabLayout-Design Library
轉載請註明出處:https://medium.com/@devwilly,違者必究
由於工作上的關係會需要使用Tab相關元件,這讓我想起以前使用Tab時的痛苦回憶,上一次使用Tab的時間我已經想不起來了,約略只對TabHost有些印象。
當然GitHub上有幾套相當有名的third party library,這邊就不多做介紹了。Google在2015年的IO大會上,帶給大家更詳細的Material Design設計規範並導入全新的Android Design Support Library,在support library中Google提供了更符合MD規範的元件,更重要的是Design Support Library向下相容到Android 2.2。
How to use TabLayout ?
首先必需先把Design Support Library 加入專案中,將程式碼加入專案內App目錄下build.gradle
中。
dependencies {
....
compile 'com.android.support:design:25.1.1'
}
接著再相對應的layout頁面中加入TabLayout 和 ViewPager元件,若想客製化TabLayout可以利用style方式來改寫屬性。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:id="@+id/tabs_layout"
style="@style/MyTabLayout"
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:layout_below="@id/tabs_layout"/></RelativeLayout>
自定義TabLayout樣式
要自定樣式前需了解TabLayout有哪些能控制的屬性,下圖為TabLayout預設提供的樣式。
tabIndicatorColor: 指標顏色
tabIndicatorHeight: 指標高度
tabBackground: 背景顏色
tabTextAppearance:字體相關設定
以下範例是針對上述四點進行改寫(overwrite)
,效果如下圖
<style name="MyTabLayout" parent="Widget.Design.TabLayout">
<item name="tabBackground">#3F51B5</item>
<item name="tabIndicatorHeight">5dp</item>
<item name="tabIndicatorColor">#ff00ff</item>
<item name="tabSelectedTextColor">#ffffff</item>
<item name="tabTextAppearance">@style/MyTextAppearance</item>
</style>
<style name="MyTextAppearance" parent="TextAppearance.Design.Tab">
<item name="android:textSize">16sp</item>
<item name="android:textColor">#dbdbdb</item>
</style>
建立ViewPager 專屬Adapter
Adapter繼承分成 FragmentPagerAdapter
和 FragmentStatePagerAdapter
兩種,最主要的差異就是沒使用的頁面資源是否會被回收。
FragmentPagerAdapter:頁面不會被回收,適合用於靜態頁面中。
FragmentStatePagerAdapter:沒使用的頁面會被回收,較適合用於多頁面。
這邊使用了Enum
小技巧讓程式碼看起來簡潔(咦),不讓程式碼中出現一大堆getResource().getString(R.string.xxx)
。
public enum MovieType {
Week("本週新片", Fragment1.class.getName()),
ComingSoon("即將上映", Fragment2.class.getName()),
TOP("排行榜", Fragment3.class.getName());
private String mTitle;
private String mFragmentName;
MovieType(String title, String fragmentName) {
this.mTitle = title;
this.mFragmentName = fragmentName;
}
public String getTitle() {
return mTitle;
}
public String getFragmentName() {
return mFragmentName;
}
}
利用Enum將許多重複性的程式碼包起來,那要如何取得相對的標題名稱呢?
MovieMode.Week.getTitle()
MovieMode.ComingSoon.getTitle()
MovieMode.LeaderBoard.getTitle()
接著Adapter內需要放哪些資料呢?
- getCount():Return the number of views available
- getPageTitle(int position):取得相對的tab position標題
- getItem(int position):每一個position對應到自己的Fragment page
public class MainTabAdapter extends FragmentPagerAdapter {
private MovieMode mTabTitle[] =
new MovieMode[]{Week, ComingSoon, TOP};
public MainTabAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return MovieListFragment.newInstance(mTabTitle[position]);
}
@Override
public int getCount() {
return mTabTitle.length;
}
@Override
public CharSequence getPageTitle(int position) {
return mTabTitle[position].getTitle();
}
}
KeyPoint
附上幾個開發時碰到較為麻煩的問題,希望大家別再踩到地雷!!
(1) 避免使用Fragment in Fragment,因為lifecycle會讓你處理到崩潰XD
(2) 注意getSupportFragmentManager() 和 getChildFragmentManager()的 使用時機,不然會造成Fragment被堆疊再FragmentManager中無法被釋放。
- Fragment parent是Activity 請使用getSupportFragmentManager()
- Fragment parent是Fragment 請使用getChildFragmentManager()
(3) FragmentPagerAdapter call notifyDataSetChanged()
是無法再次觸發adapter中的getItem(int position)
(4)FragmentStatePagerAdapter call notifyDataSetChanged()
會依據getItemPosition(Object object)
回傳的值來決定是否觸發getItem(int position)
- POSITION_NONE:
trigger
getItem method - POSITION_UNCHANGED:
non-trigger
getItem method
Conclusion
Design Support Library 能快速實作符合MD規範的功能,讓我們省去許多調整時間,但並非所有功能都必須靠它完成,有時候反而會多此一舉。
最後附上Sample Code
若你喜歡這篇文章歡迎推薦給你的朋友
若你喜歡我的部落格歡迎追隨我
Reference
轉載請註明出處:https://medium.com/@devwilly,違者必究