TabLayout仍旧是移动端比较经常使用的一个控件,这里分析一下TabLayout,分别从下面几个方面进行解析:html
先看下支持库指南。以前的TabLayout是在support中使用,新的引用所有放到老AndroidX中java
使用老的库须要用android
implementation 'com.android.support:design:28.0.0'复制代码
Android 支持库的最新版本是28.0.0,这是最后一个google发布的支持库版本,如今google已将全部support包下的库都迁移至androidx包下面,之后的更新都只会在androidx包中进行。具体对照表点击进入迁移说明。git
这些变更是因为android的jetpack项目,意在帮助开发者快速实现应用开发,将一些经常使用的框架都整合进来了。github
官网详细介绍。segmentfault
使用新的库须要引用:缓存
implementation 'androidx.appcompat:appcompat:1.0.2'复制代码
TabLayout类的继承关系:bash
java.lang.Objectapp |
|||||
---|---|---|---|---|---|
↳框架 |
android.view.View |
||||
|
↳ |
android.view.ViewGroup |
|||
|
|
↳ |
android.widget.FrameLayout |
||
|
|
|
↳ |
android.widget.HorizontalScrollView |
|
|
|
|
|
↳ |
com.google.android.material.tabs.TabLayout |
TabLayout继承自 HorizontalScrollView
TabLayout提供了用于显示选项卡的水平布局。要显示的选项卡的填充是经过TabLayout.Tab
实例完成的。能够经过建立标签 newTab()
。在此处,您能够分别经过setText(int)
和更改选项卡的标签或图标setIcon(int)
。要显示选项卡,须要经过一种addTab(Tab)
方法将其添加到布局中。例如:
TabLayout tabLayout = ...;
tabLayout.addTab(tabLayout.newTab()。setText(“ Tab 1”));
tabLayout.addTab(tabLayout.newTab()。setText(“ Tab 2”));
tabLayout.addTab(tabLayout.newTab()。setText(“ Tab 3”));复制代码
应该添加一个监听器,addOnTabSelectedListener(OnTabSelectedListener)
以在任何选项卡的选择状态更改时获得通知。
还能够经过使用将项目添加到布局中的TabLayout TabItem
。用法示例以下:
<com.google.android.material.tabs.TabLayout
android:layout_height =“ wrap_content”
android:layout_width =“ match_parent”>
<com.google.android.material.tabs.TabItem
android:text =“ @ string / tab_text” />
<com.google.android.material.tabs.TabItem
android:icon =“ @ drawable / ic_android” />
</com.google.android.material.tabs.TabLayout>复制代码
若是ViewPager
将此布局与一块儿使用,则能够调用setupWithViewPager(ViewPager)
将二者连接在一块儿。该版式将从PagerAdapter
的页面标题中自动填充。
此视图还支持用做ViewPager装饰的一部分,而且能够像这样在布局资源文件中直接添加到ViewPager:
<androidx.viewpager.widget.ViewPager
android:layout_width =“ match_parent”
android:layout_height =“ match_parent”>
<com.google.android.material.tabs.TabLayout
android:layout_width =“ match_parent”
android:layout_height =“ wrap_content”
android:layout_gravity =“ top” />
</androidx.viewpager.widget.ViewPager>复制代码
这里不少人都使用的都是design 28,主工程的gradle的配置根据不一样状况改。
上面三种使用方法,咱们使用新的库androidx看下使用的效果图:
这里列举一下主要使用到到属性,只列举几个,具体可看官方文档。
属性 |
值 |
说明 |
---|---|---|
tabMode |
scrollable/fixed |
tab是水平可滚动的仍是固定宽,个数较少的时候可使用fixed,若是标签超出了屏幕范围,设置为scrollable比较好 |
tabGravity |
fill/center |
tab的布局是布满空间仍是居中 |
tabIndicatorHeight |
(dp/pix) |
底部滑动线条的高度 |
tabMaxWidth |
(dp/pix) |
Tab的最大宽度 |
tabTextColor |
颜色值 |
默认样式未选中颜色 |
app:tabSelectedTextColor |
颜色值 |
选中颜色 |
//TabLayout的基本使用
for(int i=0;i<mTitles.length;i++){
TabLayout.Tab tab=mTabLayout.newTab();
tab.setTag(i);
tab.setText(mTitles[i]);
mTabLayout.addTab(tab);
}复制代码
属性设置
app:tabIndicatorHeight="0dp" 复制代码
有时候想指示器的宽度小一些,能够参考文章Tablayout使用全解,一篇就够了 修改指示线长度(利用的反射,感受不如本身基于源码封装一个,能够自定义长度)。
TabItem有个上下结构的默认布局,这里使用默认的
tabLayout1.addTab(tabLayout1.newTab().setText("Tab 4").setIcon(R.mipmap.ic_launcher));复制代码
设置Tab内部的子控件的Padding:
app:tabPadding="xxdp"
app:tabPaddingTop="xxdp"
app:tabPaddingStart="xxdp"
app:tabPaddingEnd="xxdp"
app:tabPaddingBottom="xxdp"复制代码
设置整个TabLayout的Padding:
app:paddingEnd="xxdp"
app:paddingStart="xxdp"复制代码
设置最大的tab宽度:
app:tabMaxWidth="xxdp"复制代码
设置最小的tab宽度:
app:tabMinWidth="xxdp"复制代码
TabLayout开始位置的偏移量:
app:tabContentStart="50dp"复制代码
若是你要设置默认选中第三项,你能够这样:
mTabLayout.getTabAt(2).select();复制代码
初始化进入的时候,监听事件的三个方法都不会执行
tabLayout1.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
//选中了tab的逻辑
Log.i(TAG, "======我选中了===="+tab.getTag());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
//未选中tab的逻辑
Log.i(TAG, "======我未被选中===="+tab.getTag());
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
//再次选中tab的逻辑
Log.i(TAG, "======我再次被选中===="+tab.getTag());
}
});复制代码
onTabReselected为已经选中的tab再次点击会走到。
mTabLayout.getTabAt(position).isSelected()复制代码
有时候要监听某个Tab的点击事件,能够参考TabLayout基本属性全解 或者 tablayout增长选择tab 的事件和重写tab点击事件
这里有两种方式添加TabItem的自定义布局,其一种方式是在TabItem的xml中定义
<com.google.android.material.tabs.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/two"
android:layout="@layout/custom_indicator1"
android:text="娱乐" />复制代码
custom_indicator1.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
/>
<ImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>复制代码
custom_indicator1.xml文件内容,值得注意的是这里的TextView的id必须是“@android:id/text1”,ImageView的id必须是“@android:id/icon”,缘由来自于与TabLayout的源码中TabView的update方法。
这种方式只能事先肯定有几个Tab的时候用到,当这个Tab个数须要动态的建立的时候不能使用此方法。
另一种方式经过代码动态设置布局,布局的选中和未选中态的更新采用监听器动态修改的方式。
/**
* 设置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);
}
}复制代码
有一些写的比较好的文章分享了一些比较高级的功能。如,TabLayout的简单运用和若干问题的解决。
这篇中介绍了怎么加分割线,设置原有字体大小,自定义标签等。
在源码中能够看到再newTab中,customView的的建立。
@NonNull
public Tab newTab() {
Tab tab = sTabPool.acquire();
if (tab == null) {
tab = new Tab();
}
tab.mParent = this;
tab.mView = createTabView(tab);
if (mViewPagerTabEventListener != null) {
tab.setCustomView(mViewPagerTabEventListener.onCreateTab(tab.mView));
}
return tab;
}复制代码
最新的TabLayout源码能够经过下面的地址中看到,看到Google是由专门的material设计和工程团队负责此库。
先看下总体的类关系图
TabLayout继承HorizontalScrollView是一个横向滚动的ViewGroup,他包含一个子View(只能包含一个)SlidingTabStrip。
SlidingTabStrip为一个LinearLayout为TabLayout的内部类。全部的TabView都是它的子View.
TabView继承于LinearLayout,以Tab为数据源,来展现Tab的样式。最终用for循环被add进SlidingTabStrip
Tab是一个简单的View Model实体类,控制TabView的title, icon, custom layout id等属性。
TabItem继承于View. 用于在layout xml中来描述Tab. 须要注意的是,它不会add到SlidingTabStrip中去。它的做用是从xml中获取到text,icon,custom layout id等属性。TabLayout inflate到TabItem并获取属性到装配到Tab中,最终add到SlidingTabStrip中的仍是TabView.
这里调newTab()
方法建立了一个tab对象,而且用对象池把建立的tab对象缓存起来。而后将TabItem
对象的属性都赋值给tab对象。在createTabView(Tab tab)
这个方法中,首先从TabView
池中获取TabView
对象,若是不存在,则实例化一个对象,并调用tabView.setTab(tab)
方法来进行了数据绑定。
private static final Pools.Pool<Tab> sTabPool = new Pools.SynchronizedPool<Tab>(16);复制代码
public Tab newTab() {
Tab tab = sTabPool.acquire();
if (tab == null) {
tab = new Tab();
}
tab.mParent = this;
tab.mView = createTabView(tab);
if (mViewPagerTabEventListener != null) {
tab.setCustomView(mViewPagerTabEventListener.onCreateTab(tab.mView));
}
return tab;
}复制代码
private TabView createTabView(@NonNull final Tab tab) {
TabView tabView = mTabViewPool != null ? mTabViewPool.acquire() : null;
if (tabView == null) {
tabView = new TabView(getContext());
}
tabView.setTab(tab);
tabView.setFocusable(true);
tabView.setMinimumWidth(getTabMinWidth());
return tabView;
}复制代码
mTabStrip自己在HorizonScrollView中,因此直接经过scrollTo方法便可实现滚动的操做,这里只须要计算位置便可。
private int calculateScrollXForTab(int position, float positionOffset) {
if (mMode == MODE_SCROLLABLE) {
final View selectedChild = mTabStrip.getChildAt(position);
final View nextChild = position + 1 < mTabStrip.getChildCount()
? mTabStrip.getChildAt(position + 1)
: null;
final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
final int selectedLeft = selectedChild != null ? selectedChild.getLeft() : 0;
final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;
// base scroll amount: places center of tab in center of parent
int scrollBase = selectedLeft + (selectedWidth / 2) - (getWidth() / 2);
// offset amount: fraction of the distance between centers of tabs
int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset);
return (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR)
? scrollBase + scrollOffset
: scrollBase - scrollOffset;
}
return 0;
}复制代码
[1] 官方介绍
[2] MaterialDesign--(7)TabLayout的使用及其源码分析
[3] TabLayout基本属性全解
[6] MaterialDesign之对TabLayout的探索
[7] https://github.com/itgoyo/AndroidSource-Analysis/blob/master/chapter1/TabLayout%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md