Android进阶之自定义控件三

    xiaoxiao2021-03-25  118

    事件分发拦截机制

    关于事件分发与拦截机制个人觉得《Android群英传》一书讲的比较通俗易懂,大家也可以去参考这本书,下面就讲解一下个人的拙见。

    事件分发拦截在我平时的工作中非常长见,我也能通过自己解决一些这方面的bug,但是一直没有理解透彻,直到看了《Android群英传》里的一个例子我才恍然大悟,下面分享给大家:

    假设你所在的公司,有一个总经理,级别最高;他下面有一个部长,级别次之;最低层,就是干活的你,没有级别。现在懂事会交给你总经理一项任务,总经理将这项任务布置给了部长,部长又把任务安排给了你。而你好不容易干完活了,你就把任务交给部长,部长觉得任务完成得不错,于是就签了他的名字交给我总经理,总经理看了也觉得不错,就也签了名字交给董事会。这样一个任务就顺利完成了。

    这个例子就是事件拦截机制的原理了,非常通俗易懂,下通过一个示例来加深印象: 一个总经理——MyViewGroupA,最外层的ViewGroup。 一个部长——MyViewGroupB,中间的ViewGroup. 一个干活的你——MyView,在最底层。

    代码如下:

    public class MyViewGroupA extends ViewGroup { public MyViewGroupA(Context context) { super(context); } public MyViewGroupA(Context context, AttributeSet attrs) { super(context, attrs); } public MyViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; ++i){ View childView = getChildAt(i); measureChild(childView, widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++){ View child = getChildAt(i); if (child.getVisibility() != View.GONE){ child.layout(l + 50, t + 50, r - 50, b - 50); } } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("zb", "MyViewGroupA dispatchTouchEvent" + ev.getAction()); return super.dispatchTouchEvent(ev); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLACK); canvas.drawText("MyViewGroupA", 0, 20, mPaint); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e("zb", "MyViewGroupA onInterceptTouchEvent" + ev.getAction()); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.e("zb", "MyViewGroupA onTouchEvent" + event.getAction()); return super.onTouchEvent(event); } } public class MyViewGroupB extends ViewGroup { public MyViewGroupB(Context context) { super(context); } public MyViewGroupB(Context context, AttributeSet attrs) { super(context, attrs); } public MyViewGroupB(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; ++i){ View childView = getChildAt(i); measureChild(childView, widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++){ View child = getChildAt(i); if (child.getVisibility() != View.GONE){ child.layout(l+ 50, t+ 50, r-200, b- 200); } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLACK); canvas.drawText("MyViewGroupB", 0, 20, mPaint); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("zb", "MyViewGroupB dispatchTouchEvent" + ev.getAction()); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e("zb", "MyViewGroupB onInterceptTouchEvent" + ev.getAction()); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { Log.e("zb", "MyViewGroupB onTouchEvent" + ev.getAction()); return super.onTouchEvent(ev); } } public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getMeasureSize(widthMeasureSpec), getMeasureSize(heightMeasureSpec)); } public static int getMeasureSize(int measureSpec) { int size = 200; int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: result = Math.min(size, specSize); break; case MeasureSpec.EXACTLY: result = specSize; break; } return result; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.BLACK); canvas.drawText("MyView", 0, 20, mPaint); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("zb", "MyView dispatchTouchEvent" + ev.getAction()); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.e("zb", "MyView onTouchEvent" + event.getAction()); return super.onTouchEvent(event); } } MainActivity的布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.diy.mylearnplan1.MainActivity"> <com.example.diy.mylearnplan1.customView android:id="@+id/cv_customView" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> <com.example.diy.mylearnplan1.MyViewGroupA android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorAccent"> <com.example.diy.mylearnplan1.MyViewGroupB android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@color/colorPrimary"> <com.example.diy.mylearnplan1.MyView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorPrimaryDark" > </com.example.diy.mylearnplan1.MyView> </com.example.diy.mylearnplan1.MyViewGroupB> </com.example.diy.mylearnplan1.MyViewGroupA> </LinearLayout>

    运行后可以看到如下Log:

    从Log可以看出,事件的传递顺序是: 总经理(MyViewGroupA)——部长(MyViewGroupB)——你(MyView)。事件传递的时候,先执行dispatchTouchEvent()方法,再执行onInterceptTouchEvent()方法。

    事件的处理顺序是: 你(MyView)——部长(MyViewGroupB)——总经理(MyViewGroupA)。事件处理都是执行onTouchEven()方法。

    事件传递的返回值非常容易理解:True,拦截,不继续;False,不拦截,继续流程。 事件处理的返回值也类似:True,处理了,不用审核了;False,给上级处理。 初始化情况下,返回值都是false。

    相信到这里大家已经充分理解了时间分发、拦截、处理的整个流程了。

    下面我们稍微改动一下,假设总经理(MyViewGroupA)发现这个任务太简单了,觉得自己完成就可以了,完全没必要再找下属。因此事件就被总经理(MyViewGroupA)使用onInterceptTouchEvent()方法把事件给拦截了,即让MyViewGroupA的onInterceptTouchEvent()方法返回True,我们再来看一下Log。

    跟我们设想的一样,总经理(MyViewGroupA)把所有的事情都干了,没后面人的事了。同理,我们让部长(MyViewGroupB)也当一次好人,即让部长(MyViewGroupB)的onInterceptTouchEvent()方法返回True,把时间拦截下来,Log就会是一下这样。

    不出我们意外的你(MyView)不用干任何活了。现在对时间分发、拦截大家比较清楚了,下面我们看看事件处理。最开始的时候讲了,当你处理完任务后会向上级报告,需要上级的确认,所以你的时间处理返回False。那么你突然有一天受不了老板的压迫了,罢工不敢了,那么你的任务就没人做了,也就不用报告上级了,所以就直接返回True。现在再来看看Log,如下所示。

    可以看见,事件传递跟以前一样,但是时间处理,到你(MyView)这就结束了,因为你返回True,表示不用向上级汇报了。

    同样的如果部长(MyViewGroupB)看到了你的报告,觉得太丢人,不敢给总经理看,所以偷偷地返回True,整个时间也就到此为止了,即部长将自己的onTouchEvent返回为True,Log如下所示:

    看到这里相信大家比较容易的了解事件的分发、拦截、处理的流程了。如果大家想更加深入地理解,可以去结合源码进一步的学习!

    转载请注明原文地址: https://ju.6miu.com/read-3914.html

    最新回复(0)