咱們在使用手機App時不難會看到這樣的頁面上面是一組起導航做用的標籤,點擊標籤就會切換到相應的頁面;在不一樣的頁面中滑動時,標籤的樣式(文字大小或者顏色)也會發生變化。這樣你任什麼時候候都能一眼看出本身停留在哪一個頁面。這個佈局出鏡率實在過高了,我甚至敢說每一個學Android的人都寫過這樣的佈局(下面就是知乎中的頁面)。html
好了,廢話少說,咱們照例先來分析一下這個佈局的組成。標籤下面的頁面比較容易想到:總體是一個左右滑動的ViewPager,每一頁則能夠用Fragment填充,也就是ViewPager+Fragment。但上面的標籤部分就有點頭大了,以前咱們都是使用第三方的項目(如PagerSlidingTabStrip),高手的話也能夠自定義一個控件。可是這樣並不是長久之計,因此谷歌後來人性化地推出了自家的標籤控件TabLayout(注意可不要跟TableLayout搞混了,後者是Android的基本佈局之一,而前者是一個控件)。TabLayout顧名思義就是包含Tab的佈局,它包含在Design support library庫中,要使用它,你須要在先添加依賴庫:java
我導入的是最新的26.0.0版本:android
compile 'com.android.support:design:26.0.0-alpha1'
準備完這些,咱們能夠開始寫代碼了。git
以前在知乎上看到有人對微信的設計改動:將使用頻率高的朋友圈、消息提醒和公衆號這三個功能獨立出來放在首頁。我很贊同這樣的設計思路,因此今天就來弄一個簡陋版的吧。大致效果以下:github
<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.lindroid.tablayoutdemo.MainActivity"> <android.support.design.widget.TabLayout android:id="@+id/tayLayout" android:layout_width="match_parent" android:layout_height="60dp" app:tabIndicatorColor="#49dd12" app:tabSelectedTextColor="#49dd12" app:tabTextColor="@android:color/darker_gray"/> <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>
上面的TabLayout的高度固定爲60dp,而後讓ViewPager佔據剩餘的空間便可。如今我來介紹一下用到的TabLayout的屬性:微信
app:tabIndicatorColor
:標籤下面移動的橫線的顏色。app:tabTextColor
:標籤文字的顏色app:tabSelectedTextColor
:標籤被選中後的文字顏色TabLayout還有不少其餘的屬性,好比你要是不想要下面的移動橫線的話,能夠調用屬性app:tabIndicatorHeight
,將高度設置爲0dp便可。關於TabLayout的其餘屬性,你們能夠看看這篇博客,動手練習一下:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0731/3247.htmlapp
爲了可以識別咱們切換到的是ViewPager的哪一個頁面,咱們在Fragment中建立一個帶參數的構造函數,動態添加一個TextView,它的文本內容跟標籤的一致就好。ide
public class TabFragment extends Fragment { private Context context; private String content; //Fragment的顯示內容 public TabFragment() { } public TabFragment(Context context,String content){ this.context = context; this.content = content; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { TextView textView = new TextView(context); textView.setText(content); textView.setTextSize(30); textView.setGravity(Gravity.CENTER); return textView; } }
先來看代碼:函數
public class MainActivity extends AppCompatActivity { private ViewPager viewPager; private TabLayout tabLayout; private List<Fragment> fragments = new ArrayList<>(); private List<String> tabs = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); } private void initData() { tabs.add("新消息"); tabs.add("朋友圈"); tabs.add("公衆號"); fragments.add(new TabFragment(this,tabs.get(0))); fragments.add(new TabFragment(this,tabs.get(1))); fragments.add(new TabFragment(this,tabs.get(2))); } private void initView() { tabLayout = (TabLayout) findViewById(R.id.tayLayout); viewPager = (ViewPager) findViewById(R.id.viewPager); //設置TabLayout的模式 tabLayout.setTabMode(TabLayout.MODE_FIXED); viewPager.setAdapter(new TabAdapter(getSupportFragmentManager())); //關聯ViewPager和TabLayout tabLayout.setupWithViewPager(viewPager); } class TabAdapter extends FragmentPagerAdapter{ public TabAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return fragments.size(); } //顯示標籤上的文字 @Override public CharSequence getPageTitle(int position) { return tabs.get(position); } } }
代碼不長,initData方法中添加數據,initView方法初始化控件,跟咱們平時使用ViewPager的寫法差異不大。要注意的是TabLayout須要設置模式(即setTabMode方法),一共有兩種:佈局
TabLayout.MODE_FIXED
:當Tab較少,且佔滿整個屏幕時可使用這種模式;TabLayout.MODE_SCROLLABLE
:當Tab數量較多,屏幕寬度不夠時使用該模式,整個TabLayout是能夠左右滑動的。除此以外,咱們須要讓Tab顯示文字,要重寫FragmentPagerAdapter的getPageTitle方法,返回每個Tab的文字內容。最後可別忘了最關鍵的一步:使用setupWithViewPager方法關聯Viewpager和TabLayout,這樣二者纔會聯動。
運行一下,就能夠看到動態圖中的效果了。
默認的Tab字體大小有點小,看起來不太舒服,當咱們去修改字體大小時卻發現,TabLayout竟然沒有提供跟TextSize相關的屬性。不過不用急,TabLayout其實提供了一個更靈活的屬性app:tabTextAppearance
,它能夠修改字體的樣式,從而間接修改字體的大小。
咱們在style.xml中自定義一個樣式,繼承於TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse
,在屬性android:textSize中設置咱們想要的字體大小,這裏我設爲20sp。
<style name="TabTextSize" parent="TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"> <item name="android:textSize">20sp</item> </style>
接下來在佈局文件中使用就能夠了。
運行一下,這下看起來清楚多了。
TabLayout的標籤之間是默認沒有分割線的,若是咱們想添加分割線,讓標籤之間更有層次感的話,能夠添加如下的代碼:
//設置分割線 LinearLayout linearLayout = (LinearLayout) tabLayout.getChildAt(0); linearLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE); linearLayout.setDividerDrawable(ContextCompat.getDrawable(this, R.drawable.divider)); //設置分割線的樣式 linearLayout.setDividerPadding(dip2px(10)); //設置分割線間隔
自定義的分割線樣式:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#80c0c0c0" /> <size android:width="1dp" /> </shape>
setDividerPadding方法中輸入的參數是單位是px,咱們須要轉換成像素:
//像素單位轉換 public int dip2px(int dip) { float density = getResources().getDisplayMetrics().density; return (int) (dip * density + 0.5); }
如今咱們來嘗試一下這樣的效果:將tab的文字分爲兩行,第二行顯示信息的數目,固然,咱們並無真的信息,因此直接輸入一些假數據就能夠。爲了讓文字變爲兩行,咱們能夠加入換行符。
tabs.add("新消息"+"\n"+999); tabs.add("朋友圈"+"\n"+99); tabs.add("公衆號"+"\n"+9);
運行,發現文字確實分紅了兩行。可是等等,怎麼文字大小小了那麼多?
若是你查一下TabLayout的源碼,就會發現這一切早就命中註定了。源碼中有這麼一段:
if (mIconView != null && mIconView.getVisibility() == VISIBLE) { // If the icon view is being displayed, we limit the text to 1 line maxLines = 1; } else if (mTextView != null && mTextView.getLineCount() > 1) { // Otherwise when we have text which wraps we reduce the text size textSize = mTabTextMultiLineSize; }
這裏是設置Tab的icon和字體大小的,在else if代碼塊中,咱們發現了,當TextView的文本大於一行時,就會強制使用特定的字體(textSize = mTabTextMultiLineSize
),這就解釋了爲何咱們的字體設置不奏效了。
那麼,咱們該怎麼辦呢?
條條大路通羅馬,TabLayout早就給咱們準備了另外一條路了,那就是自定義標籤佈局。(舒適提示:下面的代碼對以前的改動較大,你們可能會以爲以前作的都是無用功,可是凡事老是按部就班的,請沒必要灰心。)
<?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:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/tv_tab_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="標籤名" android:textColor="@drawable/tab_color" android:textSize="16sp" /> <TextView android:id="@+id/tv_tab_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="99" android:textColor="@drawable/tab_color" android:textSize="16sp" /> </LinearLayout>
這裏咱們用到了一個選擇器,代碼以下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" android:color="#49dd12"/> <item android:state_selected="false" android:color="@android:color/darker_gray"/> </selector>
/** * 設置Tab的樣式 */ private void setTabView() { holder = null; for (int i = 0; i < tabs.size(); i++) { //依次獲取標籤 TabLayout.Tab tab = tabLayout.getTabAt(i); //爲每一個標籤設置佈局 tab.setCustomView(R.layout.tab_item); holder = new ViewHolder(tab.getCustomView()); //爲標籤填充數據 holder.tvTabName.setText(tabs.get(i)); holder.tvTabNumber.setText(String.valueOf(tabNumbers.get(i))); //默認選擇第一項 if (i == 0){ holder.tvTabName.setSelected(true); holder.tvTabNumber.setSelected(true); holder.tvTabName.setTextSize(18); holder.tvTabNumber.setTextSize(18); } } //tab選中的監聽事件 tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { holder = new ViewHolder(tab.getCustomView()); holder.tvTabName.setSelected(true); holder.tvTabNumber.setSelected(true); //選中後字體變大 holder.tvTabName.setTextSize(18); holder.tvTabNumber.setTextSize(18); //讓Viewpager跟隨TabLayout的標籤切換 viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(TabLayout.Tab tab) { holder = new ViewHolder(tab.getCustomView()); holder.tvTabName.setSelected(false); holder.tvTabNumber.setSelected(false); //恢復爲默認字體大小 holder.tvTabName.setTextSize(16); holder.tvTabNumber.setTextSize(16); } @Override public void onTabReselected(TabLayout.Tab tab) { } }); } class ViewHolder{ TextView tvTabName; TextView tvTabNumber; public ViewHolder(View tabView) { tvTabName = (TextView) tabView.findViewById(R.id.tv_tab_name); tvTabNumber = (TextView) tabView.findViewById(R.id.tv_tab_number); } }
建立一個setTabView方法來設置Tab的樣式,在for循環中爲每個標籤建立佈局。setCustomView方法能夠設置Tab的佈局,getCustomView則能夠獲取當前Tab的佈局。
咱們既然使用的是自定義的佈局,那麼選中時的樣式也要手動設置了。跟ViewPager相似,TabLayout也有本身的選中監聽事件(addOnTabSelectedListener
)。在標籤被選中時將狀態設置爲選中,並切換到相應的ViewPager頁面,未選中的頁面則將選中狀態設爲false便可。
補充一點,選中Tab後字體變大這一功能是我後面加上去的,因此代碼只在GitHub中更新了。因爲屬性android:textSize
不支持drawable文件,因此這裏不能用狀態選擇器,但好在代碼裏實現也不復雜,就沒必要過多解釋了。
既然TabLayout如此貼心地給咱們提供了自定義標籤佈局的方法,那麼咱們就要好好利用它,好比除了文字以外,咱們還能夠添加圖片,讓標籤頁的內容更加豐富。另外,TabLayout不必定非要放在頂部,也能夠放在底部,去掉下劃線以後就能夠實現與RadioGroup同樣的效果。