Android實現(xiàn)網(wǎng)易嚴(yán)選標(biāo)簽欄滑動效果
標(biāo)簽欄是一個非常常見的控件,似乎也是一個比較簡單的控件,但如果在標(biāo)簽下方加個下劃線的話,就還是可以玩出挺多花來的。
網(wǎng)易嚴(yán)選的標(biāo)簽欄就做的很不錯,里面隱藏著諸多細(xì)節(jié):
- 手動滑動頁面,下劃線會跟著滑動。
- 選擇一個標(biāo)簽后,下劃線會有滑動過去的動畫。
- 選擇最左端或最右端的標(biāo)簽,標(biāo)簽欄會進(jìn)行滑動,使得標(biāo)簽向中間靠攏(如果可以滑的話)。
仔細(xì)分析下,需要在簡單標(biāo)簽欄的基礎(chǔ)上實現(xiàn)以下邏輯:
- 畫出下劃線。
- 監(jiān)聽手動滑動頁面事件,實時更新下劃線位置。
- 切換標(biāo)簽時,開始下劃線滑動的動畫,并判斷是否要同時滑動標(biāo)簽欄。
我做了一個樣例程序,其中的較難點在于計算下劃線的位置,和下劃線的動畫效果。
// 根據(jù)當(dāng)前選定的tab,得到indicator應(yīng)該移動到的位置 private Pair<Float, Float> getIndicatorTargetLeftRight(int position, float positionOffset) { View tab = tabsContainer.getChildAt(position); Pair<Float, Float> indicator = getIndicatorLeftRight(tab); float targetLeft = indicator.first; float targetRight = indicator.second; // 如果positionOffset不為0,indicator正處于兩個tab之間,需進(jìn)行加權(quán)計算得到它的位置 if (positionOffset > 0f && position < tabCount - 1) { View nextTab = tabsContainer.getChildAt(position + 1); Pair<Float, Float> indicatorForNextTab = getIndicatorLeftRight(nextTab); float left = indicatorForNextTab.first; float right = indicatorForNextTab.second; targetLeft = (positionOffset * left + (1f - positionOffset) * targetLeft); targetRight = (positionOffset * right + (1f - positionOffset) * targetRight); } return new Pair<>(targetLeft, targetRight); } private Pair<Float, Float> getIndicatorLeftRight(View tab) { float left = tab.getLeft(); float right = tab.getRight(); if (indicatorMode == IndicatorMode.WRAP && tab instanceof TextView) { TextView tabTextView = (TextView) tab; paint.setTextSize(tabTextView.getTextSize()); float textLength = paint.measureText(tabTextView.getText().toString()); float middle = (left + right) / 2f; left = middle - textLength / 2f; right = middle + textLength / 2f; } return new Pair<>(left, right); }
上面是計算下劃線位置的代碼,通過傳入在onPageScrolled()中獲得的position和positionOffset,計算下劃線是在某一個標(biāo)簽下,或者某兩個標(biāo)簽之間的位置。需要注意的是,由于各標(biāo)簽的長度可能不一,所以下劃線的長度在滑動中也可能發(fā)生變化,所以需分別計算下劃線的left和right。
private boolean isAnimateRunning = false; private static final String TARGET_LEFT = "targetLeft"; private static final String TARGET_RIGHT = "targetRight"; private void startIndicatorAnimate(final float targetLeft, final float targetRight) { // 在indicator超出屏幕范圍時,讓其從屏幕邊界處開始移動 float move = 0; if (indicatorCurrentRight < getScrollX()) { move = getScrollX() - indicatorCurrentRight; } else if (indicatorCurrentLeft > getScrollX() + DimenUtil.getScreenWidth(getContext())) { move = getScrollX() + DimenUtil.getScreenWidth(getContext()) - indicatorCurrentLeft; } indicatorCurrentLeft += move; indicatorCurrentRight += move; PropertyValuesHolder valuesHolderLeft = PropertyValuesHolder.ofFloat( TARGET_LEFT, indicatorCurrentLeft, targetLeft); PropertyValuesHolder valuesHolderRight = PropertyValuesHolder.ofFloat( TARGET_RIGHT, indicatorCurrentRight, targetRight); ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(valuesHolderLeft, valuesHolderRight) .setDuration(SCROLL_DURATION); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { if (indicatorCurrentLeft != targetLeft) { indicatorCurrentLeft = (float) animation.getAnimatedValue(TARGET_LEFT); } if (indicatorCurrentRight != targetRight) { indicatorCurrentRight = (float) animation.getAnimatedValue(TARGET_RIGHT); } if (indicatorCurrentLeft == targetLeft && indicatorCurrentRight == targetRight) { isAnimateRunning = false; } invalidate(); } }); animator.start(); isAnimateRunning = true; }
這是切換標(biāo)簽時下劃線運(yùn)行滑動動畫的代碼,使用ValueAnimator實現(xiàn),并且對下劃線超出邊界的情況做了特殊處理,以防止滑動距離過大時,滑動速度過快。
更多的細(xì)節(jié),請見https://github.com/wlkdb/page_sliding
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android分頁中顯示出下面翻頁的導(dǎo)航欄的布局實例代碼
這篇文章主要介紹了Android分頁中顯示出下面翻頁的導(dǎo)航欄的布局實例代碼,需要的朋友可以參考下2017-04-04Android SurfaceView拍照錄像實現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Android SurfaceView拍照錄像實現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02Android開發(fā)自學(xué)筆記(五):使用代碼控制界面
這篇文章主要介紹了Android開發(fā)自學(xué)筆記(五):使用代碼控制界面,本文講解了添加第二個layout、添加MyActivity的code、setup函數(shù)、getResult函數(shù)等內(nèi)容,需要的朋友可以參考下2015-04-04android View 繪制完成監(jiān)聽的實現(xiàn)方法
今天小編就為大家分享一篇android View 繪制完成監(jiān)聽的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09Android 中使用RadioGroup和Fragment實現(xiàn)底部導(dǎo)航欄的功能
這篇文章主要介紹了Android 中使用RadioGroup+Fragment實現(xiàn)底部導(dǎo)航欄的功能,整體文章大概分為兩部分介紹,通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-06-06Android中替換WebView加載網(wǎng)頁失敗時的頁面
這篇文章主要介紹了Android中替換WebView加載網(wǎng)頁失敗時的頁面,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-01-01Android仿貼吧內(nèi)容下的簡單ListView嵌套GridView
這篇文章主要為大家詳細(xì)介紹了Android仿貼吧內(nèi)容下的簡單ListView嵌套GridView,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03