浅谈Android自定义View事件传递机制

来源:互联网 发布:淘宝基础店铺全屏代码 编辑:程序博客网 时间:2024/06/05 07:32

本人作为一个技术小菜鸟,第一次写博客,有写的不好的地方希望大家多多指教,本文章一来给大家做个参考,二来说不定哪天忘记了回来看看也是好的。哈哈,废话不多说,直接进入主题!

事件传递所涉及到的方法

  1. dispatchTouchEvent() 分发方法
  2. onInterceptTouchEvent() 拦截方法
  3. onTouchEvent() 消费方法

首先简单介绍下这三个方法 :
dispatchTouchEvent() 这个方法代表事件分发,作用是改变事件传递的方向。
onInterceptTouchEvent()对事件进行拦截,作用拦截事件交给当前处理或传递到下一级。
onTouchEvent()主要的作用是消费MotionEvent事件。


方法 Activity ViewGroup View dispatchTouchEvent() 有 有 有 onInterceptTouchEvent() 无 有 无 onTouchEvent() 有 有 有

Activity和View中是没有事件拦截方法,只有分发和消费方法,具体Why这个要问谷歌了! 哈哈 这个不做解释 ..
首先我们看一下三个方法 返回的是super.XX() 还有两种返回值true/false .

  • 分发
    返回true时代表事件不分发,事件到此结束。
    返回false时,会把事件传递给上一级的onTouchEvent()处理。
    返回super.dispatchTouchEvent(ev)时分两种情况,一种是ViewGroup 会把事件传递给当前的onInterceptTouchEvent()方法处理,另一种是View 直接交给当前的onTouchEvent()事件处理。
  • 拦截
    返回true时,会把事件传递给当前的onTouchEvent()处理。
    返回false/super.onInterceptTouchEvent(ev) 这个时候代表事件不拦截,事件会传向下一级的dispatchTouchEvent()。
  • 消费
    返回true时代表此次是事件已消费,并且以后的事件如果中途没被拦截的话会一直走到当前方向
    返回false/super.onTouchEvent(ev) 这个时候代表事件不拦截,事件会传向上一级的onTouchEvent()。
    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        return super.dispatchTouchEvent(ev);//true/false    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return super.onInterceptTouchEvent(ev);//true/false    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return super.onTouchEvent(event);//true/false    }

动手做个demo

a

首先准备一个Activity
创建三个自定义View(其实两个就可以) FatherA,FatherB,ChildC

CustomViewActivity

public class CustomViewActivity extends Activity {    @Override    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {        super.onCreate(savedInstanceState, persistentState);        setContentView(R.layout.activity_custom);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return super.onTouchEvent(event);    }}

FatherA

