關於第三方庫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" 設置這個直接就解決上面我分析一大段的東西,我直接吐血而亡.看來我要努力學習英文了