前兩個章節(jié)只是簡單的說了下自定義屬性及使用, 這章會完整的繪制出一個我們自己定義的TextView出來.
1. 布局文件設(shè)置
<com.view_day02.MyTextView
android:padding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:myInputType="text"
app:myTextSize="20sp"
app:myText="zhangsan"
android:background="#00FF00"
app:myTextColor="#000000"/>
2. 創(chuàng)建畫筆
private String mText;
private int mTextSize = 15;
private int mTextColor = Color.BLACK;
// 畫筆
private Paint mPaint;
public MyTextView(Context context) {
this(context, null);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 獲取自定義屬性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTextView);
mText = typedArray.getString(R.styleable.MyTextView_myText);
mTextColor = typedArray.getColor(R.styleable.MyTextView_myTextColor, mTextColor);
mTextSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_myTextSize, sp2px(mTextSize));
// 回收
typedArray.recycle();
initPaint();
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
//初始化畫筆
private void initPaint() {
mPaint = new Paint();
// 抗鋸齒
mPaint.setAntiAlias(true);
// 設(shè)置畫筆繪制文字的大小
mPaint.setTextSize(mTextSize);
// 設(shè)置畫筆顏色
mPaint.setColor(mTextColor);
}
3. 開始測量onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 獲取寬高的模式,是dp,還是 wrap_content/match_parent
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//有以下三種模式
//MeasureSpec.AT_MOST 在布局中指定了 wrap_content
//MeasureSpec.EXACTLY 在布局中指定了確切的值, 或是match_parent/fill_parent
//MeasureSpec.UNSPECIFIED 盡可能的大 (很少用到)
// 1. 如果寬高是確定的值,這個時候不需要計算.給多少就是多少.
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 2. 如果是wrap_content. 需要計算
if (widthMode == MeasureSpec.AT_MOST) {
// 計算寬度. 寬度與字體的長度,大小有關(guān). 需要用畫筆來測量
Rect bounds = new Rect();
// 獲取文本的 Rect, 把結(jié)果返回至最后一個參數(shù) bounds
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
//因為設(shè)置了padiing,所以需要加上 padding 的 left 和 right
widthSize = bounds.width() + getPaddingLeft() + getPaddingRight();
}
if (heightMode == MeasureSpec.AT_MOST) {
Rect bounds = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), bounds);
//因為設(shè)置了padiing,所以需要加上padding的top和bottom
heightSize = bounds.height() + getPaddingTop() + getPaddingBottom();
}
// 設(shè)置自定義TextView的寬高
setMeasuredDimension(widthSize, heightSize);
}
4. 測量完成后,開始用畫筆畫
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
//dy 代表在是 高度的一半到BaseLine 的距離
//fontMetrics.top 是baseLine到頂部的距離(負值)
//fontMetrics.bottom baseLine到底部的距離(正值)
float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
float baseLine = getHeight() / 2 + dy;
int x = getPaddingLeft(); //重新加上paddingLeft
// drawText 參數(shù)說明
// text,
// x(開始的位置),
// y(基線-baseLine) 需要求BaseLine,網(wǎng)上有介紹基線的介紹,這里就不再說明
// ,paint
canvas.drawText(mText, x, baseLine, mPaint);
}
求 TextView 基線算法,了解基線后直接套公式即可
float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
float baseLine = getHeight() / 2 + dy;