在Android开发过程中,系统自带的各种控件不可能完全满足我们开发的需求,这样就需要我们进行自定义各种控件。今天给大家介绍一下自定义View的过程。
- 定义自定义View的属性
- 获取自定义的属性
- 进行测量,重写onMeasure方法,这一步不是必须的
- 有时候需要重写onLayout方法
重写onDraw方法
以上就是大致的步骤,下面我们一步步实现自定义属性。
1、在values文件夹下面创建attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="contentText" format="string" />
<attr name="contentTextColor" format="color" />
<attr name="contentTextSize" format="dimension" />
<declare-styleable name="CustomView">
<attr name="contentTextColor" />
<attr name="contentTextSize" />
<attr name="contentText" />
</declare-styleable>
</resources>
这里要注意的是,不能和android命名空间中的属性名相同。 例如:不能用text,textSize等。将属性声明在外面的好处是可以方便多个自定义的命名空间使用该属性。format中的属性有很多种,有兴趣的朋友可以去网上找。
自定义View
自定义View并获取自定义属性:
public class CustomView extends View {
private String contentText;
private int contentTextColor;
private int contentTextSize;
//声明一个画笔对象
Paint paint = null;
//控制绘制文本时的范围
private Rect bound;
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
init();
}
/**
* 获取自定义的属性
* @param context
* @param attrs
*/
private void initAttrs(Context context, AttributeSet attrs) {
//加载资源文件中定义的属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
contentText = typedArray.getString(R.styleable.CustomView_contentText);
//获取字体的颜色,默认值是蓝色
contentTextColor = typedArray.getColor(R.styleable.CustomView_contentTextColor, Color.BLUE);
//获取尺寸,默认值是20sp
contentTextSize = typedArray.getDimensionPixelSize(R.styleable.CustomView_contentTextSize, dpToPx(20));
typedArray .recycle();
}
/**
* 初始化
*/
private void init() {
//画笔
paint = new Paint();
paint.setTextSize(contentTextSize);
//初始化矩形
bound = new Rect();
//设置text所需要绘制的范围
paint.getTextBounds(contentText, 0, contentText.length(), bound);
}
/**
* 将dp转换为px,1:1转
*
* @param size 需要sp的大小
* @return
*/
private int dpToPx(int size) {
size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size, getResources().getDisplayMetrics());
return size;
}
}
这样对所定义的属性和自定义的属性就初始化完成了,注意的是获取自定义属性时,在资源文件中的”format”是什么值,获取的时候就应该用对应的值获取。即命名空间+属性的名称,中间用’_’连接。例如declare-styleable的name=’CustomView’;attr的name=’contentText’,根据自定义属性获取是的命名规则,那么我们在获取属性时的名称是:R.styleable.CustomView_contentText。
接下来就是进行测量了:首先要了解下onMeasure中的MeasureSpec中的三种类型:
- UNSPECIFIED:控件可一有任意的大小,一般我们不会使用这个属性
- EXACTLY:如果我们不进行测量的话,onMeasure方法会自动默认使用该属性,一般指固定的大小,或者是match_parent
- AT_MOST:表示子布局限制在一个固定的大小,一般是wrap_content
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取宽度的类型
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
//获取宽度的值
int widthSize = MeasureSpec.getSize(heightMeasureSpec);
//获取高度的类型
int heightMode = MeasureSpec.getMode(widthMeasureSpec);
//获取高度的值
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0, height = 0;
//判断宽度是什么类型,如果宽度确定,直接赋值给width,否则进行测量
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//获取字体的大小
paint.setTextSize(contentTextSize);
//绘制的范围
paint.getTextBounds(contentText, 0, contentText.length(), bound);
float textWidth = bound.width();
//最后确定的宽度,左内边距加字体的长度加右内边距
int exactWidth = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = exactWidth;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
paint.setTextSize(contentTextSize);
paint.getTextBounds(contentText, 0, contentText.length(), bound);
float textHeight = bound.height();
int exactHeight = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = exactHeight;
}
setMeasuredDimension(width, height);
}
最后将其画出来即可
@Override
protected void onDraw(Canvas canvas) {
//绘制背景
paint.setColor(Color.CYAN);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
paint.setColor(contentTextColor);
canvas.drawText(contentText, getWidth() / 2 - bound.width() / 2, getHeight() / 2 + bound.height() / 2, paint);
}