本文摸索了View的几个特性,measure、layout、draw及event处理,包括它们的功能、步骤及调用栈。
首先measure获得View的大小,即width和height;其次由layout获得View的位置,及left、top、right、bottom;最后由draw来绘制内容。
由event来处理触屏事件,如单击、双击、滑动等。
1、下面用一个自定义View来演示具体功能。
public class MyView extends View {
private static final String TAG = MyView.class.getSimpleName();
GestureDetector mGestureDetector;
public MyView(Context context){
this(context, null);
}
public MyView(final Context context, AttributeSet attrs) {
super(context, attrs);
Log.println(Log.DEBUG, TAG, "MyView");
mGestureDetector = new GestureDetector(context, mSimpleOnGestureListener,null);
}
@Override
protected void onDraw(Canvas canvas) {
Log.println(Log.DEBUG, TAG, "onDraw-"+getWidth()+"-"+getHeight());
//do your drawing
//ViewGroup.drawChild->View.draw->View.onDraw
super.onDraw(canvas);
canvas.drawColor(Color.RED);
}
@Override
public void draw(Canvas canvas) {
Log.d(TAG, "draw");
super.draw(canvas);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//ViewGroup.onLayout->View.layout->View.onLayout
Log.println(Log.DEBUG, TAG, "onLayout-"+changed+"-"+left+"-"+top+"-"+right+"-"+bottom);
}
@Override
public void layout(int l, int t, int r, int b) {
Log.d(TAG, "layout-"+l+"-"+t+"-"+r+"-"+b);
super.layout(l, t, r, b);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//ViewGroup.onMeasure->View.measure->View.onMeasure
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.println(Log.DEBUG, TAG, "onMeasure-"+widthMode+"-"+heightMode+"-"+widthSize+"-"+heightSize);
//在这里设置自定义视图的大小
setMeasuredDimension(widthSize/2, widthSize/2+100);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
//接受并在这里处理触摸事件
//MyView->MyLinearLayout->MyScrollView->MainActivity
Log.println(Log.DEBUG, TAG, "onTouchEvent");
// return super.onTouchEvent(event);
return mGestureDetector.onTouchEvent(event);
}
//implements OnGestureListener, OnDoubleTapListener
private SimpleOnGestureListener mSimpleOnGestureListener = new GestureDetector.SimpleOnGestureListener(){
public boolean onDown(MotionEvent e) {
Log.d(TAG, "onDown-"+e.getX()+"-"+e.getY()+"-"+e.hashCode());
//起点
return true;
};
public boolean onDoubleTap(MotionEvent e) {
Log.d(TAG, "onDoubleTap-"+e.getX()+"-"+e.getY()+"-"+e.hashCode());
return true;
};
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d(TAG, "onFling-"+e1.getX()+"-"+e1.getY()+"-"+e1.hashCode());
return true;
};
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.d(TAG, "onScroll-"+e1.getX()+"-"+e1.getY()+"-"+e1.hashCode());
return true;
};
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d(TAG, "onSingleTapConfirmed-"+e.getX()+"-"+e.getY()+"-"+e.hashCode());
return true;
};
public boolean onDoubleTapEvent(MotionEvent e) {
Log.d(TAG, "onDoubleTapEvent-"+e.getX()+"-"+e.getY()+"-"+e.hashCode());
return true;
};
public boolean onSingleTapUp(MotionEvent e) {
Log.d(TAG, "onSingleTapUp-"+e.getX()+"-"+e.getY()+"-"+e.hashCode());
return true;
};
};
}
public class MyLinearLayout extends LinearLayout {
private static final String TAG = MyLinearLayout.class.getSimpleName();
public MyLinearLayout(Context context){
this(context, null);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "MyLinearLayout");
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "onLayout-"+changed+"-"+l+"-"+t+"-"+r+"-"+b);
View view = getChildAt(0);
Log.d(TAG, view.getWidth()+"|"+view.getHeight()+"|"+view.getMeasuredWidth()+"|"+view.getMeasuredHeight());
view.layout(100, 100, view.getMeasuredWidth()+100, view.getMeasuredHeight()+100);
}
@Override
protected void onDraw(Canvas canvas) {
Log.d(TAG, "onDraw");
super.onDraw(canvas);
}
@Override
public void draw(Canvas canvas) {
Log.d(TAG, "draw");
super.draw(canvas);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
Log.d(TAG, "drawChild");
return super.drawChild(canvas, child, drawingTime);
}
@Override
protected void dispatchDraw(Canvas canvas) {
Log.d(TAG, "dispatchDraw");
super.dispatchDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent");
//View.onTouchEvent
return super.onTouchEvent(event);
}
}
public class MyScrollView extends ScrollView {
private static final String TAG = MyScrollView.class.getSimpleName();
public MyScrollView(Context context){
this(context, null);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "MyScrollView");
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, "onMeasure");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "onLayout-"+changed+"-"+l+"-"+t+"-"+r+"-"+b);
super.onLayout(changed, l, t, r, b);
}
@Override
protected void onDraw(Canvas canvas) {
//draw->onDraw->dispatchDraw->drawChild
Log.d(TAG, "onDraw");
super.onDraw(canvas);
}
@Override
public void draw(Canvas canvas) {
Log.d(TAG, "draw");
super.draw(canvas);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
Log.d(TAG, "drawChild");
return super.drawChild(canvas, child, drawingTime);
}
@Override
protected void dispatchDraw(Canvas canvas) {
Log.d(TAG, "dispatchDraw");
super.dispatchDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent");
//ScrollView.onTouchEvent
return super.onTouchEvent(event);
}
}
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.println(Log.DEBUG, TAG, "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent");
return super.onTouchEvent(event);
}
}
<?xml version="1.0" encoding="utf-8"?>
<com.qinuli.customviewtest.MyScrollView
android:layout_width="match_parent"
android:layout_height="300dp"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#00ff00">
<com.qinuli.customviewtest.MyLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#0000ff">
<com.qinuli.customviewtest.MyView
android:id="@+id/myView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.qinuli.customviewtest.MyLinearLayout>
</com.qinuli.customviewtest.MyScrollView>
2、分析
onTouchEvent的触发顺序为MyView->MyLinearLayout->MyScrollLayout->MainActivity
1)写一个扩展SimpleOnGestureListener的类。SimpleOnGestureListener是OnGestureListener, OnDoubleTapListener两个接口的缺省适配,GestureDetector的嵌套类。OnGestureListener, OnDoubleTapListener是GestureDetector的嵌套接口。
2)通过GestureDetector构造传参封装SimpleOnGestureListener,即new GestureDetector(Context,OnGestureListener)
3)在View.onTouchEvent方法调用实现触摸事件捕捉,即return mGestureDetector.onTouchEvent(event)
3、View自定义属性
1)在res/values/attrs.xml文件resources标签下添加子标签,format类型可以是string、integer、boolean等
<declare-styleable name="trendchart">
<attr name="showPrompt" format="boolean"/>
</declare-styleable>
2)在使用该属性的xml文件设置值

3)在自定义View类中取值
private void init(AttributeSet attrs){
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.trendchart);
showPrompt = ta.getBoolean(R.styleable.trendchart_showPrompt, false);
ta.recycle();
}
4、实现视图缩放
mScaleGestureDetector = new ScaleGestureDetector(this, mScaleGestureListener);//注册
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {//先
Log.d(TAG, "dispatchTouchEvent-"+ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {//后
Log.d(TAG, "onTouchEvent-"+event.getAction());
if(mScaleGestureDetector!=null){
mScaleGestureDetector.onTouchEvent(event);
}
return super.onTouchEvent(event);
}
SimpleOnScaleGestureListener mScaleGestureListener = new SimpleOnScaleGestureListener(){
@Override
public boolean onScale(ScaleGestureDetector detector) {
Log.d(TAG, detector.getCurrentSpan()+"/"+detector.getPreviousSpan());
return super.onScale(detector);
}
public boolean onScaleBegin(ScaleGestureDetector detector) {
return super.onScaleBegin(detector);
};
public void onScaleEnd(ScaleGestureDetector detector) {
};
};