Android View篇之調整字型大小滑桿的實現

RAE發表於2019-05-11

小夥伴們大家好呀,這次介紹一個稍微有點意思的View,在很多閱讀類、新聞類的APP上都標配的字型大小調整功能。100多行程式碼就可以實現,來看看效果吧!

效果圖

思路分析

1、刻度線代表著每個字型的大小取值,是不是SeekBar就是這樣的功能呀,改一下刻度浮標的樣式即可。

2、刻度條上面的標註的文字代表著字型大小的說明,假設用一個LinearLayout包著3個TextView的方法很難實現文字跟刻度對齊,只能畫出來了。

開始寫程式碼

我們以SeekBar作為基礎,對它進行重寫onDraw()的方法,在這之前先來初始化一下基本屬性。


/**
 * 字型大小調整滑桿
 * Created by ChenRui on 2017/10/13 0013 12:50.
 */
public class RaeSeekBar extends AppCompatSeekBar {

    //  刻度說明文字,陣列數量跟刻度數量一致,跟mTextSize的長度要一致
    private String[] mTickMarkTitles = new String[]{
            "A",
            "標準",
            "",
            "",
            "A"
    };
    // 刻度代表的字型大小
    private int[] mTextSize = new int[]{
            16,
            18,
            24,
            26,
            28
    };

    // 刻度文字畫筆
    private final Paint mTickMarkTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
    //  刻度文字字型大小
    private float mTickMarkTitleTextSize = 18;
    // 刻度文字跟刻度之間的間隔
    private float mOffsetY = 40;
    // 刻度線的高度
    private int mLineHeight = 10;
    // 儲存位置大小資訊
    private final Rect mRect = new Rect();

    // ...省略一些其他建構函式
    public RaeSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    protected void init() {
        //  初始化刻度文字字型大小
        mTickMarkTitleTextSize = getSize(mTickMarkTitleTextSize);
        // 刻度文字跟刻度之間的間隔
        mOffsetY = getSize(mOffsetY);
        // 刻度線的高度
        mLineHeight = getSize(mLineHeight);
        // 刻度文字的對齊方式為居中對齊
        mTickMarkTitlePaint.setTextAlign(Paint.Align.CENTER);
        // 刻度文字的字型顏色
        mTickMarkTitlePaint.setColor(ContextCompat.getColor(getContext(), R.color.ph1));
        // 設定最大刻度值為字型大小陣列的長度
        setMax(mTextSize.length);
        // 設定當前的刻度
        setProgress(1);
    }
}
複製程式碼

測量佈局

因為要在原來的SeekBar的基礎 上新增文字,那就應該在原來的SeekBar的 高度上再增加最大刻度的文字的高度就是控制元件佈局的高度。

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    // 原來的佈局大小
    int wm = MeasureSpec.getMode(widthMeasureSpec);
    int hm = MeasureSpec.getMode(heightMeasureSpec);
    int w = getMeasuredWidth();
    int h = getMeasuredHeight();
    
    // 以最大的字型為基礎,加上刻度字型大小
    h += getSize(mTextSize[mTextSize.length - 1]);
    // 加上與刻度之間的間距大小
    h += mOffsetY;
    // 儲存測量結果
    setMeasuredDimension(MeasureSpec.makeMeasureSpec(w, wm), MeasureSpec.makeMeasureSpec(h, hm));
}
複製程式碼

重繪

總結一下畫的思路,這樣可以比較好理解程式碼的實現。整個過程一共需要我們畫3部分:

  • 直線
  • 刻度線
  • 刻度文字

當然還有個滑動塊,這個我們可以使用SeekBar自帶的效果,即可以自定義樣式,又能偷下懶。下面來一個個解析畫的具體步驟。

1、畫直線

我們先理解成外部是一個矩形,直線位於中間,左右兩邊的間距為滑塊的一半。通過研究發現getPaddingLeft() getPaddingRight() 正好就是這個一半值。

Android View篇之調整字型大小滑桿的實現

2、刻度線

很容易看得出來,刻度線實際是把直線進行等分,等分的多少取決於setMax()的取值,也相當於mTextSize.length,這樣我們通過畫直線就能輕鬆實現了。

3、刻度文字

最重要的是確定文字所在的(x,y) 座標值即可,不難發現文字的座標是跟隨刻度線的位置變化的,所以在畫刻度線的時候就可以一起把文字也畫出來了。

4、滑塊位置

系統滑塊的位置其實是跟分割線的位置一樣的。等分直線,處於分割線中心。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 刻度長度
    int maxLength = getMax();
    int width = getWidth();
    int height = getHeight();
    int h2 = height / 2; // 居中

    // 畫刻度背景
    mRect.left = getPaddingLeft();
    mRect.right = width - getPaddingRight();
    mRect.top = h2 - getSize(1); // 居中
    mRect.bottom = mRect.top + getSize(1.5f); // 1.5f為直線的高度
    // 直線的長度
    int lineWidth = mRect.width();
    // 畫直線
    canvas.drawRect(mRect, mTickMarkTitlePaint);

    //  遍歷刻度,畫分割線和刻度文字
    for (int i = 0; i <= maxLength; i++) {

        // 刻度的起始間隔 = 左間距 + (線條的寬度 * 當前刻度位置 / 刻度長度)
        int thumbPos = getPaddingLeft() + (lineWidth * i / maxLength);
        // 畫分割線
        mRect.top = h2 - mLineHeight / 2;
        mRect.bottom = h2 + mLineHeight / 2;
        mRect.left = thumbPos;
        mRect.right = thumbPos + getSize(1.5f); // 直線的寬度為1.5
        canvas.drawRect(mRect, mTickMarkTitlePaint);

        // 畫刻度文字
        String title = mTickMarkTitles[i % mTickMarkTitles.length]; // 拿到刻度文字
        mTickMarkTitlePaint.getTextBounds(title, 0, title.length(), mRect); // 計算刻度文字的大小以及位置
        mTickMarkTitlePaint.setTextSize(getSize(mTextSize[i])); // 設定刻度文字大小
        // 畫文字
        canvas.drawText(title, thumbPos, getSize(mTextSize[mTextSize.length - 1]), mTickMarkTitlePaint);
    }
}
複製程式碼

畫好了是不是等不及了來實際應用一下呢?

具體應用示例

  • 佈局檔案
<!--thumb屬性為滑塊的圖片-->
<com.rae.cnblogs.widget.RaeSeekBar
    android:id="@+id/seekBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:progressDrawable="@android:color/transparent"
    android:theme="@style/Widget.AppCompat.SeekBar"
    android:thumb="@drawable/seekbar_thumb_material_anim_font_setting" />
複製程式碼
  • thumb滑塊圖片
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
    <item>
        <shape android:shape="oval">
            <solid android:color="@color/badge_color" />
            <size android:width="24dp" android:height="24dp" />
        </shape>
    </item>
</selector>
複製程式碼
  • 滑塊回撥監聽
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int value, boolean b) {
            //  獲取滑塊所在位置對應的字型大小
            int size = mSeekBar.getRawTextSize(value);
            mMessage.setTextSize(size);
        }
        //... 省略其他方法
    });
複製程式碼

原始碼

好啦,本篇文章到這裡結束了。忍不住想試試的,可以參考原始碼來做,在部落格園Android開源客戶端專案中,喜歡的給個Star~~

原始碼主要類:

相關文章