Android事件分发机制详解与实战剖析,一张事件分发流程图,让你彻底搞明白

本文为自己多年来在Android实战开发过程中总结归纳的一些常见问题,现在分享出来希望对初学者有所帮助。 

本文出自门心叼龙的博客,转载请注明出处: https://blog.csdn.net/geduo_83/article/details/86560896    

事件分发是Android开发过程中的重点又是难点, 一张事件分发流程图,让你彻底搞明白。网上有很多文章写事件分发,感觉都没有讲明白,恭喜你,今天你看到好文章了,你彻底搞清楚...

目录

1.在Android操作系统中,拥有事件传递功能的类都有哪些?

2.触摸事件的类型?

3.事件传递的三个阶段?

4.简述View的事件传递机制?

5.简述ViewGroup的事件传递机制?

6.事件分发流程图

7.实战案例


1.在Android操作系统中,拥有事件传递功能的类都有哪些?

  • Activity:拥有dispathTouchEvent和onTouchEvent方法
  • View:拥有dispathTouchEvent和onTouchEvent方法   
  • ViewGroup:拥有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent

2.触摸事件的类型?

主要有三种:

  • ACTION_DOWN : 手指的按下操作
  • ACTION_MOVE:手指按下后,松开手之前,轻微移动所触发的事件
  • ACTION_UP:手指离开屏幕的操作

3.事件传递的三个阶段?

  • 3.1 按照事件进行划分

  • 3.2 按照View进行划分

结论:无论是View还是ViewGroup,不管他是DispatchTouchEvent还是onTouchEvent方法,方法返回true、返回false的处理逻辑都是一样的,只是调用父类的同名方法的时候处理的逻辑有所不同,View偏重消费、ViewGourp偏重分发

4.简述View的事件传递机制?

触摸事件的传递流程是从dispatchTouchEvent开始的,如果不进行人工干预,则事件将会依照View树的嵌套层次从外层向内层传递,到达最内层的View时,就由它的onTouchVent方法处理
如果事件在传递过程中,进行了人工干预,事件分发函数返回true表示自行消费,返回父类的同名方法则该事件传递给自身的onTouchEvent进行处理,返回false表示该事件会回传给父view的onTouchEvent方法进行处理,此时后面的事件都接受不到了,最后由哪个View处理,以后的所有事件都交由它来处理
如果事件在传递过程中,进行了人工干预,事件处理函数,返回true和调用同名方法表示该事件被消费,返回false则表示该事件回传给父类的同名方法进行处理
事件触发是先触发onTouch,再触发onClick,如果onTouch方法返回tue,表示消费掉该事件,不在继续进行事件传递,onClick也不会被调用,如果onTouch方法返回false,则继续会事件传递,onClick会被调用

5.简述ViewGroup的事件传递机制?

触摸事件的传递顺序是由Activity到ViewGroup,再由ViewGroup递归传递给他的子View,ViewGroup通过onInterceptTouchEvent方法对事件进行拦截,如果该方法返回true,则事件不会继续往下传递给子View,如果返回false或者是调用super.onInterceptTouchEvent,则事件会继续会传递给子View
如果事件在传递过程中,进行了人工干预,事件分发函数返回true表示事件被自行消费,返回false,则回传给父View的onTouchEvent进行处理,此时后面的事件都接受不到了,调用同名方法则继续传递
如果事件在传递过程中,进行了人工干预,事件处理函数,返回true则表示该事件被消费,返回false和调用同名方法则表示该事件回传给父类的同名方法进行处理

6.事件分发流程图

一张android事件分发流程,让你彻底搞明白Android的事件分发机制

ViewGroup的事件分发的伪代码: 

    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (onInterceptTouchEvent(ev)) {
            if (!(mOnTouchListener != null && mOnTouchListener.onTouchEvent(ev))) {
                consume = onTouchEvent(ev);
            }
        } else {
            consume = child.dispatchTouchEvent(ev);
        }
        return consume;
    }

    public void onTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (mOnClickListener != null) {
            mOnClickListener.onClick(this);
            consume = true;
        }
        return consume;
    }

View的事件分发伪代码: 

 public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        consume = onTouchEvent(ev);
        return consume;
    }

    public void onTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (mOnClickListener != null) {
            mOnClickListener.onClick(this);
            consume = true;
        }
        return consume;
    }

7.实战案例

实现效果如下,底部的行程详情可以往上拖动覆盖在地图之上,也可以往下拖动停止在屏幕的正中位置,地图相关操作:放大、缩小、移动都能正常的响应,怎么实现?下面就是具体的源码实现过程

  • 7.1 事件分发处理
