一、自定义View的基本步骤概括
- 自定义View的属性
- 在View子类的构造方法中获取自定义的属性
- 重写
onMesure()
(非必须) - 重写
onDraw()
二、自定义View的属性
- 在res/values/目录下创建xml文件用于定义View的属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension"/>
<attr name="textContent" format="string"/>
<declare-styleable name="MyTextView">
<attr name="textColor"/>
<attr name="textSize"/>
<attr name="textContent"/>
</declare-styleable>
</resources>
attr标签表示定义一个属性;format表示该属性的取值类型,有如下几种
string | color | demension | integer |
---|---|---|---|
enum | reference | float | boolean |
fraction(百分比) | flag | - | - |
三、在View子类的构造方法中获取自定义的属性
- 默认的布局文件调用的是两个参数的构造方法,在构造方法中还可以完成对Paint等的初始化工作。
//获取自定义的属性集
TypedArray typedArray = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.MyTextView, defStyleAttr, 0);
//遍历每一个属性,并和所声明的属性绑定
for (int i = 0; i < typedArray.getIndexCount(); i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.MyTextView_textColor:
//绑定颜色并设置默认值为红色
textColor = typedArray.getColor(attr, Color.RED);
break;
case R.styleable.MyTextView_textContent:
textContent = typedArray.getString(attr);
break;
case R.styleable.MyTextView_textSize:
//第二个参数的写法好像固定
textSize = typedArray.getDimensionPixelSize(attr
, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP
, 16, getResources().getDisplayMetrics()));
break;
}
}
//别忘了回收,虽然不知道是为什么
typedArray.recycle();
四、重写 onMesure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width;
int height;
//获取宽高的定义方式,有MATCH_PARENT、WRAP_CONTENT、或具体的数值
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//获取设置的宽高
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//根据宽高的具体定义方式来确定具体的自定义View的宽高
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = 100;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = 100;
}
//设置宽高,不能忘
setMeasuredDimension(width, height);
}
注意:
- 当设置了明确的宽度和高度值时,获取到的
widthSize
和heightSize
就是我们设置的结果,当设置WRAP_CONTENT
或MATCH_PARENT
时系统测量的结果都是就是MATCH_PARENT
。 - MeasureSpec的specMode,一共三种类型:
EXACTLY
:表示设置了明确的值或者是MATCH_PARENT
AT_MOST
:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED
:表示子布局想要多大就多大,很少使用 - onMeasure() 中的写法基本固定不变
五、重写onDraw()
@Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
//在这里完成绘图工作
//根具具体情况的不同而不同
}
六、总结
自定义View的步骤很简单,就4步,然而要想实现或复杂或美观的自定义View还需不断努力,与君共勉!