咱们在使用手机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同样的效果。