Android实现网易严选标签栏滑动效果

所属分类: 软件编程 / Android 阅读数: 62
收藏 0 赞 0 分享

标签栏是一个非常常见的控件,似乎也是一个比较简单的控件,但如果在标签下方加个下划线的话,就还是可以玩出挺多花来的。

网易严选的标签栏就做的很不错,里面隐藏着诸多细节:

  • 手动滑动页面,下划线会跟着滑动。
  • 选择一个标签后,下划线会有滑动过去的动画。
  • 选择最左端或最右端的标签,标签栏会进行滑动,使得标签向中间靠拢(如果可以滑的话)。

仔细分析下,需要在简单标签栏的基础上实现以下逻辑:

  • 画出下划线。
  • 监听手动滑动页面事件,实时更新下划线位置。
  • 切换标签时,开始下划线滑动的动画,并判断是否要同时滑动标签栏。

我做了一个样例程序,其中的较难点在于计算下划线的位置,和下划线的动画效果。

// 根据当前选定的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,计算下划线是在某一个标签下,或者某两个标签之间的位置。需要注意的是,由于各标签的长度可能不一,所以下划线的长度在滑动中也可能发生变化,所以需分别计算下划线的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实现,并且对下划线超出边界的情况做了特殊处理,以防止滑动距离过大时,滑动速度过快。

更多的细节,请见https://github.com/wlkdb/page_sliding

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Android样式和主题之选择器的实例讲解

今天小编就为大家分享一篇关于Android样式和主题之选择器的实例讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Android应用动态修改主题的方法示例

今天小编就为大家分享一篇关于Android应用动态修改主题的方法示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Flutter 网络请求框架封装详解

这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Flutter倒计时/计时器的实现代码

这篇文章主要介绍了Flutter倒计时/计时器的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

Android使用google breakpad捕获分析native cash

这篇文章主要介绍了Android使用google breakpad捕获分析native cash 的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Flutter WebView与JS互相调用简易指南

这篇文章主要介绍了详解Flutter WebView与JS互相调用简易指南,分为JS调用Flutter和Flutter调用JS,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android开发实现ListView和adapter配合显示图片和文字列表功能示例

这篇文章主要介绍了Android开发实现ListView和adapter配合显示图片和文字列表功能,涉及Android使用ListView结合adapter适配器实现图文显示功能相关的布局、解析、权限控制等操作技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

Android文字基线Baseline算法的使用讲解

今天小编就为大家分享一篇关于Android文字基线Baseline算法的使用讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Flutter自定义实现神奇动效的卡片切换视图的示例代码

这篇文章主要介绍了Flutter自定义实现神奇动效的卡片切换视图的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android实现自定义滑动刻度尺方法示例

这篇文章主要给大家介绍了关于Android实现自定义滑动刻度尺的相关资料,文中通过示例代码介绍的非常详细,对各位Android开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
收藏 0 赞 0 分享
查看更多