1.概述
一开始也很害怕自定义 View,机缘之下看了辉哥-红橙Darren的视频,跟着视频开始学习自定义 View,之后一系列的文章也是记录自定义 View 的学习过程。
2.自定义 View 的基本流程
- 创建 View Class
- 创建 attr 属性文件,确定属性
- View Class 绑定 attr 属性
- onMeasure 测量
- onDraw 绘制
2.1 创建 View Class ( 以 CustomTextView 为例子 )
public class CustomTextView extends View {
//在初始化时调用
public CustomTextView(Context context) {
this(context, null);
}
//在 Layout 中调用
public CustomTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
//在 Layout 中设置 style 调用
public CustomTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
2.2 创建 Attr 属性文件
res -> values -> New Values Resource File
attr.xml
2.3 View Class 绑定属性
定义属性
private void initAttr(Context context, AttributeSet attrs) {
//第一步获取 TypedArray
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
//第二步获取属性
mText = array.getString(R.styleable.CustomTextView_cusText);
mTextColor = array.getColor(R.styleable.CustomTextView_cusTextColor, mTextColor);
//默认是 px,将 sp 转换成 px
mTextSize = array.getDimensionPixelSize(R.styleable.CustomTextView_cusTextSize, sp2px(mTextSize));
//回收
array.recycle();
}
private int sp2px(int sp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}
2.4 onMeasure 测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//第一步获取测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//第二步获取数值
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//第三步判断模式,如果是 wrap_content 就需要计算宽高
//通过画笔 Paint 测量文本的宽高
Rect rect = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), rect);
if (widthMode == MeasureSpec.AT_MOST) {
widthSize = rect.width() + getPaddingLeft() + getPaddingRight();
}
if (heightMode == MeasureSpec.AT_MOST) {
heightSize = rect.height() + getPaddingTop() + getPaddingBottom();
}
//第四部设置宽高
setMeasuredDimension(widthSize, heightSize);
}
2.5 onDraw 绘制
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getPaddingLeft();
//需要计算基线 baseline 位置
//获取
Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
//两种计算方式
//(fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom
//(fontMetricsInt.top - fontMetricsInt.bottom) / 2 - fontMetricsInt.top
int y = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
int baseline = getHeight() / 2 + y;
canvas.drawText(mText, x, baseline, mPaint);
}
给大家大致讲一下基线计算方式
发现在原有的图上操作太麻烦,画了一张草图
最简单的自定义 View 流程大概就是这么多,总结一下需要常用的方法。
- onMeasure
- MeasureSpec.getMode
- MeasureSpec.getSize
- setMeasuredDimension
- onDraw
- Paint.FontMetricsInt 计算 BaseLine 基线
- drawText|drawCircle|drawArc ......
GitHub 源码链接