使用系統TabLayout的app快來修Bug
前言
系統 TabLayout 和 ViewPager 配合使用時有個 Bug,當切換 Tab 的時候,Tab 會整體往左抖一下,這個抖動速度很快,大家稍微注意點能看到,那麼筆者在公司做業務時也有使用到系統的 TabLayout ,進行視覺校驗的時候沒逃過設計師的法眼,設計師要求高是件好事,但這個時候重新寫一個也不現實,那麼該怎麼辦呢?
問題描述
先來看看直接使用系統 TabLayout 而出現問題的一些App:
問題分析
在分析問題之前,我們先回顧下這個 Bug 復現的場景:先選中一個靠後的 Tab,然後滑動 TabLayout 到最左邊,點選第一個 Tab,會發現整個 TabLayout 往左抖了一下,速度很快,但無法忽視
那麼我們要解決的就是快速抖動的問題。
想解決這個問題,TabLayout 的原始碼還是得分析的,TabLayout 直接繼承的 HorizontalScrollView,不難推測,抖動的產生其實就是被執行了 scroll。
我們回想下,讓 TabLayout 發生 scroll 行為的場景會有哪些?
o 直接選中指定 Tab
o 滑動 ViewPager
我們發現的 Bug 出現的場景是點選 Tab 發生的,那麼我們點選了 Tab 後符合上面說的場景一,那麼 Tab 切換後會導致 ViewPager 滑動,那也會觸發 scroll,擦,難道就是因為這樣,導致了閃了一下?只能看看原始碼了:
首先看看點選 Tab 觸發的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// TabLayout#selectTab void selectTab(final Tab tab, boolean updateIndicator) { final Tab currentTab = mSelectedTab; if (currentTab == tab) { ... } else { final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION; if (updateIndicator) { if ((currentTab == null || currentTab.getPosition() == Tab.INVALID_POSITION) && newPosition != Tab.INVALID_POSITION) { ... } else { // 讓 Tab 做動畫 animateToTab(newPosition); } ... } ... } } |
再看 TabLayout#animateToTab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
private void animateToTab(int newPosition) { if (newPosition == Tab.INVALID_POSITION) { return; } ... final int startScrollX = getScrollX(); final int targetScrollX = calculateScrollXForTab(newPosition, 0); if (startScrollX != targetScrollX) { // 建立 scroll 動畫 ensureScrollAnimator(); mScrollAnimator.setIntValues(startScrollX, targetScrollX); // scroll 動畫開始執行 mScrollAnimator.start(); } // Now animate the indicator mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION); } |
再看看 ensureScrollAnimator 做了啥:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private void ensureScrollAnimator() { if (mScrollAnimator == null) { mScrollAnimator = new ValueAnimator(); mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); mScrollAnimator.setDuration(ANIMATION_DURATION); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animator) { // 屬性動畫回撥中呼叫 scrollTo scrollTo((int) animator.getAnimatedValue(), 0); } }); } } |
可以看到點選 Tab 最終會使 TabLayout 發生 scroll 行為。
繼續順著剛才說的點選 Tab 的時候也會觸發 ViewPager 的滑動,我們看看 ViewPager 滑動方法裡做了啥:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// TabLayout#TabLayoutOnPageChangeListener public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener { ... @Override public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { final TabLayout tabLayout = mTabLayoutRef.get(); if (tabLayout != null) { ... // 這裡又呼叫了設定 Scroll 的位置的方法 tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator); } } ... } |
再看看 setScrollPosition :
1 2 3 4 5 6 |
void setScrollPosition(int position, float positionOffset, boolean updateSelectedText, boolean updateIndicatorPosition) { ... scrollTo(calculateScrollXForTab(position, positionOffset), 0); ... } |
看到了吧,這裡也呼叫了 scrollTo。
那麼之前抖動的問題就很顯然了,點選 Tab 的時候會觸發 TabLayout 的scrollTo,而點選 Tab 會觸發ViewPager 滑動,ViewPager的滑動也特麼觸發了 scrollTo,這 ViewPager 滑動導致的 scrollTo 就是我們閃爍的原因!
分析完畢,如何解決呢?
解決
其實解決方案很簡單,我們只要使點選 Tab 的時候不觸發 ViewPager 滑動的那個 scrollTo 就行了。
How?
我們在自己滑動 ViewPager 的時候 scrollTo 還是要走的,那麼自己滑動和點選 Tab 觸發的 ViewPager 滑動有啥區別呢?當然有!pageScrollState 不同!自己滑動的時候是 SCROLL_STATE_DRAGGING,而點選 Tab 時是 SCROLL_STATE_IDLE。
那麼顯而易見了,透過 pageScrollState 來區分下就行了。
我們需要對剛剛分析的 TabLayoutOnPageChangeListener 類的實現做點改變:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public static class FixedTabLayoutOnPageChangeListener extends TabLayout.TabLayoutOnPageChangeListener { private boolean isTouchState; public FixedTabLayoutOnPageChangeListener(TabLayout tabLayout) { super(tabLayout); } @Override public void onPageScrollStateChanged(int state) { super.onPageScrollStateChanged(state); if (state == SCROLL_STATE_DRAGGING) { isTouchState = true; } else if (state == SCROLL_STATE_IDLE) { isTouchState = false; } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (isTouchState) { super.onPageScrolled(position, positionOffset, positionOffsetPixels); } } } |
只有 pageScrollState 是 SCROLL_STATE_DRAGGING 的時候才觸發 TabLayoutOnPageChangeListener 的 onPageScrolled。
但是 TabLayoutOnPageChangeListener 是 TabLayout 的 mPageChangeListener 變數,我們需要替換它,那隻能反射了。
1 2 3 4 5 6 7 8 9 |
try { Field field = TabLayout.class.getDeclaredField("mPageChangeListener"); field.setAccessible(true); field.set(this, new FixedTabLayoutOnPageChangeListener(this)); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } |
這樣一來,就完成了,看看效果:
尾語
即使是官方的東西但難免也會有點小問題,重視細節,再解決它,這個過程還是不錯的。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4798/viewspace-2812522/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 直播系統app原始碼,TabLayout:自定義字型大小APP原始碼TabLayout自定義字型
- MaterialDesign系列文章(八)TabLayout的使用TabLayout
- 快來升級了!蘋果釋出最新版系統,修復安全漏洞蘋果
- 使用ViewPager和TabLayout來實現滑動切換效果ViewpagerTabLayout
- iOS APP執行時Crash自動修復系統iOSAPP
- win10系統修復工具怎樣使用_win10系統修復工具使用步驟Win10
- 使用 requests 建立 Cookies 時遇到“系統不知道 filename 哪來的”錯誤,如何修復?Cookie
- [BUG反饋]編輯【系統-系統管理-選單管理】有個BUG
- 微軟修復Bug的補丁產生了新的Bug微軟
- Win10系統三大功能或累積更新:修復大量BUGWin10
- Linux 系統的單使用者模式、修復模式、跨控制檯登入在系統修復中的運用Linux模式
- 日常Bug排查-系統失去響應-Redis使用不當Redis
- TabLayoutTabLayout
- 常用快取系統使用經驗總結快取
- 系統效能提升利刃 | 快取技術使用快取
- strtotime () 帶來的 bug
- CentOS 系統修復CentOS
- 一個系統BUG引發的血案 -- FKDownloader
- Twitter PWA Win10系統版更新:修復Bug,增加剪貼簿發圖功能Win10
- 使用Java和Redis構建高效能的快取系統JavaRedis快取
- 模仿Google News的TabLayoutGoTabLayout
- 電腦系統崩潰怎麼修復 修復電腦系統的三種方法
- 移動CRM系統使用起來如何
- 使用Systemctl命令來管理系統服務
- 如何修復ubuntu的系統引導Ubuntu
- 微軟開始推送Win10 20H1預覽版19008系統:帶來一系列錯誤Bug修復微軟Win10
- 限免| 網易雲創沙龍杭州站來襲,傳統IT系統、雲實踐……快來!
- 系統效能提升利刃 | 快取技術使用的實踐與思考快取
- flexible.js 相容bug修復FlexJS
- KDE Plasma 5的第二個bug修復版本釋出,帶來了很多的改變ASM
- CSS之樣式無效BUG的修復CSS
- 被冰封的 Bug:Fishhook Crash 修復紀實Hook
- 同事改Bug飛快,原來掌握了這些程式碼Debug技巧
- Win10 1909系統自帶的清理快取怎樣使用 win10 1909清理快取的使用方法Win10快取
- Denial of App - Google Bug 13416059 分析APPGo
- 漏洞修復如何輕、快、好、省?深信服EDR來支招
- Windows10系統三大正式版收穫累計補丁更新:修復大量bugWindows
- 如何快速清除 Ubuntu 的系統快取Ubuntu快取