1. 說(shuō)明
這篇文章分析下View的Touch事件分發(fā)的順序蒸甜,這里記錄一下:
2. 分析
public class MainActivity extends AppCompatActivity {
private TouchView touch_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touch_view = (TouchView) findViewById(R.id.touch_view);
touch_view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG" , "觸摸onTouch() -> " + event.getAction()) ;
return true;
}
});
touch_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG" , "點(diǎn)擊onClick -> ") ;
}
});
}
}
public class TouchView extends View {
public TouchView(Context context) {
super(context);
}
public TouchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG" , "onTouchEvent -> " + event.getAction()) ;
return super.onTouchEvent(event);
}
}
現(xiàn)象一:onTouchListener、onTouch睛竣、onClick三個(gè)都有的情況下晰房,并且onTouchListener返回false,表示自己不處理事件酵颁,直接把事件分發(fā)下去給子控件嫉你,所以3個(gè)方法都會(huì)執(zhí)行;
03-25 08:18:36.589 3344-3344/com.jackchen.view_day010_2 E/TAG: 觸摸onTouch() -> 0
03-25 08:18:36.589 3344-3344/com.jackchen.view_day010_2 E/TAG: onTouchEvent -> 0
03-25 08:18:36.672 3344-3344/com.jackchen.view_day010_2 E/TAG: 觸摸onTouch() -> 2
03-25 08:18:36.672 3344-3344/com.jackchen.view_day010_2 E/TAG: onTouchEvent -> 2
03-25 08:18:36.851 3344-3344/com.jackchen.view_day010_2 E/TAG: 觸摸onTouch() -> 1
03-25 08:18:36.851 3344-3344/com.jackchen.view_day010_2 E/TAG: onTouchEvent -> 1
03-25 08:18:36.852 3344-3344/com.jackchen.view_day010_2 E/TAG: 點(diǎn)擊onClick ->
由以上可知:0代表DOWN躏惋、2代表MOVE幽污、1代表UP,執(zhí)行順序如下:
onTouchListener的DOMN -> onTouch的DOWN ->
onTouchListener的MOVE -> onTouch的MOVE ->
onTouchListener的UP -> onTouch的UP ->
onClick
現(xiàn)象二:onTouchListener簿姨、onTouch距误、onClick三個(gè)都有的情況下,并且onTouchListener返回true扁位,表示自己處理事件准潭,就不會(huì)把事件分發(fā)給子控件,所以就只會(huì)執(zhí)行自己而方法域仇;
03-25 08:25:30.493 23991-23991/? E/TAG: 觸摸onTouch() -> 0
03-25 08:25:30.625 23991-23991/? E/TAG: 觸摸onTouch() -> 2
03-25 08:25:30.625 23991-23991/? E/TAG: 觸摸onTouch() -> 1
執(zhí)行順序如下:
onTouchListener的DOWN -> onTouchListener的MOVE -> onTouchListener的UP
現(xiàn)象三:只有 onTouchEvent()刑然、onClickListener(),前提是onTouchEvent返回true暇务,表示自己處理事件泼掠,就不會(huì)把事件分發(fā)下去,所以就只執(zhí)行自己的方法垦细,不會(huì)執(zhí)行下邊的方法择镇;
03-25 09:19:11.648 3005-3005/? E/TAG: onTouchEvent -> 0
03-25 09:19:11.752 3005-3005/? E/TAG: onTouchEvent -> 2
03-25 09:19:11.752 3005-3005/? E/TAG: onTouchEvent -> 1
執(zhí)行順序如下:
onTouchEvent的DOWN -> onTouchEvent的MOVE -> onTouchEvent的UP,不會(huì)執(zhí)行onClick事件
onClick不執(zhí)行的原因:
因?yàn)閛nClick事件是在 View中的 onTouchEvent中的 case MotionEvent.ACTION_UP:里邊調(diào)用了performClick()括改,而這里onTouchEvent返回的是true腻豌,而不是super.onTouchEvent(event),所以就不會(huì)執(zhí)行View中的方法,所以就不會(huì)執(zhí)行 View中的 onTouchEvent中的 case MotionEvent.ACTION_UP吝梅,所以onClick不會(huì)調(diào)用
現(xiàn)象四: onTouchListener onTouchEvent onClick dispatchTouchEvent 都有虱疏,前提是dispatchTouchEvent返回true,那么一個(gè)方法都不會(huì)執(zhí)行憔涉;如果dispatchTouchEvent返回true订框,并且添加super.dispatchTouchEvent(event) ;那么現(xiàn)象和現(xiàn)象一是一樣的析苫。
public class MainActivity extends AppCompatActivity {
private TouchView touchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touchView = (TouchView) findViewById(R.id.touch_view);
touchView.setEnabled(false); //touchView不可用
touchView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG" , "onTouch -> "+event.getAction()) ;
return false;
}
});
touchView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG" , "onClick") ;
}
});
}
}
public class TouchView extends View {
public TouchView(Context context) {
this(context, null);
}
public TouchView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG" , "onTouchEvent -> "+event.getAction()) ;
return super.onTouchEvent(event);
}
/**
* 復(fù)寫 dispatchTouchEvent作用就是兜叨,在onTouchEvent()方法中可以返回任何值,return false衩侥、return true国旷、return super.onTouchEvent(event)都是可以的
* 但是在 dispatchTouchEvent必須返回true,并且添加super.dispatchTouchEvent(event) ,這樣的話現(xiàn)象就是現(xiàn)象一
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
super.dispatchTouchEvent(event) ;
Log.e("TAG" , "dispatchTouchEvent -> "+event.getAction()) ;
return true;
}
}
3. View和Touch相關(guān)的有2個(gè)非常重要的方法
3.1>:dispatchTouchEvent()事件分發(fā):
源碼分析:
在View源碼中的 dispatchTouchEvent()方法中:
public boolean dispatchTouchEvent(MotionEvent event) {
// result:默認(rèn)是false
boolean result = false茫死;
// ListenerInfo:里邊存放了 View的所有的 Listener信息
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
return result;
}
以上中:
boolean result = false;
ListenerInfo 很重要跪但,它里邊存放了View的所有的Listener信息,比如:onTouchListener峦萎、onClickListener
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
分析以上代碼可知:
li為true屡久,li.mOnTouchListener != null 為true
(mViewFlags & ENABLED_MASK) == ENABLED 是否是ENABLE可用的
li.mOnTouchListener.onTouch(this, event):
如果返回false,說(shuō)明進(jìn)不去if語(yǔ)句爱榔,那么就直接用初始化的result = false的值被环;
如果返回true,那么 result = true 详幽;
if (!result && onTouchEvent(event)) {
result = true;
}
分析以上代碼可知:
對(duì)于if (!result && onTouchEvent(event)) :
如果上邊的result為false筛欢,那么 !result為true,那么就會(huì)執(zhí)行后邊的 onTouchEvent()方法了唇聘;
如果 result為true版姑,那么 !result為false,那么就不會(huì)執(zhí)行后邊的 onTouchEvent()方法了迟郎;
到目前來(lái)講剥险,我們都還沒(méi)有看到點(diǎn)擊事件?
在View的onTouchEvent中的 case MotionEvent.ACTION_UP里邊調(diào)用了 performClick()方法宪肖,點(diǎn)擊 performClick()進(jìn)入里邊方法后表制,具體代碼是:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
分析:因?yàn)閛nTouchEvent返回true,即return true 匈庭,而沒(méi)有return super.onTouchEvent(event)夫凸,所以就不會(huì)執(zhí)行View,更不會(huì)執(zhí)行View中的onTouchEvent中的case MotionEvent.ACTION_UP阱持,所以就只會(huì)執(zhí)行 onTouchEvent()的Down夭拌、Move、Up事件,不會(huì)執(zhí)行onClick()方法了鸽扁;
3.2>:onTouchEvent()蒜绽,這個(gè)方法一般都被我們復(fù)寫,并且直接return true即可桶现;
注意:
注意:自定義View是沒(méi)有 onInterceptTouchEvent() 攔截方法的
4. 總結(jié)如下:
- 如果onTouchListener返回false躲雅,表示不處理事件,把事件分發(fā)給子控件骡和,讓下級(jí)處理相赁,執(zhí)行順序就是:
onTouchListener、onTouchEvent慰于、onClickListener - 如果onTouchListener返回true钮科,表示自己處理事件,就不會(huì)把事件分發(fā)給子控件婆赠,所以就只會(huì)執(zhí)行onTouchListener