一、概述
相信大家实际开发中会经常碰到需要实现类似于网易新闻Tab标签效果的界面,为了加快开发效率,以前会经常使用到JakeWharton大神的开源框架Viewpager Indicator,框架好是好,但是毕竟比较旧了。其实Google官方也提供了一个TabLayout控件来实现类似的效果。
TabLayout是Google提供的design包中的一个控件,其实现的效果类似于Viewpager Indicator中TabPageIndicator的效果,但是也提供了更多的功能。与TabpagerIndicator相比,TabLayout的优势:
- 使用更加方便
- 可以方便的自定义Tab上显示的View
- 提供了对Tab点击事件的监听方式(单次点击监听,重复点击监听)
- Tab的指示器带一个动画效果
使用之前我们需要添加design包:
Android Studio中在Gradle中添加:compile 'com.android.support:design:23.1.0'
Eclipse则需要手动添加SDK下\extras\android\support\design\libs中的Jar包
Eclipse则需要手动添加SDK下\extras\android\support\design\libs中的Jar包
二、TabLayout的使用
先来看看最简单的使用。我们先写这样一个布局
<LinearLayout xmlns:android="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">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="#00eeff"
app:tabSelectedTextColor="#00eeff"
app:tabTextColor="#000000" />
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
我们可以看到TabLayout中使用了三个属性
tabIndicatorColor 指示器(下划线)的颜色
tabTextColor Tab文字颜色
tabSelectedTextColor Tab被选中时文字的颜色
然后给ViewPager写一个简单的适配器
public class TabPageAdapter extends PagerAdapter {
private List<ImageView> imageViews;
private String[] titles;
public TabPageAdapter(List<ImageView> imageViews, String[] titles) {
this.imageViews = imageViews;
this.titles = titles;
}
@Override
public int getCount() {
return imageViews.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(imageViews.get(position));
return imageViews.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(imageViews.get(position));
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
}
然后在MainActivity中写一下几行代码就可以实现效果
//创建适配器
TabPageAdapter adapter = new TabPageAdapter(imageViewList, titles);
viewPager.setAdapter(adapter);
//把ViewPager和TabLayout关联
tabLayout.setupWithViewPager(viewPager);
setupWithViewPager()这个方法会在内部帮我们创建Tab添加到TabLayout,当然我们也可以手动自己添加
for (int i = 0; i < 5; i++) {
TabLayout.Tab tab = tabLayout.newTab();
//为Tab设置文字
tab.setText(titles[i]);
//为Tab设置图标
tab.setIcon(R.mipmap.ic_launcher);
//为tab设置自定义显示布局
//tab.setCustomView(customView);
tabLayout.addTab(tab);
}
//创建适配器
TabPageAdapter adapter = new TabPageAdapter(imageViewList, titles);
viewPager.setAdapter(adapter);
//设置Tab显示填满布局
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
//设置Tab滚动模式为默认
tabLayout.setTabMode(TabLayout.MODE_FIXED);
//设置指示器高度
tabLayout.setSelectedTabIndicatorHeight(10);
但这个时候我们就不能使用setupWithViewPager()来使TabLayout和ViewPager关联了,我们必须分别为ViewPager和TabLayout设置监听来实现关联
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
<span style="white-space:pre"> </span>//当tab被重复选择时回调的方法
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//如果想要指示器随着ViewPager一起滑动就使用此方法
//第三个参数设置为true,表示滑动后更新tab的选择状态
//就不需要在onPageSelected中写代码了
tabLayout.setScrollPosition(position, positionOffset, true);
}
@Override
public void onPageSelected(int position) {
// tabLayout.getTabAt(position).select();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
那为什么自己添加tab后不能用setupWithViewPager()呢?我们先来看看源码
public void setupWithViewPager(@NonNull ViewPager viewPager) {
final PagerAdapter adapter = viewPager.getAdapter();
if (adapter == null) {
throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
}
// First we'll add Tabs, using the adapter's page titles
setTabsFromPagerAdapter(adapter);
// Now we'll add our page change listener to the ViewPager
viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(this));
// Now we'll add a tab selected listener to set ViewPager's current item
setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
// Make sure we reflect the currently set ViewPager item
if (adapter.getCount() > 0) {
final int curItem = viewPager.getCurrentItem();
if (getSelectedTabPosition() != curItem) {
selectTab(getTabAt(curItem));
}
}
}
public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) {
removeAllTabs();
for (int i = 0, count = adapter.getCount(); i < count; i++) {
addTab(newTab().setText(adapter.getPageTitle(i)));
}
}
这样原因就很明显了setupWithViewPager()中会调用setTabsFromPageAdapter()方法,而这个方法中会清空TabLayout中已经添加的Tab,然后再根据传入的适配器确定Tab的个数重新添加Tab和根据adapter中getPageTitle()设置Tab的标签。所以如果你为Tab设置的自定义View无法显示时,很有可能就是你在设置自定义View后误用了setupWithViewPager()或setTabsFromPageAdapter()方法。