1. 程式人生 > >Android-View事件處理機制

Android-View事件處理機制

             又遇到過監聽事件無效的情況,然後找了一些資料。在這裡做點筆記方便以後忘了複習!

一,先看一段程式碼:我自定義一個MyButton繼承Button,並重寫了dispatchTouchEvent(...)方法,onTouchEvent(...)方法

package com.example.getmynumber.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;

public class MyButton extends Button{
	
	
	
	public MyButton(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	public MyButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public MyButton(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}
	
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch(event.getAction()){
		   case MotionEvent.ACTION_DOWN:
			   Log.i("wangsongbin", "dispatchTouchEvent:ACTION_DOWN");
			   break;
		   case MotionEvent.ACTION_MOVE:
			   Log.i("wangsongbin", "dispatchTouchEvent:ACTION_MOVE");
			   break;
		   case MotionEvent.ACTION_UP:
			   Log.i("wangsongbin", "dispatchTouchEvent:ACTION_UP");
			   break;
		}
		return super.dispatchTouchEvent(event);
	}
	
	
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		   case MotionEvent.ACTION_DOWN:
			   Log.i("wangsongbin", "onTouchEvent:ACTION_DOWN");
			   break;
		   case MotionEvent.ACTION_MOVE:
			   Log.i("wangsongbin", "onTouchEvent:ACTION_MOVE");
			   break;
		   case MotionEvent.ACTION_UP:
			   Log.i("wangsongbin", "onTouchEvent:ACTION_UP");
			   break;

		}
		return super.onTouchEvent(event);
	}

}
在xml檔案中引用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    

    <com.example.getmynumber.view.MyButton
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="hello touchEvent"/>
    
</RelativeLayout>

在Activity中為其繫結監聽器
package com.example.getmynumber.activity.textview;

import com.example.getmynumber.R;
import com.example.getmynumber.view.MyButton;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class TouchEventTestActivity extends Activity{
	
	private MyButton mButton;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_touchtest);
		mButton=(MyButton) findViewById(R.id.btn);
		mButton.setOnTouchListener(new OnTouchListener() {
			
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				switch(event.getAction()){
				   case MotionEvent.ACTION_DOWN:
					   Log.i("wangsongbin", "onTouch:ACTION_DOWN");
					   break;
				   case MotionEvent.ACTION_MOVE:
					   Log.i("wangsongbin", "onTouch:ACTION_MOVE");
					   break;
				   case MotionEvent.ACTION_UP:
					   Log.i("wangsongbin", "onTouch:ACTION_UP");
					   break;
				}
				return false;
			}
		});
	}

}

我適當的擦了一下MyButton控制元件,結果如下:


可一個看到一個執行順序:dispatchTouchEvent(...)  ,觸控監聽touch(...),onTouchEvent(...)

其實我們看一下View的dispatchTouchEvent(...)的原始碼就清楚了。

    public boolean dispatchTouchEvent(MotionEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {  //監聽器onTouch(...)方法的執行位置
                return true;
            }

            if (onTouchEvent(event)) {  //onTouchEvnent(...)方法的執行位置。
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
    }
我們可以很明顯的看到監聽器的onTouch方法的執行位置在onTouchEvent執行方法的前面。而且我還可以看到,如果li(即控制元件所有

監聽器的封裝類)不為null,li.mTouchListener(我們設定的觸控監聽)不為null, 控制元件的狀態為Enable,觸控監聽的onTouch方法

返回true(即觸控事件被消耗掉),我們的onTouchEvent方法可能執行不了。

二,onClickListener,與onLongClickListener 

在這裡我重新為MyButton添加了onClickListener和onLongClickListener監聽。然後我手指放下,稍稍等一會再拿起來。

package com.example.getmynumber.activity.textview;

import com.example.getmynumber.R;
import com.example.getmynumber.view.MyButton;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;

public class TouchEventTestActivity extends Activity{
	
	private MyButton mButton;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_touchtest);
		mButton=(MyButton) findViewById(R.id.btn);
		mButton.setOnTouchListener(new OnTouchListener() {
			
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				switch(event.getAction()){
				   case MotionEvent.ACTION_DOWN:
					   Log.i("wangsongbin", "onTouch:ACTION_DOWN");
					   break;
				   case MotionEvent.ACTION_MOVE:
					   Log.i("wangsongbin", "onTouch:ACTION_MOVE");
					   break;
				   case MotionEvent.ACTION_UP:
					   Log.i("wangsongbin", "onTouch:ACTION_UP");
					   break;
				}
				return false;
			}
		});
		
		mButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				Log.i("wangsongbin", "onClick:ACTION_UP");
			}
		});
		
		mButton.setOnLongClickListener(new OnLongClickListener() {
			
			@Override
			public boolean onLongClick(View v) {
				Log.i("wangsongbin", "onLongClick:ACTION_UP");
				return false;
			}
		});
	}

}
我特地把Action_MOVE去掉,不然很難看到效果。所以去掉後看到的結果是!


onLongClick方法在Action_Down之後,Action_UP之前就執行了。

這段邏輯在onTouchEvent中:

onLongClick的執行途徑:

Action_DWON時會啟動一個115ms延遲的任務,如果115後如果還沒有觸發Action_up,則繼續啟動一個500-115ms的延遲任務

如果500ms後,則執行onLongClick方法。

onClick的執行途徑:

在Action_Dwon是標PrePressed,如果在115~500ms內,觸發了Action_up則執行onClick方法。

如果在500ms以後觸發了Action_up,

1,onLongClickListener.onLongClick(...)返回true,則不會執行onClickListener.onClick(...),

2,onLongClickListener.onLongClick(...)返回false,則依然會執行onClickListener.onClick(...)