最近一直在接触自定义控件的知识,自己就尝试着写了一个小的demo,算是对自定义知识点进行下总结
今天先来看下自定义控件需要重写的三个重要方法
看代码
package com.example.testcode; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.view.View; public class DrawView extends View { public DrawView(Context context) { super(context); Log.e("123", "drawview_1"); // TODO Auto-generated constructor stub } public DrawView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Log.e("123", "drawview_3"); // TODO Auto-generated constructor stub } public DrawView(Context context, AttributeSet attrs) { super(context, attrs); Log.e("123", "drawview_2"); // TODO Auto-generated constructor stub } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); Log.e("123", "MeasureSpec.UNSPECIFIED==" + MeasureSpec.UNSPECIFIED); Log.e("123", "MeasureSpec.AT_MOST==" + MeasureSpec.AT_MOST); Log.e("123", "MeasureSpec.EXACTLY==" + MeasureSpec.EXACTLY); Log.e("123", "widthMeasureSpec===" + widthMeasureSpec); Log.e("123", "heightMeasureSpec===" + heightMeasureSpec); Log.e("123", "widthMode==" + widthMode + " widthSize===" + widthSize); Log.e("123", "heightMode==" + heightMode + " heightSize===" + heightSize); //这两个方法必须有一个,否则会报错 //super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(75, 75); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // TODO Auto-generated method stub Log.e("123", "change===" + changed + " left===" + left + " top===" + top + " right===" + right + " bottom===" + bottom); super.onLayout(changed, left, top, right, bottom); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub Log.e("123", "onDraw"); super.onDraw(canvas); } }
xml中使用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.testcode.MainActivity" > <com.example.testcode.DrawView android:layout_width="100dp" android:layout_height="100dp" android:background="#ff0000" /> </RelativeLayout>
先看下我们的打印结果
1 09-28 22:53:26.901: E/123(17442): drawview_2 2 09-28 22:53:26.959: E/123(17442): MeasureSpec.UNSPECIFIED==0 3 09-28 22:53:26.959: E/123(17442): MeasureSpec.AT_MOST==-2147483648 4 09-28 22:53:26.959: E/123(17442): MeasureSpec.EXACTLY==1073741824 5 09-28 22:53:26.959: E/123(17442): widthMeasureSpec===1073741974 6 09-28 22:53:26.960: E/123(17442): heightMeasureSpec===-2147482958 7 09-28 22:53:26.960: E/123(17442): widthMode==1073741824 widthSize===150 8 09-28 22:53:26.960: E/123(17442): heightMode==-2147483648 heightSize===690 9 09-28 22:53:26.960: E/123(17442): MeasureSpec.UNSPECIFIED==0 10 09-28 22:53:26.960: E/123(17442): MeasureSpec.AT_MOST==-2147483648 11 09-28 22:53:26.961: E/123(17442): MeasureSpec.EXACTLY==1073741824 12 09-28 22:53:26.961: E/123(17442): widthMeasureSpec===1073741899 13 09-28 22:53:26.961: E/123(17442): heightMeasureSpec===1073741974 14 09-28 22:53:26.961: E/123(17442): widthMode==1073741824 widthSize===75 15 09-28 22:53:26.961: E/123(17442): heightMode==1073741824 heightSize===150 16 09-28 22:53:27.001: E/123(17442): change===true left===0 top===0 right===75 bottom===75 17 09-28 22:53:27.030: E/123(17442): MeasureSpec.UNSPECIFIED==0 18 09-28 22:53:27.031: E/123(17442): MeasureSpec.AT_MOST==-2147483648 19 09-28 22:53:27.031: E/123(17442): MeasureSpec.EXACTLY==1073741824 20 09-28 22:53:27.031: E/123(17442): widthMeasureSpec===1073741974 21 09-28 22:53:27.031: E/123(17442): heightMeasureSpec===-2147482958 22 09-28 22:53:27.031: E/123(17442): widthMode==1073741824 widthSize===150 23 09-28 22:53:27.031: E/123(17442): heightMode==-2147483648 heightSize===690 24 09-28 22:53:27.031: E/123(17442): MeasureSpec.UNSPECIFIED==0 25 09-28 22:53:27.031: E/123(17442): MeasureSpec.AT_MOST==-2147483648 26 09-28 22:53:27.031: E/123(17442): MeasureSpec.EXACTLY==1073741824 27 09-28 22:53:27.032: E/123(17442): widthMeasureSpec===1073741899 28 09-28 22:53:27.032: E/123(17442): heightMeasureSpec===1073741974 29 09-28 22:53:27.032: E/123(17442): widthMode==1073741824 widthSize===75 30 09-28 22:53:27.032: E/123(17442): heightMode==1073741824 heightSize===150 31 09-28 22:53:27.033: E/123(17442): change===false left===0 top===0 right===75 bottom===75 32 09-28 22:53:27.045: E/123(17442): onDraw
从上面的结果我们可以知道xml中控件加载过程
1.xml中使用的控件,加载的时候,调用的是控件两个参数的方法
public DrawView(Context context, AttributeSet attrs) { }
一个是上下文,一个是属性
2.解析来会调用
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { }
方法,这个方法是用来确定控件的宽高的,这个值是从控件的width、height中读出来的。但是我们发现,这两个值的打印结果很奇怪,甚至还有负数。网上对此的解释是
onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值。
具体什么我也不清楚,不过,它其实是包含了很多信息在里面的。一个就是,从这个数值,我们可以获得这个控件宽跟高的形式
使用如下方法
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
这个mode的取值,可以有一下三种
MeasureSpec.EXACTLY-是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
MeasureSpec.AT_MOST 是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行 变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。