日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android中事件分发机制的总结

發(fā)布時間:2025/4/16 Android 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android中事件分发机制的总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文出處:http://blog.csdn.net/jdsjlzx/article/details/52355249

事件傳遞雖然算不上某個單獨的知識點,但是在實際項目開發(fā)中肯定會碰到,如果不明白其中的原理,那在設計各種滑動效果時就會感到很困惑。

關于事件的傳遞,我們可能會有以下疑問:

  • 事件是如何傳遞的
  • 事件是如何處理的
  • 自定義view的時候,事件也沖突了怎么解決

帶著這三個疑問,我們來總結(jié)一下事件傳遞機制是怎么回事。

事件分發(fā)原理

事件是如何傳遞的:

  • 首先由Activity分發(fā),分發(fā)給根View,也就是DecorView(DecorView為整個Window界面的最頂層View)

  • 然后由根View分發(fā)到子的View

如下圖所示:

再來看下面這張圖:(這張圖是整個事件傳遞機制的核心)

如上圖所示:

在ViewGroup中可以通過onInterceptTouchEvent方法對事件傳遞進行攔截。

onInterceptTouchEvent方法:

  • 返回true代表不允許事件繼續(xù)向子View傳遞,將會觸發(fā)當前View的onTouchEvent(),進行事件的消費;
  • 返回false代表不對事件進行攔截,事件可以傳遞給子View;
  • 默認返回false。

事件是如何處理的:


再來看下面這張圖:

上圖顯示:子View中如果將傳遞的事件消費掉,父類的ViewGroup中將無法接收到任何事件。

onTouch和onClick事件同時發(fā)生的問題

首先這里要解釋一下各種概念,避免混淆。

各種概念

  • 事件:混合體(可能是點擊事件也可能是觸摸事件)。

  • 觸摸事件:按下,滑動和離開

  • 點擊事件:按下,停留一會兒和離開

觸摸onTouch事件和點擊onClick事件有什么關系?

  • 執(zhí)行先后不一樣。觸摸事件先執(zhí)行

  • 觸摸事件返回值影響點擊事件(前者影響后者,而后者不影響前者)

onTouch和onClick事件同時執(zhí)行:

如果按鈕的onTouch和onClick方法同時執(zhí)行,會有什么效果呢?我們通過代碼來看一下:

import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.Button;public class MainActivity extends Activity {private static final String TAG = "MainActivity";private Button btn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);//按鈕的touch觸摸事件btn.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "btn is MotionEvent.ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "btn is MotionEvent.ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "btn is MotionEvent.ACTION_UP");break;}return false; //默認的返回值}});//按鈕的點擊事件btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.d(TAG, "btn is click");}});} }

上方代碼中,按鈕btn既包含了onTouch事件,也包含了onClick事件,現(xiàn)在運行程序,點擊按鈕,后臺打印的日志如下:

通過上方日志我們可以看到,onTouch事件是比onClick事件先執(zhí)行的。

備注:這里提示一下,如果我們僅僅只是用手指點擊按鈕,然后馬上松開,onTouch事件中只會執(zhí)行ACTION_DOWN和ACTION_UP動作;如果用手機點擊按鈕,并且手指還在按鈕上滑動了一會兒,那么滑動的過程中,ACTION_MOVE動作就會不停的執(zhí)行。現(xiàn)在我們應該能明白這三個動作的含義了吧?

只執(zhí)行onTouch事件,不執(zhí)行onClick事件:

如果按鈕的onTouch和onClick方法同時執(zhí)行,在有些情況下不太滿足產(chǎn)品的需求。那如果只想執(zhí)行onTouch事件,不執(zhí)行onClick事件,該怎么做呢?很簡單,只需要在上方代碼中,將第39行的代碼改為return true,就行了,即:將onTouch方法的返回值改為true,就會只執(zhí)行onTouch事件,不執(zhí)行onClick事件。改完代碼之后,后臺的運行效果如下:

為什么這樣改代碼就可以了呢?這就需要從源碼的角度來理解了。

button按鈕中沒有dispatchTouchEvent方法,需要去它的父類View.java中去找dispatchTouchEvent方法。源碼如下所示:

上圖分析:紅框部分的onTouch()方法默認是返回false,所以就會執(zhí)行藍框部分的代碼,即:調(diào)用onTouchEvent()方法。而onTouchEvent方法中,會在ACTION_UP動作里面會去初始化onClick事件。如下圖所示:

于是onClick事件就得到了執(zhí)行。

onClick和onLongClick事件能同時發(fā)生:

我們通過代碼來演示一下。

1、onTouch事件、onLongClick事件、onClick事件默認是同時執(zhí)行:(執(zhí)行的先后順序:onTouch > onLongClick > onClick)

完整版代碼如下:

import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.Button;public class MainActivity extends Activity {private static final String TAG = "MainActivity";private Button btn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);//按鈕的touch事件btn.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "btn is MotionEvent.ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "btn is MotionEvent.ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "btn is MotionEvent.ACTION_UP");break;}return false; //默認的返回值}});//按鈕的onLongClick事件btn.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {Log.d(TAG, "btn is onLongClick");return false; //默認的返回值}});//按鈕的onClick事件btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.d(TAG, "btn is onClick");}});} }

