Material Design – TabLayout使用
1. 基本使用及最常见用法
2. 在Tablayout显示图标 及 源码解析
3. 自定义布局
1) 在build.gradle 添加依赖
implementation 'com.android.support:design:27.1.1'
2) 编写布局
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
.support.design.widget.TabLayout>
.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent">
.support.v4.view.ViewPager>
3) 编写Fragment及Activity
Fragment
public class TestFragment extends Fragment {
public static final String FRAGMENT_TYPE = "fragment_type";
private String mContent;
public static Fragment newInstance(String content) {
Fragment fragment = new TestFragment();
Bundle bundle = new Bundle();
bundle.putString(FRAGMENT_TYPE, content);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContent = getArguments().getString(FRAGMENT_TYPE);
}
private static final String TAG = "TestFragment";
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: "+container);
//这里的布局只是一个简单的FrameLayout,后面手动添加了一个TextView
ViewGroup layout = (ViewGroup) inflater.inflate(R.layout.fragment_test, container, false);
TextView textView = new TextView(getContext());
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
textView.setText(mContent);
textView.setLayoutParams(layoutParams);
layout.addView(textView);
return layout;
}
}
PagerAdapter类:注意这里getPageTitle返回的即为TabLayout的标题,至于为什么后面再讲
public class MyPagerAdapter extends FragmentPagerAdapter {
private List mFragments;
private String[] mTitles;
public MyPagerAdapter(FragmentManager fm, List fragments, String[] titles) {
super(fm);
this.mFragments = fragments;
this.mTitles = titles;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
}
Activity:这里简单的创建了4个TestFragment,关键的一句在于mTabLayout.setupWithViewPager(mViewPager)将TabLayout与ViewPager绑定
public class MainActivity extends AppCompatActivity {
private String[] mTitles = {"日报","主题","专栏","热门"};
private List mFragments;
private TabLayout mTabLayout;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTabLayout = findViewById(R.id.tabLayout);
mViewPager = findViewById(R.id.viewPager);
mFragments = new ArrayList<>();
mFragments.add(TestFragment.newInstance(mTitles[0]));
mFragments.add(TestFragment.newInstance(mTitles[1]));
mFragments.add(TestFragment.newInstance(mTitles[2]));
mFragments.add(TestFragment.newInstance(mTitles[3]));
mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), mFragments, mTitles));
mViewPager.setOffscreenPageLimit(2);
mTabLayout.setupWithViewPager(mViewPager);
}
}
tabBackground:标签的背景;
tabMode:fixed, 固定标签,tab均分,为默认值;scrollable,可滚动标签。
tabTextColor:标签字体颜色;
tabSelectedTextColor:标签选中字体颜色;
tabIndicatorColor:底部滑动的线条的颜色,默认是colorAccent;
tabIndicatorHeight:底部滑动线条Indicator的高度。
遗憾的是无法调节滑动线条的长度,网上有人使用反射获取属性去调节其高度。
想要在TabLayout上显示图标,这时候就需要使用到TabItem了,TabItem是用来配合作为TabLayout的子View使用的。
在布局文件TabLayout标签下添加TabItem
.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/icon4"
android:text="日报"/>
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/icon3"
android:text="主题"/>
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/icon2"
android:text="专栏"/>
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/icon1"
android:text="热门"/>
.support.design.widget.TabLayout>
这时候再点击运行,
你会,咦。。。
怎么没有图标出现
不急不急
把mTabLayout.setupWithViewPager(mViewPager)
这一行注释掉
替换为以下两行代码
mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
这时图标就显示出来了。
为什么要替换为另外两句代码呢?这两句其实就是setUpWithViewPager的核心代码,
那这两句与setUpWithViewPager有什么不同呢?与让我们到源码里面看看吧
private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh,
boolean implicitSetup) {
if (mViewPager != null) {
// If we've already been setup with a ViewPager, remove us from it
if (mPageChangeListener != null) {
mViewPager.removeOnPageChangeListener(mPageChangeListener);
}
if (mAdapterChangeListener != null) {
mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
}
}
if (mCurrentVpSelectedListener != null) {
// If we already have a tab selected listener for the ViewPager, remove it
removeOnTabSelectedListener(mCurrentVpSelectedListener);
mCurrentVpSelectedListener = null;
}
if (viewPager != null) {
mViewPager = viewPager;
// Add our custom OnPageChangeListener to the ViewPager
if (mPageChangeListener == null) {
mPageChangeListener = new TabLayoutOnPageChangeListener(this);
}
mPageChangeListener.reset();
viewPager.addOnPageChangeListener(mPageChangeListener);
// Now we'll add a tab selected listener to set ViewPager's current item
mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
addOnTabSelectedListener(mCurrentVpSelectedListener);
final PagerAdapter adapter = viewPager.getAdapter();
if (adapter != null) {
// Now we'll populate ourselves from the pager adapter, adding an observer if
// autoRefresh is enabled
setPagerAdapter(adapter, autoRefresh);
}
// Add a listener so that we're notified of any adapter changes
if (mAdapterChangeListener == null) {
mAdapterChangeListener = new AdapterChangeListener();
}
mAdapterChangeListener.setAutoRefresh(autoRefresh);
viewPager.addOnAdapterChangeListener(mAdapterChangeListener);
// Now update the scroll position to match the ViewPager's current item
setScrollPosition(viewPager.getCurrentItem(), 0f, true);
} else {
// We've been given a null ViewPager so we need to clear out the internal state,
// listeners and observers
mViewPager = null;
setPagerAdapter(null, false);
}
mSetupViewPagerImplicitly = implicitSetup;
}
可以看到函数里先移除了之前的监听器,然后在将新的监听器添加上去,
这是与我们的做法一致的,那么到底哪里不一样呢?让我们看到调用的setPagerAdapter(adapter, autoRefresh);方法内调用的populateFromPagerAdapter();
void populateFromPagerAdapter() {
removeAllTabs();
if (mPagerAdapter != null) {
final int adapterCount = mPagerAdapter.getCount();
for (int i = 0; i < adapterCount; i++) {
addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
}
// Make sure we reflect the currently set ViewPager item
if (mViewPager != null && adapterCount > 0) {
final int curItem = mViewPager.getCurrentItem();
if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
selectTab(getTabAt(curItem));
}
}
}
}
在这个方法内,他首先将所有tab清除掉了,然后再用for循环一个一个加回来,tab个数取自PagerAdapter,并且title取自PagerAdapter的getPageTitle()方法,这也就说明了前面的疑问,为什么getPageTitle返回的即为TabLayout的标题。
然后他这里没有重新设置图标,只设置了一下标题,这也就是为什么只调用setUpWithViewPager不显示图标的原因了。
如果你想问下有没有什么其他方法?那肯定是有的啊。
我们可以照常调用setUpWithViewPager
不用在布局文件中添加TabItem,而是在代码中设置添加TabItem,记住如果调用了setuPWithViewPager,那么只有在调用它之后的TabItem图标设置才会有效,添加如下代码重新设置图标,这里可以不用设置文字
mTabLayout.getTabAt(0).setText(mTitles[0]).setIcon(R.drawable.icon1);
mTabLayout.getTabAt(1).setText(mTitles[1]).setIcon(R.drawable.icon2);
mTabLayout.getTabAt(2).setText(mTitles[2]).setIcon(R.drawable.icon3);
mTabLayout.getTabAt(3).setText(mTitles[3]).setIcon(R.drawable.icon4);
如果不使用setupWithViewPager呢?
那就手动调用mTabLayout.addTab(mTabLayout.newTab().setText().setIcon())
代码中创建TabItem并配合
mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
来完成Tablayout与ViewPager的联动
其他操作
显示图标其实还有其他一些骚操作,我们可以看回自己的PagerAdapter类的代码,getPageTitle()方法其实返回的是CharSequence类实例,熟悉继承关系的知道其实String 和 SpannableString 都是继承自CharSequence的,用过SpannableString的可能会知道,SpannableString 是可以实现图标和文字的组合显示的,那我们就可以返回一个包含图标和文字的SpannableString,只调用setupWithViewPager实现图标和文字的显示,感兴趣的可以看我之前关于SpannableString的一篇博客Android – SpannableString 实现富文本效果用法全解析,讲解了SpannableString的所有用法。
这时候问题又来了,如果我们的UI不是这种上面图标,下面文字的形式呢?我们的图标可能在上边,在下边,在左边,在右边,甚至不止图标和文字,还要有其他元素。
没问题,没问题,自定义布局通通满足你。
xml中设置
.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/custom_container1"/>
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/custom_container2"/>
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/icon3"
android:text="专栏"/>
.support.design.widget.TabItem
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="@drawable/icon4"
android:text="热门"/>
.support.design.widget.TabLayout>
如代码中所示,前面两个TabItem使用了自定义布局。
custom_container1布局代码如下,custom_container2也是类似的
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="1"
android:text="日报" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon1"/>
LinearLayout>
如果你无法显示图标,那就再仔细看看这篇文章的第二部分吧,答案都在里面了。
代码中创建自定义TabItem
mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.custom_container1));
mTabLayout.addTab(mTabLayout.newTab().setCustomView(R.layout.custom_container2));
关于TabLayout的内容就是这些了,如果帮助到了你,请点个赞吧,如果有什么不懂或新思路,也可以留言交流哦!