欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

使用TabLayout看这篇就够了,使用TabLayout,在没有TabLayout

来源: 开发者 投稿于  被查看 40214 次 评论:230

使用TabLayout看这篇就够了,使用TabLayout,在没有TabLayout


首先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,那也很简单。有两个思路:

 
1:把指示条高度设为0:


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+文本吧?呵呵。。。又改??? 
还好还好,还是上面的方案,稍微修改下代码。在SpannableString中添加文本就可以了:

还好还好,至于图片的select效果应该很easy了,就不演示了,效果如下。

图片在左边?要不放右边吧,不不不,放上面,算了算了,放下面吧。到底放哪??? 


如果需求太奇葩,常规手段或者奇技淫巧都无法满足需求的话,就只有最后一招了:自定义。前面说过了TabItem本质上也是View,我们可以根据自己的实际需求来重写这个View。

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)); 
mTabLayout.getTabAt(1).setCustomView(getTabView(1));


这个getTabView(int i)方法是我们自己写的,用于初始化自定义Item的一些数据,如下:

没准切换的时候,产品经理让icon还要带点动画效果,所以这里我们随便来了个属性动画,在点击和切换的时候触发:

这个切换或者点击时候的事件就要我们自己实现了,所以我们要实现addOnTabSelectedListener这个接口,并在onTabSelected和onTabUnselected里做出相应的改变:

选中和未选中的时候我们分别调用下changeTabStatus,这个是我们自己写的状态改变的方法:

再来看下运行效果

是不是很简单!


有同学说Popupwindow的弹出事件与ViewPager的切换事件冲突了,其实这个稍加调整就可以了,我们定义个变量selectedPosition 用来标识当前被选中的位置,然后比较下当前点击的这个位置如果已经是被选中状态,则弹出Popupwindow,否则就切换ViewPager。

最后呢,我们再来解答下一个同学的问题。

好了,关于TabLayout的用法就探讨这里!


源码:https://github.com/stayinxing/TabLayout



作者:尹star

用户评论