TabLayout+ViewPager 实现app底部Tab布局

前言

在谷歌发布Android Design Support Library之前,底部tab布局的实现方法有不少,我最初用的是LinearLayout,后来用过RadioGroup模拟、其它方法没有用过,虽然这些方法能达到效果,但个人一直觉得不爽。Google在2015的IO大会带来了全新的Android Design Support Library,里面包含了许多新控件,这些新控件有许多是把以前的一些第三方开源库官方化,实现起来更为方便。其中的TabLayout控件可以实现底部tab布局。

2016年4月9日更新一个问题:由于TabLayout继承自HorizontalScrollView,因此,无论如何布局,在TabLayout上滑动时,总会能滑出边界一小段距离,也就是说TabLayout不是完全固定的,是可以Scroll的,用代码设置一下就可以了:

        // 禁止滚动,否则即便怎样,都可以滚动一小段,因为TabLayout继承自HorizontalScrollView;
        tabLayout.setOnTouchListener { view, motionEvent -> true }

先看看效果:

[![](/images/2016/04/Screenshot_2016-04-03-22-54-08-169x300.jpeg)](/images/2016/04/Screenshot_2016-04-03-22-54-08.jpeg)

下面我们来看看怎么简单实现。

过程

TabLayout和ViewPager分别是属于design和v4包下的,所以先在app的build.gradle中添加以下依赖:

// version define;
buildscript {
    versions['supportLib'] = '23.2.1'
}
dependencies {
    compile "com.android.support:support-v4:$versions.supportLib"
    compile "com.android.support:design:$versions.supportLib"
}

然后在主布局文件content_main.xml中添加布局控件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/rlContent"
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.test.MainActivity"
    tools:showIn="@layout/activity_main"
    >

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_alignParentBottom="true"
        android:background="@drawable/bg_bottom_tab"
        app:tabGravity="fill"
        app:tabIndicatorHeight="0dp"
        app:tabMode="fixed"
        app:tabBackground="@android:color/transparent"
        >
    </android.support.design.widget.TabLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/tabLayout"
        android:scrollbars="none"
        >
    </android.support.v4.view.ViewPager>

</RelativeLayout>

用ViewPager和TabLayout来实现Bottom Tab布局,比以前的方法简单很多。
属性解释:

android:layout_alignParentBottom="true" //对齐底部,TabLayout就放置到底部了
android:background="@drawable/bg_bottom_tab" // 设置整个TabLayout的背景
app:tabGravity="fill" // 各个tab的gravity设置为fill
app:tabIndicatorHeight="0dp" // tab底部指示条高度设置为0不显示
app:tabMode="fixed" // tabLayout中的tab不可滑动
app:tabBackground="@android:color/transparent" // 这是tabLayout中的每个tab的背景,这里设置为透明是避免点击时出现灰色背景

在代码中初始化布局,设置适配器:

// 各个tab item的标题文字
val titlesBottomTab by lazy {
resources.getStringArray(R.array.titleBottomTab)
}
// 各个tab item的背景drawable(用selector实现背景变化)
val bgTabs = listOf(R.drawable.bg_tab1, R.drawable.bg_tab2, R.drawable.bg_tab3, R.drawable.bg_tab4)
val homeFragments = listOf(Tab1Fragment.newInstance("", ""), Tab2Fragment.newInstance("", ""), Tab3Fragment.newInstance("", ""), Tab4Fragment.newInstance("", ""))
private fun initViews(){
    viewPager.adapter = object : FragmentPagerAdapter(supportFragmentManager){
        override fun getPageTitle(position: Int): CharSequence? = titlesBottomTab[position]
        override fun getCount(): Int = titlesBottomTab.size
        override fun getItem(position: Int): Fragment? = homeFragments[position]
    }

    tabLayout.setupWithViewPager(viewPager)

    for (i in 0..bgTabs.lastIndex) {
        val tmpView = layoutInflater.inflate(R.layout.bottom_tab_item, null)
        tmpView.tabItemImage.background = resources.getDrawable(bgTabs[i])
        tmpView.tabItemText.text = titlesBottomTab[i]
        tabLayout.getTabAt(i)?.customView = tmpView
        if (i == viewPager.currentItem) tmpView.isSelected = true
    }

    tabLayout.setOnTabSelectedListener(object :TabLayout.OnTabSelectedListener{
        override fun onTabUnselected(tab: TabLayout.Tab?) {
        }

        override fun onTabSelected(tab: TabLayout.Tab?) {
            var idxSelectedTab = 0
            for (i in 0..bgTabs.lastIndex) {
                if (tab == tabLayout.getTabAt(i)){
                    idxSelectedTab = i
                    break
                }
            }
            viewPager.currentItem = idxSelectedTab
        }

        override fun onTabReselected(tab: TabLayout.Tab?) {
        }
    })
}

自定义View设置到tab中,原生的那个图标和文字的比例实在是看不惯

<!--bottom_tab_item.xml->

<?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">

<ImageView
    android:id="@+id/tabItemImage"
    android:layout_width="36dp"
    android:layout_height="36dp" />

<TextView
    android:id="@+id/tabItemText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:singleLine="true"
    android:textColor="@color/color_tab_item_text"
    android:textAppearance="@style/TextAppearance.AppCompat.Small" />

</LinearLayout>
文字的颜色变化也是用color selector实现的(@color/color_tab_item_text)

以上就是简单用TabLayout+ViewPager实现app底部Tab布局的整个过程。

参考:

http://stackoverflow.com/questions/31640563/how-do-i-change-a-tab-background-color-when-using-tablayout

http://stackoverflow.com/questions/30614272/how-to-create-app-bar-with-icons-using-tablayout-android-design

http://www.jianshu.com/p/adf7a994613a