关于第三方库SmartTabLayout的一点小修改

语言: CN / TW / HK

theme: channing-cyan

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

介绍

项目中目前使用了SmartTabLayout来作为ViewPager的选项卡

SmartTabLayout1.gif 大家可以看到下面的指示器会跟随着滑动而变的很扁,一开始我感觉还挺好后面越看越奇怪,直到有一天新的需求出来了,所有选项卡下方指示器都不允许变扁,目前为止这个库都没有提供这个取消变扁的方法,我们只能深入源码了

分析

首先我们项目时自定义的一个选项卡,而且这个库也暴露了一个方法可以让我们获取指定下标的选项卡

mSmart.getTabAt(i) 上面的代码会返回一个View,我们在xml中设置的选项卡文字根布局就是TextView,所以这个获取的也是TextView

xml中配置 <com.ogaclejapan.smarttablayout.SmartTabLayout android:id="@+id/smart" android:layout_width="match_parent" android:layout_height="50dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:stl_customTabTextLayoutId="@layout/item_tablayout_option" app:stl_customTabTextViewId="@id/item_tablayout_text" app:stl_defaultTabTextHorizontalPadding="14dp" app:stl_defaultTabTextMinWidth="0dp" app:stl_indicatorColor="@color/teal_200" app:stl_indicatorCornerRadius="5dp" app:stl_indicatorGravity="bottom" app:stl_indicatorWidth="12dp" /> 现在就点进去看看吧

image.png

一目了然,一般来说这种情况tabStrip一定是选项卡了,或者与选项卡及其相关,点到他的类看看吧

image.png

代码我就不放太多了,翻译和使用的一些地方就已经表明他是选项卡了.接下来我们来找指示器绘制的地方吧

image.png

命名规范的好处就体现出来了,我直接搜draw或者Indicator不一会就能找到目的地,真不戳,接着看代码吧

switch (indicatorGravity) { case GRAVITY_TOP: center = indicatorThickness / 2f; top = center - (thickness / 2f); bottom = center + (thickness / 2f); break; case GRAVITY_CENTER: center = height / 2f; top = center - (thickness / 2f); bottom = center + (thickness / 2f); break; case GRAVITY_BOTTOM: default: center = height - (indicatorThickness / 2f); top = center - (thickness / 2f); bottom = center + (thickness / 2f); }

这一步就很简单,判断xml中属性来设置对应的参数,以达到控制指示器位置的目的.我们项目中一直都是bottom所以重点关注下GRAVITY_BOTTOM

bottom没有做任何操作,直接转给default.这里面参数有点多我们看看谁调用了drawIndicator

``` private void drawDecoration(Canvas canvas) { final int height = getHeight(); final int width = getWidth(); final int tabCount = getChildCount(); final SmartTabLayout.TabColorizer tabColorizer = getTabColorizer(); final boolean isLayoutRtl = Utils.isLayoutRtl(this);

if (indicatorInFront) { drawOverline(canvas, 0, width); drawUnderline(canvas, 0, width, height); }

// Thick colored underline below the current selection if (tabCount > 0) { View selectedTab = getChildAt(selectedPosition); int selectedStart = Utils.getStart(selectedTab, indicatorWithoutPadding); int selectedEnd = Utils.getEnd(selectedTab, indicatorWithoutPadding); int left; int right; if (isLayoutRtl) { left = selectedEnd; right = selectedStart; } else { left = selectedStart; right = selectedEnd; }

int color = tabColorizer.getIndicatorColor(selectedPosition);
float thickness = indicatorThickness;

if (selectionOffset > 0f && selectedPosition < (getChildCount() - 1)) {
  int nextColor = tabColorizer.getIndicatorColor(selectedPosition + 1);
  if (color != nextColor) {
    color = blendColors(nextColor, color, selectionOffset);
  }

  // Draw the selection partway between the tabs
  float startOffset = indicationInterpolator.getLeftEdge(selectionOffset);
  float endOffset = indicationInterpolator.getRightEdge(selectionOffset);
  float thicknessOffset = indicationInterpolator.getThickness(selectionOffset);

  View nextTab = getChildAt(selectedPosition + 1);
  int nextStart = Utils.getStart(nextTab, indicatorWithoutPadding);
  int nextEnd = Utils.getEnd(nextTab, indicatorWithoutPadding);
  if (isLayoutRtl) {
    left = (int) (endOffset * nextEnd + (1.0f - endOffset) * left);
    right = (int) (startOffset * nextStart + (1.0f - startOffset) * right);
  } else {
    left = (int) (startOffset * nextStart + (1.0f - startOffset) * left);
    right = (int) (endOffset * nextEnd + (1.0f - endOffset) * right);
  }
  thickness = thickness * thicknessOffset;
}
//绘制指示器
drawIndicator(canvas, left, right, height, thickness, color);

}

if (!indicatorInFront) { drawOverline(canvas, 0, width); drawUnderline(canvas, 0, getWidth(), height); }

// Vertical separators between the titles drawSeparator(canvas, height, tabCount);

} ```

好家伙,上面一堆计算,怕了怕了,还是回去吧,不过粗粗的看一眼还是能看出来个大概,就是滑动的距离,咔咔咔一顿处理,就得出了一定的数值.回到原来的方法吧.回来后把目光聚集到这个地方

if (indicatorWidth == AUTO_WIDTH) { indicatorRectF.set(left, top, right, bottom); } else { float padding = (Math.abs(left - right) - indicatorWidth) / 2f; indicatorRectF.set(left + padding, top, right - padding, bottom); } 把计算过的参数给传递进去了,点进去看看

image.png

原来如此哈

case GRAVITY_BOTTOM: default: center = height - (indicatorThickness / 2f); top = center - (thickness / 2f); bottom = center + (thickness / 2f);

那这个代码就一目了然了,根据滑动距离计算的值来计算它的高度和宽度.我们直接把动态的数据设置成静态的就差不多能达到我们的效果

case GRAVITY_BOTTOM: default: center = height - (indicatorThickness / 2f); top = center - (indicatorThickness / 2f); bottom = center + (indicatorThickness / 2f);

试一下效果

SmartTabLayout2.gif

嘿嘿,简简单单.

后面我写Demo的时候遇到一个贼好笑的事情,因为这个库是歪果仁写的,全是英文,api翻译后更是驴唇不对马嘴,我闲着没事试试api都代表着啥,没想到啊

app:stl_indicatorInterpolation="linear" 设置这个直接就解决上面我分析一大段的东西,我直接吐血而亡.看来我要努力学习英文了