TabLayout与ViewPager配合使用踩坑总结

前言:TabLayout是个神器,Android系统提供的一种控件,和ViewPager搭配使用,能帮助我们很快捷的实现头部Tab点击切换和ViewPager滑动关联滑动的动画效果,但是在我近期的使用过程中,还是发现了一些坑,耗费了一点时间,发现问题后简直被自己蠢哭了,特此记录一下使用中的坑,供大家参考。

先来看看我们将要实现的一个效果吧:
在这里插入图片描述

这是我们最终版的效果,那么其中的过程是什么样的呢?一步步往下看吧!

一、TabLayout的标签名称不显示

在这里插入图片描述
这个视频里会发现TabLayout的Tab名称不显示了,阿西吧!当TabLayout没有调用setupWithViewPager方法时,我们是能看到标签名称的,好吧,那么问题应该就出在了setupWithViewPager这个方法里,跟进去看看吧!

public void setupWithViewPager(@Nullable ViewPager viewPager) {
   setupWithViewPager(viewPager, true);
}

public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) {
   setupWithViewPager(viewPager, autoRefresh, false);
}

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;
}

里面调了几个重载方法,关注最后一个setupWithViewPager方法中的这一段代码:

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);
}

继续看看setPagerAdapter

void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {
    if (mPagerAdapter != null && mPagerAdapterObserver != null) {
        // If we already have a PagerAdapter, unregister our observer
        mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);
    }

    mPagerAdapter = adapter;

    if (addObserver && adapter != null) {
        // Register our observer on the new adapter
        if (mPagerAdapterObserver == null) {
            mPagerAdapterObserver = new PagerAdapterObserver();
        }
        adapter.registerDataSetObserver(mPagerAdapterObserver);
    }

    // Finally make sure we reflect the new adapter
    populateFromPagerAdapter();
}

最后执行了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));
            }
        }
    }
}

可以看到这个方法里首先执行removeAllTabs,清除了TabLayout的所有Tabs,然后遍历添加Tab:

addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);

我们要注意setText中设置的是mPagerAdapter中getPageTitle拿到的值,好吧,我们的PagerAdapter中那就得实现getPageTitle方法了:

@Nullable
@Override
public CharSequence getPageTitle(int position) {
  return girlList.get(position).name;
}

到这里TabLayout标签不展示的问题就解决了!

二、TabLayout标签的customView不显示

接着上面的问题,运行起来看看页面:
在这里插入图片描述
但是这里又有一个新的问题,我们会发现这里展示的样式并不是我们自己设置的View样式,从上面的代码中也可以知道,因为populateFromPagerAdapter中清除了所有的Tabs,然后取的是PagerAdapter中的title,所以这就会丢掉我们setCustomView中设置的样式,那我们应该怎么改呢?

这里最简单粗暴的方式是注释掉那两行代码:

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));
            }
        }
    }
}

这样的话就可以解决样式丢失的问题了。

三、TabLayout的tab点击无反应

在这里插入图片描述
如图所示,会发现我们关联ViewPager后,是可以左右滑动联通TabLayout切换动画的,但是当我们点击TabLayout的Tab的时候是没有任何反应的,可以理解为点击事件失效,刚开始我也以为是代码写的不对导致的,但是排查了半天发现并没有什么不妥之处,后来上网查询网友的答案,看到一句话,是不是ViewPager把TabLayout盖住了?

大吃一斤!!!

回过来看看自己的布局,用的是RelativeLayout,然后TabLayout放在ViewPager位置的上面

<com.zhangyan.mytablayout.tablayout.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:tabGravity="fill"
    app:tabIndicatorColor="#CBA788"
    app:tabIndicatorHeight="3dp"
    app:tabLineOffset="10dp"
    app:tabMode="fixed"
    app:tabSelectedTextColor="#CBA788"
    app:tabTextColor="#CCFFFFFF" />

<android.support.v4.view.ViewPager
  android:id="@+id/viewpager"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

由于ViewPager的Item并没有背景颜色,所以这样显示出来并无不妥之处,TabLayout仍然显示出来了,但是实际上是被ViewPager盖住了,所以这就是导致ViewPager滑动能联动TabLayout滑动,但是TabLayout点击无反应的原因!

四、TabLayout源码

在使用TabLayout和ViewPager时特别需要注意某些设置关联的时机,并且在该判空的地方做好空指针判断,避免出问题,另外由于系统的TabLayout源码支持自定义的可改动性较差,那么这里我们将系统的TabLayout源码拷贝出来了,进行了一些修改,在使用过程中可以对一些属性进行个性化的配置,不再局限于系统样式了,上述案例的源码和TabLayout的源码都在附件中,有需要的同学可以自取,么么哒各位~

源码地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章