做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); }
<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,目前位置,已经结束啦。
项目下载