首先TabLayout一般都是配合Viewpager使用的,Viewpager里的Fragment随着顶部的Tab一起联动,这种场景再熟悉不过了。在没有TabLayout的日子里关于这种设计一般都是自己实现的。
上代码,看效果 先来个简单通俗的代码: 上面代码的运行效果如下: 为了使用TabLayout,我们要让Activity继承自AppCompatActivity,但有时候你项目里的BaseActivity却是继承自FragmentActivity的,这就尴尬了。其实没关系的, AppCompatActivity 也是extends FragmentActivity的。可以把BaseActivity extends AppCompatActivity。如果不想这么做也可以,可以指定当前Activity的theme为 android:theme="@style/Theme.AppCompat" 然后build.gradle文件在dependencies里加上 compile 'com.android.support:design:25.0.0' 然后基本上就不会有什么问题了。 下面来解析下TabLayout的一些基本属性: 重要的属性基本就这些,其他简单的属性可以自己去摸索,这里选中和未选中的字体颜色,可以根据自己的设计自行修改,同样指示条的高度颜色也可以随意修改。 但假如我的设计里不需要指示条怎么办,好像没发现隐藏的API,那也很简单。有两个思路: app:tabIndicatorHeight="0dp" 2:把指示条的颜色设为透明: app:tabIndicatorColor="@color/transparent" 效果如下: TabItem 在高版本的design库里已经有了TabItem,TabItem是作为TabLayout的子View而配合使用的,点进去发现其实代码很简单,就是个自定义View。 所以当我们的需求能够明确知道Tab的个数时,可以在xml里直接添加TabItem。但是但是,心细的你不知道有没有发现问题,我在上面的代码中,tab明明设置的小写,但是运行出来确是大写: 事先申明我可没在代码里重新设置文本,就是这么操蛋。好在天无绝人之路,找到了一个属性叫app:tabTextAppearance,这是Tablayout的属性。TabItem代码简单到几乎没有什么属性可供设置,什么字体大小,颜色貌似都设置不了。 所以我们自己写了个样式,然后酱写: app:tabTextAppearance="@style/MyTabLayoutTextAppearance" MyTabLayoutTextAppearance里的代码如下: 这里的android:textAllCaps属性就是控制字体大小写的,TabLayout里默认是true,我们手动改成false即可,我们顺便设置了下字体。 但是但是,问题又来了,我设置的字体大小貌似没什么卵用,无论我怎么调节字体大小就是不变。呵呵,还是要从tabTextAppearance这个属性来着手。 下面我们把代码改成这样: app:tabTextAppearance="@android:style/TextAppearance.Holo.Large" 这下好了,字体的大小写解决了,字体大小也解决了。这样的属性我们找到了3组, 大 小 分别设置字体为大中小,说实话,这玩意真的不太好用,跟其他控件比起来,这个属性设置有点绕。 不要用文本了,改成icon吧,wtf,TabItem根本没有这样的属性啊,TabLayout貌似也没有啊。怎么搞?TabLayout没有明确地提供向Tab中设置图标的途径,但是很多事情总可以另辟蹊径。我们知道,Tab是使用adapter中的getPageTitle()方法做其显示的内容,这个方法返回类型为CharSequence。于是,我们可以在PagerAdapter中重写getPageTitle()方法,创建一个SpannableString,而将图标放置在ImageSpan中,设置在SpannableString中: 好了,运行起来,效果有了。 要不改成icon+文本吧?呵呵。。。又改??? 还好还好,至于图片的select效果应该很easy了,就不演示了,效果如下。 图片在左边?要不放右边吧,不不不,放上面,算了算了,放下面吧。到底放哪??? icon在右边 icon在上边 可以发现通过自定义View的方式我们可以随意摆放文本和icon的位置,无所谓上下左右,处理起来都是一样的。甚至一个tab想放两个icon或者两个文本什么的都不在话下。总体来讲Tablayout的坑还是蛮多的,很多API都没提供,或者提供了但留了很多坑,这很google,一方面给你一个很常用的控件,一方面这个控件又留了很多坑,最后这个控件带给你无限想象和发挥,根据自己的想法,动手去实现吧。 关于自定义Tablayout我们放在后面再说,我们再来看看Tablayout的一些其他“坑”。 开发过程中有同学需要修改abIndicator这个指示条的长度。关于这个问题我只能又呵呵了,因为控件居然没有提供修改tabIndicator长度的API。如果非要强行修改就只能用反射了,但有时候需求就是这么操蛋。直接上代码吧,调用下面这个方法,传入左右间距即可,具体长度可以根据实际情况调试。 setIndicator(this, mTabLayout, 20, 20); 运行效果如下: 又有同学提到TabItem动态添加比较好,那是自然的,很多时候我们的数据可能是从服务器下来的,具体有多少个TabItem 有可能也是不太确定的,这个时候当然是动态添加比较好。关于TabItem 的动态添加也很简单。 或许有些时候是从一个集合里读取数据填充,那就要来个for循环了,这个根据自己的实际情况来定。 到现在我们还没有上TabLayout的用法相关代码,下面我们来一个相对标准的使用简介,通常情况下大家会这么写。 运行起来之后,居然是这个样子的。我的标题呢? 官方推荐我们使用setupWithViewPager()方法,来完成这种Tablayout+Viewpager+Fragment组合的数据绑定,上面的代码实在看不出上面问题,但是我的标题呢?我们点进setupWithViewPager()方法查阅了下源码。 这个方法也不算太长,只是对viewPager的状态做了一些检查校验,设置了一些监听。看这一句: setPagerAdapter方法里又调用populateFromPagerAdapter(); 点进populateFromPagerAdapter() 方法第一句就是removeAllTabs(); 就是这么简单粗暴,直接全部清理掉了,我们且不去探讨源码中的这个逻辑是否有问题,单就setupWithViewPager()这个方法来说,这也算是一个不大不小的坑。所以我们自己在写代码的时候要注意避让。那关于上面我们自己写的代码就要做些调整,我们修改下initFragment方法,主要是代码语句的顺序上做些调整,如下: 代码逻辑上,我们在setupWithViewPager()方法最终remove掉了标题栏之后再重新设置标题,这样就不会出现标题栏消失的问题了。 那既然setupWithViewPager()这方法这么操蛋,我们可不可以不用这个方法来做数据视图绑定,当然也是可以的,但是官方还是推荐我们用setupWithViewPager()方法。我们先把setupWithViewPager()方法注掉看看会如何: 好像ViewPager和TabLayout之间的纽带断了,不会联动了。那我们就模仿setupWithViewPager()方法的源码让它们联动起来。我们要做的就是点击TabLayout的时候ViewPager会跟着滚动。滑动ViewPager的时候TabLayout会跟着滚动,那就简单了,我们分别给这二位设置个监听就好了。 比较起来好像还是setupWithViewPager()更方便些,虽然有坑。 然而然而,又有同学像我求助了,他们的设计把下面的abIndicator设计成两端圆角的了,这个用Tablayout怎么做。相信此刻该同学内心是崩溃的,但是没关系,I can do it,这里当然只能自定义TabItem啦,前面已经说了,现在我们正式探讨这个问题。 需求这东西是很随意的,有时是不会管你标准不标准,规范不规范的。譬如这样: 拿标准的Tablayout就套不进去了,首先它没有tabIndicator,关于如何去除tabIndicator在之前已经讲过,这里不再赘述。其次这两个Tab一个是有icon,一个是没有icon的。当然这个需求本身还是很简单的,假如不用Tablayout也无非就是写个布局,切换viewpager的时候对应的状态改变。但这里我就非要用Tablayout,通过这个例子来说明我们要讲的问题。 对于上面的需求,我们可以自定义TabItem来实现,这个算是比较简单的需求,有时候可能会更复杂,我们都可以通过自定义来达到想要的效果。这是我们需要的layout,一个线性布局,左边是文字,右边是icon。 TabLayout有一个方法叫setCustomView(),通过调用这个方法可以把我们自定义的布局塞进去。那具体的调用就是这样: mTabLayout.getTabAt(0).setCustomView(getTabView(0)); 这个getTabView(int i)方法是我们自己写的,用于初始化自定义Item的一些数据,如下: 没准切换的时候,产品经理让icon还要带点动画效果,所以这里我们随便来了个属性动画,在点击和切换的时候触发: 这个切换或者点击时候的事件就要我们自己实现了,所以我们要实现addOnTabSelectedListener这个接口,并在onTabSelected和onTabUnselected里做出相应的改变: 选中和未选中的时候我们分别调用下changeTabStatus,这个是我们自己写的状态改变的方法: 再来看下运行效果 是不是很简单! 有同学说Popupwindow的弹出事件与ViewPager的切换事件冲突了,其实这个稍加调整就可以了,我们定义个变量selectedPosition 用来标识当前被选中的位置,然后比较下当前点击的这个位置如果已经是被选中状态,则弹出Popupwindow,否则就切换ViewPager。 最后呢,我们再来解答下一个同学的问题。 好了,关于TabLayout的用法就探讨这里! 源码:https://github.com/stayinxing/TabLayout
1:把指示条高度设为0:
还好还好,还是上面的方案,稍微修改下代码。在SpannableString中添加文本就可以了:
如果需求太奇葩,常规手段或者奇技淫巧都无法满足需求的话,就只有最后一招了:自定义。前面说过了TabItem本质上也是View,我们可以根据自己的实际需求来重写这个View。
mTabLayout.getTabAt(1).setCustomView(getTabView(1));
作者:尹star