Android-View事件處理機制
又遇到過監聽事件無效的情況,然後找了一些資料。在這裡做點筆記方便以後忘了複習!
一,先看一段程式碼:我自定義一個MyButton繼承Button,並重寫了dispatchTouchEvent(...)方法,onTouchEvent(...)方法
在xml檔案中引用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 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(...)