天天看點

android.view.View及android.view.ViewGroup

本文摸索了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檔案設定值

android.view.View及android.view.ViewGroup

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) {
	};
};