Android實(shí)現(xiàn)網(wǎng)易嚴(yán)選標(biāo)簽欄滑動效果
標(biāo)簽欄是一個(gè)非常常見的控件,似乎也是一個(gè)比較簡單的控件,但如果在標(biāo)簽下方加個(gè)下劃線的話,就還是可以玩出挺多花來的。

網(wǎng)易嚴(yán)選的標(biāo)簽欄就做的很不錯(cuò),里面隱藏著諸多細(xì)節(jié):
- 手動滑動頁面,下劃線會跟著滑動。
- 選擇一個(gè)標(biāo)簽后,下劃線會有滑動過去的動畫。
- 選擇最左端或最右端的標(biāo)簽,標(biāo)簽欄會進(jìn)行滑動,使得標(biāo)簽向中間靠攏(如果可以滑的話)。
仔細(xì)分析下,需要在簡單標(biāo)簽欄的基礎(chǔ)上實(shí)現(xiàn)以下邏輯:
- 畫出下劃線。
- 監(jiān)聽手動滑動頁面事件,實(shí)時(shí)更新下劃線位置。
- 切換標(biāo)簽時(shí),開始下劃線滑動的動畫,并判斷是否要同時(shí)滑動標(biāo)簽欄。

我做了一個(gè)樣例程序,其中的較難點(diǎn)在于計(jì)算下劃線的位置,和下劃線的動畫效果。
// 根據(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正處于兩個(gè)tab之間,需進(jìn)行加權(quán)計(jì)算得到它的位置
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);
}
上面是計(jì)算下劃線位置的代碼,通過傳入在onPageScrolled()中獲得的position和positionOffset,計(jì)算下劃線是在某一個(gè)標(biāo)簽下,或者某兩個(gè)標(biāo)簽之間的位置。需要注意的是,由于各標(biāo)簽的長度可能不一,所以下劃線的長度在滑動中也可能發(fā)生變化,所以需分別計(jì)算下劃線的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超出屏幕范圍時(shí),讓其從屏幕邊界處開始移動
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)簽時(shí)下劃線運(yùn)行滑動動畫的代碼,使用ValueAnimator實(shí)現(xiàn),并且對下劃線超出邊界的情況做了特殊處理,以防止滑動距離過大時(shí),滑動速度過快。
更多的細(xì)節(jié),請見https://github.com/wlkdb/page_sliding
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android DrawableTextView圖片文字居中顯示實(shí)例
在我們開發(fā)中,TextView設(shè)置Android:drawableLeft一定使用的非常多,但Drawable和Text同時(shí)居中顯示可能不好控制,小編想到通過自定義TextView實(shí)現(xiàn),具體詳情大家參考下本文2017-03-03
詳解android webView獨(dú)立進(jìn)程通訊方式
本篇文章主要介紹了android webView獨(dú)立進(jìn)程通訊方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
基于Manifest.xml中不要出現(xiàn)重復(fù)的uses permission的說明
本篇文章對Manifest.xml中不要出現(xiàn)重復(fù)的uses permission進(jìn)行了介紹。需要的朋友參考下2013-05-05
Android中實(shí)現(xiàn)ping功能的多種方法詳解
這篇文章主要介紹了Android中實(shí)現(xiàn)ping功能的多種方法詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
Android FlowLayout流式布局實(shí)現(xiàn)詳解
這篇文章主要為大家詳細(xì)介紹了Android FlowLayout流式布局的實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09

