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的源碼都在附件中,有需要的同學可以自取,麼麼噠各位~

源碼地址

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