運行程序后,長按按鈕,后臺日志如下:

源碼比較長,就不貼出來了,通過查看源碼我們得知,當onTouch事件中的ACTION_DOWN動作執(zhí)行180ms之后,就會執(zhí)行onLongClick事件。

那我們現(xiàn)在知道了,如果在一個按鈕上按下的時間過長,onLongClick事件會比onClick事件先執(zhí)行。

2、只執(zhí)行onTouch事件和onLongClick事件,不執(zhí)行onClick事件:

為了實現(xiàn)這種邏輯,也很簡單,只需要將上方的第50行代碼改為return true就行了,即:將onLongClick方法的返回值改為true,就不會執(zhí)行onClick事件了。改完代碼之后,后臺的運行效果如下:

為什么這樣改代碼就可以了呢?這就需要從源碼的角度來理解了。在View.java中的dispatchTouchEvent方法里,ACTION_UP動作里面對onLongTouch事件進行了處理,具體源碼就不展示出來了,這個有點復雜。

事件傳遞機制調(diào)用順序:

ViewGroup的事件傳遞方法:

  • dispatchTouchEvent
  • onInterceptTouchEvent
  • onTouchEvent

View的事件傳遞方法:

  • View的dispatchTouchEvent
  • View的onTouchEvent
    注意,只有父的ViewGroup容器才有onInterceptTouchEvent方法。這也很好理解,最小的那個子的view沒必要再攔截了,因為無法繼續(xù)向下傳遞事件,是否攔截已經(jīng)沒有意義了。

接下來,我們用LinearLayout代表ViewGroup,用Button代表子View,然后去重寫LinearLayout和Button中的事件傳遞方法,看一下各個方法的調(diào)用順序。代碼如下:

1. MyLinearLayout.java:(重寫LinearLayout中的事件傳遞方法)

package com.example.smyhvae.touchdemo;import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout;/*** Created by smyhvae on 2015/9/11.*/ public class MyLinearLayout extends LinearLayout {private static final String TAG = "MainActivity";public MyLinearLayout(Context context) {super(context);}public MyLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);}public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "ViewGroup dispatchTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "ViewGroup dispatchTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "ViewGroup dispatchTouchEvent ACTION_UP");break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "ViewGroup onInterceptTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "ViewGroup onInterceptTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "ViewGroup onInterceptTouchEvent ACTION_UP");break;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "ViewGroup onTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "ViewGroup onTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "ViewGroup onTouchEvent ACTION_UP");break;}return super.onTouchEvent(event);} }

2. MyButton.java:(重寫B(tài)utton中的事件傳遞方法,注意:這里面沒有onInterceptTouchEvent方法)

package com.example.smyhvae.touchdemo;import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.Button;/*** Created by smyhvae on 2015/9/11.*/ public class MyButton extends Button {private static final String TAG = "MainActivity";public MyButton(Context context) {super(context);}public MyButton(Context context, AttributeSet attrs) {super(context, attrs);}public MyButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "View dispatchTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "View dispatchTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "View dispatchTouchEvent ACTION_UP");break;}return super.dispatchTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN: //按下的動作Log.d(TAG, "View onTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE: //滑動的動作Log.d(TAG, "View onTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP: //離開的動作Log.d(TAG, "View onTouchEvent ACTION_UP");break;}return true;} }

上方代碼中,將onTouchEvent方法的返回值修改為true(59行),表示這個子的view希望消費這個事件。

3. activity_main.xml:

<com.example.smyhvae.touchdemo.MyLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.example.smyhvae.touchdemo.MyButtonandroid:id="@+id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按鈕"/></com.example.smyhvae.touchdemo.MyLinearLayout>

上面的xml中,將我們自定義的MyLinearLayout和MyButton用上了。

4. MainActivity.java:

package com.example.smyhvae.touchdemo;import android.app.Activity; import android.os.Bundle; import android.widget.Button;public class MainActivity extends Activity {private static final String TAG = "MainActivity";private Button btn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn); } }

分析之前,我們先記住下面這句話:(記住這句話,分析下面的日志就好理解了)

在Android中,一切事件處理的開始都是從Down事件開始的,如何你處理了Down事件,其他的事件就都收不到了。

  • 按照上面的代碼,后臺日志如下:

通過上圖的箭頭處可以看到,事件是傳遞給了子view去消費。

  • 上面的代碼中,將MyLinearLayout.java中onTouchEvent方法返回值修改為true,將MyButton.java中的onTouchEvent方法返回值修改為false,后臺日志如下:

通過上圖的箭頭處可以看到,事件是傳遞給了子view,子view說它不消費了,于是又回傳給父的ViewGroup,ViewGroup消費了這個事件。

  • 上面的代碼中,將MyLinearLayout.java中onTouchEvent方法返回值修改為true,將MyLinearLayout.java中onInterceptTouchEvent方法返回值修改為true,后臺日志如下:

通過上圖的箭頭處可以看到,此時ViewGroup已經(jīng)將事件攔截了,所以根本就不會傳遞給子的Veiw,父的ViewGroup自己把事件給消費掉了。

總結(jié)

以上是生活随笔為你收集整理的Android中事件分发机制的总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。