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

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

我做了一個樣例程序,其中的較難點在于計算下劃線的位置,和下劃線的動畫效果。
// 根據當前選定的tab,得到indicator應該移動到的位置
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之間,需進行加權計算得到它的位置
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,計算下劃線是在某一個標簽下,或者某兩個標簽之間的位置。需要注意的是,由于各標簽的長度可能不一,所以下劃線的長度在滑動中也可能發(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;
}
這是切換標簽時下劃線運行滑動動畫的代碼,使用ValueAnimator實現(xiàn),并且對下劃線超出邊界的情況做了特殊處理,以防止滑動距離過大時,滑動速度過快。
更多的細節(jié),請見https://github.com/wlkdb/page_sliding
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android DrawableTextView圖片文字居中顯示實例
在我們開發(fā)中,TextView設置Android:drawableLeft一定使用的非常多,但Drawable和Text同時居中顯示可能不好控制,小編想到通過自定義TextView實現(xiàn),具體詳情大家參考下本文2017-03-03
基于Manifest.xml中不要出現(xiàn)重復的uses permission的說明
本篇文章對Manifest.xml中不要出現(xiàn)重復的uses permission進行了介紹。需要的朋友參考下2013-05-05
Android FlowLayout流式布局實現(xiàn)詳解
這篇文章主要為大家詳細介紹了Android FlowLayout流式布局的實現(xiàn)方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-09-09

