本文摸索了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) {
};
};