欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-05-02 21:12:53
标签栏是一个非常常见的控件,似乎也是一个比较简单的控件,但如果在标签下方加个下划线的话,就还是可以玩出挺多花来的。 网易严选的标签栏就做的很不错,里面隐藏着诸多细节:...

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

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

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

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

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

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

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

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

// 根据当前选定的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实现,并且对下划线超出边界的情况做了特殊处理,以防止滑动距离过大时,滑动速度过快。

更多的细节,请见

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