public class FatherA extends LinearLayout {    public FatherA(Context context) {        super(context);    }    public FatherA(Context context, AttributeSet attrs) {        super(context, attrs);    }    public FatherA(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        return super.dispatchTouchEvent(ev);//true    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return super.onInterceptTouchEvent(ev);//true    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return super.onTouchEvent(event);    }}

FatherB和FatherA一样 此处省略
ChildC

public class ChildC extends View {    public ChildC(Context context) {        super(context);    }    public ChildC(Context context, AttributeSet attrs) {        super(context, attrs);    }    public ChildC(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return super.onTouchEvent(event);    }}

最后贴一个主布局的xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:gravity="center"              android:orientation="vertical">    <com.sincerly.android.activity.customView.view.FatherA        android:layout_width="300dp"        android:layout_height="300dp"        android:gravity="center"        android:background="@color/blue">        <com.sincerly.android.activity.customView.view.FatherB            android:layout_width="200dp"            android:background="@color/green"            android:gravity="center"            android:layout_height="200dp">            <com.sincerly.android.activity.customView.view.ChildC                android:layout_width="100dp"                android:layout_height="100dp"                android:background="@color/white"/>        </com.sincerly.android.activity.customView.view.FatherB>    </com.sincerly.android.activity.customView.view.FatherA></LinearLayout>

测试一,点击FatherB(绿色区域),FatherA的分发方法返回true,其他都为默认


E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()

可以根据Log看出FatherB这个View根本没获取到事件 因为FatherA的分发方法返回了true,就好像给事件加了个标示 如果到FatherA的分发方法事件就无效了。

下面我们把FatherA分发事件改为false

E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()E/tag: CustomViewActivity onTouchEvent()E/tag: CustomViewActivity DispatchTouchEvent()E/tag: CustomViewActivity onTouchEvent()

分发方法返回false把事件进行向上传递,给上级的onTouchEvent()处理。

下面我们把FatherA分发事件改为super.dispatchTouchEvent(ev)

E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()E/tag: FatherA onInterceptTouchEvent()E/tag: FatherB DispatchTouchEvent()E/tag: FatherB onInterceptTouchEvent()E/tag: FatherB onTouchEvent()E/tag: FatherA onTouchEvent()E/tag: CustomViewActivity onTouchEvent()E/tag: CustomViewActivity DispatchTouchEvent()E/tag: CustomViewActivity onTouchEvent()

可见分发事件只有返回super.dispatchTouchEvent(ev)才会进行分发。
这里解释一下,点击的是绿色 也就是FatherB,处理流程,FatherA分发返回super.d() 进行传递至FatherA的拦截方法 拦截方法返回的super.onInterceptTouchEvent() 不进行拦截 并将事件传递给FatherB的分发事件 然后同理 FatherB 进行分发 ,不拦截(因为我们点击的是绿色区域 并没有点击到ChildC(内层白色区域) ) 所以不拦截之后, 交给当前(FatherB)的onTouchEvent()处理 因为返回的false,所以事件向上传递给FatherA的onTouchEvent() 因为返回的false 所以向上传递给Activity的onTouchEvent()处理.


方法 Activity FatherA FatherB ChildC dispatchTouchEvent() super.d() super.d() super.d() super.d() onInterceptTouchEvent() 无 super.onI()/false super.onI()/false 无 onTouchEvent() super.on() super.on() super.on() super.on()

拦截方法和消费方法 返回super和false的区别 有兴趣可以自行测试,因为效果一样,我这里不做过多解释了。

下面我们测试一下FatherA的拦截方法,返回true

E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()E/tag: FatherA onInterceptTouchEvent()E/tag: FatherA onTouchEvent()E/tag: CustomViewActivity onTouchEvent()E/tag: CustomViewActivity DispatchTouchEvent()E/tag: CustomViewActivity onTouchEvent()

可见虽然点击了FatherB ,但是在A处 事件已经拦截。
下图为当前事件的返回值,A拦截返回true,然后走A的onTouchEvent() 返回false,事件并没有被消费, 所以返回Activity的onTouchEvent() 然后整个事件处理完毕。

记得之前有一个例子不错 我引用一下。
校长 教师 班长 小明
校长有一张表不想写,就把这张表下发给教师 说:”你帮我填一下”,然后这个老师也比较懒,然后给班长, 班长有急事然后交给小明说 “帮忙填一下”,小明不会写 ,然后给班长说:”我不会写”, 班长给老师说“我有事 写不了”,然后老师给校长说我不想填(哈哈,找抽),最终表还是到了校长的手中。

如果教师一开始就说“没问题”, 这就是拦截方法 为true 并且如果表填完了,onTouchEvent()方法就为true 也就是说这个表已经填好了, 而且下次再来一张表的时候 还是到这个地方结束。


方法 Activity FatherA FatherB ChildC dispatchTouchEvent() super.d() super.d() super.d() super.d() onInterceptTouchEvent() 无 true super.onI()/false 无 onTouchEvent() super.on() super.on() super.on() super.on()

再做最后一个测试 就是FatherB的拦截返回true,消费返回true
其他全为super.();

E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()E/tag: FatherA onInterceptTouchEvent()E/tag: FatherB DispatchTouchEvent()E/tag: FatherB onInterceptTouchEvent()E/tag: FatherB onTouchEvent()E/tag: CustomViewActivity DispatchTouchEvent()E/tag: FatherA DispatchTouchEvent()E/tag: FatherA onInterceptTouchEvent()E/tag: FatherB DispatchTouchEvent()E/tag: FatherB onTouchEvent()

可以看出流程,DOWN事件 进入Activity分发,A分发,A不拦截,到B的 分发,拦截,消费。


由于第一次写博文,经验非常不足,有的解释的没到点上 请谅解,希望对大家有点帮助,缺个流程图,会直观点 ,正在积极制作中。 有没有会做动态图的 那个是什么软件做的? 就是效果演示的那个

0 0