Android View篇之調整字型大小滑桿的實現
小夥伴們大家好呀,這次介紹一個稍微有點意思的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()
正好就是這個一半值。

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
~~
原始碼主要類: