做Android项目也做了好久啦,自定义控件也用了不少,有用别人现成的,也有用自己写的,现在项目已经告一段落,今天我们就来聊一聊Android自定义View流程。
Android自定义,大致分为以下几步:
1、确定自定义View所要完成的功能;
2、确定所需要的属性;
3、在自定义View的构造方法中获取相应的属性;
4、重写
onMeasure()方法;
5、重写onLayout()方法;
6、重写onDraw()方法
其中的第4、5步不是必须的。
下面我们根据一个例子,来说明一下,这几步是怎么起作用的。
1、
确定自定义View所要完成的功能:我们来自定义一个MyTextView,效果基本上和普通的TextView一样。好了,第一步已经完成了;
2、
确定所需要的属性:需要文本、颜色、字号,目前就这些吧,然后我们开始自定义属性,属性类型定义有什么不明白的可以参看:
Android 自定义属性 attr format取值类型
这些属性我们应该放在
res/values/attr.xml中:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="my_customer">
<attr name="textSize" format="dimension"></attr>
<attr name="text" format="string"></attr>
<attr name="textColor" format="color"></attr>
</declare-styleable>
</resources>
3、
在构造方法中获取相应的属性:
private String mText;
private int mTextSize;
private int mTextColor;
private Paint mPaint;
private Rect mBound;
public MyCustomerView(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
public MyCustomerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
// TODO Auto-generated constructor stub
}
public MyCustomerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
TypedArray array = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.my_customer, defStyleAttr, 0);
int n = array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.my_customer_text:// 文本
mText = array.getString(attr);
break;
case R.styleable.my_customer_textColor:// 文本的颜色
mTextColor = array.getColor(attr, 0xff000000);
break;
case R.styleable.my_customer_textSize:// 文本字号
mTextSize = array.getDimensionPixelSize(attr, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_PX, 12,
getResources().getDisplayMetrics()));
break;
}
}
array.recycle();
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mBound = new Rect();
mPaint.getTextBounds(mText, 0, mText.length(), mBound);
}
4、
重写
onMeasure()方法,这一步的目的是为了设置自定义View的宽高,如果自定义view属于ViewGroup,还会设置及测量子View的宽高:
<span style="white-space:pre"> </span>@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {//确定值,定值\match_parent
width = widthSize;
} else {//wrap_content
width = getPaddingLeft() + mBound.width() + getPaddingRight();
}
if (heightMode == MeasureSpec.EXACTLY) {//确定值,定值\match_parent
height = heightSize;
} else {//wrap_content
height = getPaddingTop() + mBound.height() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
简单介绍下三种测量模式:
EXACTLY:表示设置了精确的值,一般当View设置其宽、高为精确值、match_parent时,为
EXACTLY;
AT_MOST:表示子布局被限制在一个最大值内,一般当View设置其宽、高为wrap_content时,为AT_MOST;
UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
5、重写onLayout()方法:目前这个自定义View还没有用到,ViewGroup中子View的布局方法,用于放置子View的位置,放置子View很简单,只需要重写onLayout方法就可以啦,然后获取子View的实例,调用子View的layout方法实现布局。
6、
重写onDraw()方法:
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
mPaint.setColor(0xff00ff00);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(mTextColor);
canvas.drawText(mText, getWidth() / 2 - mBound.width() / 2, getHeight()
/ 2 + mBound.height() / 2, mPaint);
}
OK,目前位置,已经结束啦。