小夥伴們大家好呀,這次介紹一個稍微有點意思的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
~~
原始碼主要類: