- 接上一篇继续,有同学有在下面留言问tabIndicator这个指示条的长度怎么修改。关于这个问题只能又呵呵了,因为控件居然没有提供修改tabIndicator长度的API。如果非要强行修改就只能用反射了,但有时候需求就是这么操蛋。直接上代码吧,调用下面这个方法,传入左右间距即可,具体长度可以根据实际情况调试。
setIndicator(this, mTabLayout, 20, 20);
public void setIndicator(MainActivity context, TabLayout tabs, int leftDip, int rightDip) {
Class<?> tabLayout = tabs.getClass();
Field tabStrip = null;
try {
tabStrip = tabLayout.getDeclaredField("mTabStrip");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
tabStrip.setAccessible(true);
LinearLayout ll_tab = null;
try {
ll_tab = (LinearLayout) tabStrip.get(tabs);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
int left = (int) (getDisplayMetrics(context).density * leftDip);
int right = (int) (getDisplayMetrics(context).density * rightDip);
for (int i = 0; i < ll_tab.getChildCount(); i++) {
View child = ll_tab.getChildAt(i);
child.setPadding(0, 0, 0, 0);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
params.leftMargin = left;
params.rightMargin = right;
child.setLayoutParams(params);
child.invalidate();
}
}
运行效果如下:
- 又有同学提到TabItem动态添加比较好,那是自然的,很多时候我们的数据可能是从服务器下来的,具体有多少个TabItem 有可能也是不太确定的,这个时候当然是动态添加比较好。关于TabItem 的动态添加也很简单。
tabLayout.addTab(tabLayout.newTab().setText("A"));
tabLayout.addTab(tabLayout.newTab().setText("B"));
tabLayout.addTab(tabLayout.newTab().setText("C"));
或许有些时候是从一个集合里读取数据填充,那就要来个for循环了,这个根据自己的实际情况来定。
- 到现在我们还没有上TabLayout的用法相关代码,下面我们来一个相对标准的使用简介,通常情况下大家会这么写。
public class MainActivity extends AppCompatActivity {
ViewPager mViewPager;
ListFragment mFragment1;
ListFragment mFragment2;
ListFragment mFragment3;
PagerAdapter mPagerAdapter;
private TabLayout mTabLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setOffscreenPageLimit(2);
mTabLayout = (TabLayout) findViewById(R.id.toolbar_tab);
initFragment();
}
private void initFragment() {
mTabLayout.addTab(mTabLayout.newTab().setText("A"));
mTabLayout.addTab(mTabLayout.newTab().setText("B"));
mTabLayout.addTab(mTabLayout.newTab().setText("C"));
if (mFragment1 == null) {
mFragment1 = new ListFragment();
}
if (mFragment2 == null) {
mFragment2 = new ListFragment();
}
if (mFragment3 == null) {
mFragment3 = new ListFragment();
}
mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPagerAdapter);
mTabLayout.setupWithViewPager(mViewPager);
}
public class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return mFragment1;
} else if (position == 1) {
return mFragment2;
} else if (position == 2) {
return mFragment3;
}
return null;
}
@Override
public int getCount() {
return 3;
}
}
}
运行起来之后,居然是这个样子的。我的标题呢?
官方推荐我们使用setupWithViewPager()方法,来完成这种Tablayout+Viewpager+Fragment组合的数据绑定,上面的代码实在看不出上面问题,但是我的标题呢?我们点进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;
}
这个方法也不算太长,只是对viewPager的状态做了一些检查校验,设置了一些监听。看这一句:
if (adapter != null) {
// Now we'll populate ourselves from the pager adapter, adding an observer if
// autoRefresh is enabled
setPagerAdapter(adapter, autoRefresh);
}
setPagerAdapter方法里又调用了populateFromPagerAdapter();
点进populateFromPagerAdapter() 方法第一句就是:
removeAllTabs();
/**
* Remove all tabs from the action bar and deselect the current tab.
*/
public void removeAllTabs() {
// Remove all the views
for (int i = mTabStrip.getChildCount() - 1; i >= 0; i--) {
removeTabViewAt(i);
}
for (final Iterator<Tab> i = mTabs.iterator(); i.hasNext();) {
final Tab tab = i.next();
i.remove();
tab.reset();
sTabPool.release(tab);
}
mSelectedTab = null;
}
就是这么简单粗暴,直接全部清理掉了,我们且不去探讨源码中的这个逻辑是否有问题,单就setupWithViewPager()这个方法来说,这也算是一个不大不小的坑。所以我们自己在写代码的时候要注意避让。那关于上面我们自己写的代码就要做些调整,我们修改下initFragment方法,主要是代码语句的顺序上做些调整,如下:
private void initFragment() {
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
if (mFragment1 == null) {
mFragment1 = new ListFragment();
}
if (mFragment2 == null) {
mFragment2 = new ListFragment();
}
if (mFragment3 == null) {
mFragment3 = new ListFragment();
}
mPagerAdapter = new PagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(mPagerAdapter);
mTabLayout.setupWithViewPager(mViewPager);
mTabLayout.getTabAt(0).setText("A");
mTabLayout.getTabAt(1).setText("B");
mTabLayout.getTabAt(2).setText("C");
}
代码逻辑上,我们在setupWithViewPager()方法最终remove掉了标题栏之后再重新设置标题,这样就不会出现标题栏消失的问题了。
- 那既然setupWithViewPager()这方法这么操蛋,我们可不可以不用这个方法来做数据视图绑定,当然也是可以的,但是官方还是推荐我们用setupWithViewPager()方法。我们先把setupWithViewPager()方法注掉看看会如何:
好像ViewPager和TabLayout之间的纽带断了,不会联动了。那我们就模仿setupWithViewPager()方法的源码让它们联动起来。我们要做的就是点击TabLayout的时候ViewPager会跟着滚动。滑动ViewPager的时候TabLayout会跟着滚动,那就简单了,我们分别给这二位设置个监听就好了。
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
changeTabStatus(tab, false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
比较起来好像还是setupWithViewPager()更方便些,虽然有坑。
- 原本计划写自定义TabItem的,写着写着就跑偏了,那还是留在下篇吧。先到这里了。
完整代码:GitHub