摘要:初步實現之前在知乎上看到有人對微信的設計改動將使用頻率高的朋友圈消息提醒和公眾號這三個功能獨立出來放在首頁。
1、介紹和準備
我們在使用手機App時不難會看到這樣的頁面上面是一組起導航作用的標簽,點擊標簽就會切換到相應的頁面;在不同的頁面中滑動時,標簽的樣式(文字大小或者顏色)也會發生變化。這樣你任何時候都能一眼看出自己停留在哪個頁面。這個布局出鏡率實在太高了,我甚至敢說每個學Android的人都寫過這樣的布局(下面就是知乎中的頁面)。
好了,廢話少說,我們照例先來分析一下這個布局的組成。標簽下面的頁面比較容易想到:整體是一個左右滑動的ViewPager,每一頁則可以用Fragment填充,也就是ViewPager+Fragment。但上面的標簽部分就有點頭大了,之前我們都是使用第三方的項目(如PagerSlidingTabStrip),高手的話也可以自定義一個控件。但是這樣并非長久之計,所以谷歌后來人性化地推出了自家的標簽控件TabLayout(注意可不要跟TableLayout搞混了,后者是Android的基本布局之一,而前者是一個控件)。TabLayout顧名思義就是包含Tab的布局,它包含在Design support library庫中,要使用它,你需要在先添加依賴庫:
我導入的是最新的26.0.0版本:
compile "com.android.support:design:26.0.0-alpha1"
準備完這些,我們可以開始寫代碼了。
2、初步實現之前在知乎上看到有人對微信的設計改動:將使用頻率高的朋友圈、消息提醒和公眾號這三個功能獨立出來放在首頁。我很贊同這樣的設計思路,所以今天就來弄一個簡陋版的吧。大體效果如下:
上面的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.html
2.2 創建Viewpager頁面(Fragment)為了能夠識別我們切換到的是ViewPager的哪個頁面,我們在Fragment中創建一個帶參數的構造函數,動態添加一個TextView,它的文本內容跟標簽的一致就好。
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; } }2.3 MainActivity代碼
先來看代碼:
public class MainActivity extends AppCompatActivity { private ViewPager viewPager; private TabLayout tabLayout; private Listfragments = new ArrayList<>(); private List 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,這樣兩者才會聯動。
運行一下,就可以看到動態圖中的效果了。
3、進階 3.1 修改標簽字體大小默認的Tab字體大小有點小,看起來不太舒服,當我們去修改字體大小時卻發現,TabLayout居然沒有提供跟TextSize相關的屬性。不過不用急,TabLayout其實提供了一個更靈活的屬性app:tabTextAppearance ,它可以修改字體的樣式,從而間接修改字體的大小。
我們在style.xml中自定義一個樣式,繼承于TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse ,在屬性android:textSize中設置我們想要的字體大小,這里我設為20sp。
接下來在布局文件中使用就可以了。
運行一下,這下看起來清楚多了。
3.2 添加分割線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)); //設置分割線間隔
自定義的分割線樣式:
setDividerPadding方法中輸入的參數是單位是px,我們需要轉換成像素:
//像素單位轉換 public int dip2px(int dip) { float density = getResources().getDisplayMetrics().density; return (int) (dip * density + 0.5); }3.2 顯示信息數目
現在我們來嘗試一下這樣的效果:將tab的文字分為兩行,第二行顯示信息的數目,當然,我們并沒有真的信息,所以直接輸入一些假數據就可以。為了讓文字變為兩行,我們可以加入換行符。
tabs.add("新消息"+" "+999); tabs.add("朋友圈"+" "+99); tabs.add("公眾號"+" "+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),這就解釋了為什么我們的字體設置不奏效了。
那么,我們該怎么辦呢?
3.3 自定義標簽條條大路通羅馬,TabLayout早就給我們準備了另一條路了,那就是自定義標簽布局。(溫馨提示:下面的代碼對之前的改動較大,大家可能會覺得之前做的都是無用功,但是凡事總是循序漸進的,請不必灰心。)
自定義標簽布局這里我們用到了一個選擇器,代碼如下:
標簽顏色選擇器在代碼中實現
/** * 設置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文件,所以這里不能用狀態選擇器,但好在代碼里實現也不復雜,就不必過多解釋了。
4、開拓思維既然TabLayout如此貼心地給我們提供了自定義標簽布局的方法,那么我們就要好好利用它,比如除了文字之外,我們還可以添加圖片,讓標簽頁的內容更加豐富。另外,TabLayout不一定非要放在頂部,也可以放在底部,去掉下劃線之后就可以實現與RadioGroup一樣的效果。
最后,是說好的源碼了:
CSDN
GitHub
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67124.html
摘要:但對于我們的對于界面還原度要求較高,對于之間的間距也有一些要求,所以也要處理,對于間距部分的處理可以按照之前的方式通過反射來完成。注意,這種方式因為需要計算的文字寬度,所以要放到設置完所有的后調用。 修改下劃線寬度的坑 效果如下: showImg(https://s2.ax1x.com/2019/04/18/ES2KYV.png); 代碼實現方式: 如果想要實現這種效果,最主要控制的就...
閱讀 3154·2021-11-22 14:45
閱讀 3300·2019-08-29 13:11
閱讀 2306·2019-08-29 12:31
閱讀 922·2019-08-29 11:21
閱讀 2991·2019-08-29 11:09
閱讀 3616·2019-08-28 18:11
閱讀 1420·2019-08-26 13:58
閱讀 1273·2019-08-26 13:27