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