mTransparentView = findViewById(R.id.view_tansparent);
mTransparentView.setListener(new TransparentView.TouchEventListener() {
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return mMapView.dispatchTouchEvent(event);
    }
});
mScrollView = findViewById(R.id.view_scrollview);
mScrollView.setListener(new TransparentView.TouchEventListener() {
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Rect rect = new Rect();
        mTransparentView.getLocalVisibleRect(rect);
        if(rect.contains((int)event.getX(),(int)event.getY())){
            return true;
        }else{
            return false;
        }
    }
});
  • 7.2 自定义ViewTransparentView
public class TransparentView extends View {
    TouchEventListener mListener;
    public interface TouchEventListener{
        boolean dispatchTouchEvent(MotionEvent event);
    }
    public TransparentView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if(mListener != null){
            return  mListener.dispatchTouchEvent(event);
        }else{
            return super.dispatchTouchEvent(event);
        }
    }

    public void setListener(TouchEventListener listener) {
        mListener = listener;
    }
}
  • 7.3 自定义TransScrollView
public class TransScrollView extends NestedScrollView {
    public TransparentView.TouchEventListener mListener;

    public TransScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mListener != null && mListener.dispatchTouchEvent(ev)) {
            return false;
        }
        return super.onInterceptTouchEvent(ev);
    }

    public void setListener(TransparentView.TouchEventListener listener) {
        mListener = listener;
    }
}
  • 7.4 布局文件
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <com.amap.api.maps.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        />
    <com.zhijiaxing.travel.trip.record.view.TransScrollView
        android:id="@+id/view_scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"

        >
        <com.zhijiaxing.travel.trip.record.view.TransparentView
            android:id="@+id/view_tansparent"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:background="#00000000"
            />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#ffffff"
            android:orientation="vertical"
            >
        </LinearLayout>

    </LinearLayout>
    </com.zhijiaxing.travel.trip.record.view.TransScrollView>
</FrameLayout>

 

发布了102 篇原创文章 · 获赞 918 · 访问量 42万+
展开阅读全文

为什么onTouchEvent方法会在dispatchTouchEvent方法之前执行呢?

03-31

新建一个类MyButton,该类继承自Button 然后重写其中的onTouchEvent和dispatchTouchEvent方法 ``` public class MyButton extends Button { private Context mContext; public MyButton(Context context) { super(context); this.mContext = context; } public MyButton(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:MOVE"); break; case MotionEvent.ACTION_UP: Log.d("TTTT", "context:" + mContext + "MyButton|dispatchTouchEvent|return:" + super.dispatchTouchEvent(event) + "|event:UP"); break; } Log.d("TTTT", "========================== "); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("TTTT", "context:" + mContext + "MyButton|onTouchEvent|return:" + super.onTouchEvent(event) + "|event:DOWN"); break; case MotionEvent.ACTION_MOVE: Log.d("TTTT", "context:" + mContext + "MyButton|onTouchEvent|return:" + super.onTouchEvent(event) + "|event:MOVE"); break; case MotionEvent.ACTION_UP: Log.d("TTTT", "context:" + mContext + "MyButton|onTouchEvent|return:" + super.onTouchEvent(event) + "|event:UP"); break; } return super.onTouchEvent(event); } } ``` 然后将这个Button加入到布局文件中,然后在Activity中给这个Button设置一个onTouchListener ``` <LinearLayout xmlns: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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.testtouchevent.MainActivity" > <com.example.testtouchevent.MyButton android:id="@+id/bt" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按钮" /> </LinearLayout> ``` ``` public class MainActivity extends Activity { private Button bt; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt = (Button) findViewById(R.id.bt); bt.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); } } ``` 按下按钮时候,log显式 ``` 03-31 11:51:08.493: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:DOWN 03-31 11:51:08.493: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|dispatchTouchEvent|return:true|event:DOWN 03-31 11:51:08.493: D/TTTT(1609): ========================== 03-31 11:51:08.493: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:DOWN ``` 松开按钮时候,log显式: ``` 03-31 11:51:10.285: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:UP 03-31 11:51:10.285: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|dispatchTouchEvent|return:true|event:UP 03-31 11:51:10.285: D/TTTT(1609): ========================== 03-31 11:51:10.289: D/TTTT(1609): context:com.example.testtouchevent.MainActivity@528486ecMyButton|onTouchEvent|return:true|event:UP ``` 可见onTouchEvent方法是在dispatchTouchEvent方法之前执行的 可是View的事件传递机制不是dispatchTouchEvent方法先执行用来传递消息的吗? 请问这是怎么回事儿? 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐

分享到微信朋友圈

×

扫一扫,手机浏览