欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android知识点总结(一)Android事件分发机制

程序员文章站 2022-05-14 15:26:15
...

相关文章:

Android 知识点总结(目录) https://blog.csdn.net/a136447572/article/details/81027701

Touch事件的分发

一 分发顺序

Activity —–> ViewGroup —–> View

Activity 时通常是调用他本身内部的ViewGroup的Touch事件,所以可以当做ViewGroup理解

ViewGroup的相关事件有三个:

onInterceptTouchEvent // 判断当前ViewGroup 是否拦截事件
dispatchTouchEvent // 处理事件的分发
onTouchEvent // 处理事件

View的相关事件只有两个:

dispatchTouchEvent
onTouchEvent

引用自 Android:30分钟弄明白Touch事件分发机制
Android知识点总结(一)Android事件分发机制

当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件

下面用实例 进行测试

Android知识点总结(一)Android事件分发机制

以点击 view1 时 做事件分发测试 其余代码在文章底部

dispatchTouchEvent 的事件分发
当ViewGroup 的dispatchTouchEvent 方法 返回 true 时 事件会分发给当前ViewGroup 并由他的 dispatchTouchEvent 方法消费,同时停止向下传
Android知识点总结(一)Android事件分发机制

点击结果
Android知识点总结(一)Android事件分发机制

当ViewGroup 的dispatchTouchEvent 方法 返回 false 时 事件分发为两种情况
1.如果View 获取的事件直接来自Activity,这会返回activity的onTouchEvent进行消费
2.如果View获取的事件来自外层父控件 这回将时间返回给父View的onTouchEvent进行消费

Android知识点总结(一)Android事件分发机制

因为这个例子 FlowView 是父布局 所以事件由activity的onTouchEvent进行处理
Android知识点总结(一)Android事件分发机制

当 ViewGroup 的dispatchTouchEvent 方法 返回 super.dispatchTouchEvent(ev) 事件会分发给当前的View的onInterceptTouchEvent方法 如果 onInterceptTouchEvent不处理 这正常流程 传到最终 viewzi1 处理
Android知识点总结(一)Android事件分发机制

点击view1 结果

Android知识点总结(一)Android事件分发机制

这里 ViewGroup dispatchTouchEvent 重复执行 可以通过第一幅图看出 dispatchTouchEvent 会循环所有子View 直到 有地方拦截处理

这里不是源码 安自己理解写的 super.dispatchTouchEvent(ev) 中大体会这样处理
Android知识点总结(一)Android事件分发机制

onInterceptTouchEvent

当 ViewGroup 的onInterceptTouchEvent方法 返回 true 表示 对事件进行拦截 并将拦截的事件交给当前ViewGroup 的onTouchEvent 进行处理

Android知识点总结(一)Android事件分发机制

点击结果
Android知识点总结(一)Android事件分发机制

这里是 因为ViewGroup的 onTouchEvent 没有处理 所有返回了 Activity 的onTouchEvent 进行处理

当 ViewGroup 的onInterceptTouchEvent方法 返回 false表示 将事件方行 传递到子View上,在传递到子View的dispatchTouchEvent 再进行事件的分发
当 ViewGroup 的onInterceptTouchEvent方法 返回 super.onInterceptTouchEvent(ev) 时 与false 处理相同 表示不拦截
Android知识点总结(一)Android事件分发机制

Android知识点总结(一)Android事件分发机制

返回结果

Android知识点总结(一)Android事件分发机制

onTouchEvent

当 ViewGroup 的onTouchEvent 方法 返回true 事件拦截 自己处理
当 View 的onTouchEvent 方法 返回true 事件拦截 自己处理
onTouchEvent 稍有不同 当同事为true 时 以点击位置为准 点击谁谁处理

    // ViewZi 中的 onTouchEvent代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - onTouchEvent");
        return true;
    }

    // FlowView中的 onTouchEvent代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewGroup - onTouchEvent");
        return true;
    }

点击结果 上面红框是点击view1 下面篮框是点击了FlowView
Android知识点总结(一)Android事件分发机制

false 放弃处理 ,返回到上级,并由上层View的onTouchEvent 来接受
super.onTouchEvent(ev)默认处理事件的逻辑和返回false的一样
View 与 ViewGroup 相同 如果都不拦截,不处理 会返回Activity的 onTouchEvent

    // ViewZi 中的 onTouchEvent代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - onTouchEvent");
        return false;
    }
       // FlowView中的 onTouchEvent代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - onTouchEvent");
        return false;
    }

Android知识点总结(一)Android事件分发机制

如果 View 不拦截 ViewGroup 拦截的时候

    // ViewZi 中的 onTouchEvent代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - onTouchEvent");
        return false;
    }
       // FlowView中的 onTouchEvent代码
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - onTouchEvent");
        return true;
    }

返回结果

Android知识点总结(一)Android事件分发机制

如果 View 不拦截 ViewGroup 拦截 时 ViewGroup 的 onTouchEvent 事件处理

调用顺序
在有注册onTouchListener的情况下会先调用
onTouch
true 这事件不在向下层传递
false 像onTouchEvent传递
然后 onTouchEvent
然后onClick

Activity 代码

public class Demo5 extends AppCompatActivity {

    private ViewZi viewzi1 ,viewzi2 ,viewzi3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo5);

        viewzi1 = (ViewZi) findViewById(R.id.viewzi1);
        viewzi2 = (ViewZi) findViewById(R.id.viewzi2);
        viewzi3 = (ViewZi) findViewById(R.id.viewzi3);
        viewzi1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i("info","viewzi1--onClick");
            }
        });
        viewzi2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i("info","viewzi2--onClick");
            }
        });
        viewzi3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.i("info","viewzi3--onClick");
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","Activity - onTouchEvent");
        return super.onTouchEvent(event);
    }
}

布局代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.administrator.alldemokeshan.demo.Demo5">


    <com.example.administrator.alldemokeshan.Myview.FlowView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#82e2e1">


        <com.example.administrator.alldemokeshan.Myview.ViewZi
            android:id="@+id/viewzi1"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="#f11"
            android:gravity="center"
            android:text="view1"
            android:textColor="#333"
            android:textSize="20sp" />

        <com.example.administrator.alldemokeshan.Myview.ViewZi
            android:id="@+id/viewzi2"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="#11f"
            android:gravity="center"
            android:text="view2"
            android:textColor="#333"
            android:textSize="20sp" />

        <com.example.administrator.alldemokeshan.Myview.ViewZi
            android:id="@+id/viewzi3"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="#1f1"
            android:gravity="center"
            android:text="view3"
            android:textColor="#333"
            android:textSize="20sp" />

    </com.example.administrator.alldemokeshan.Myview.FlowView>

</RelativeLayout>

ViewZi 代码

package com.example.administrator.alldemokeshan.Myview;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

/**
 * Created by Administrator on 2018/7/10.
 */

public class ViewZi extends AppCompatTextView {

    public ViewZi(Context context) {
        super(context);
    }

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

    public ViewZi(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewZi - onTouchEvent");
        return super.onTouchEvent(event);
    }
}


FlowView 代码

package com.example.administrator.alldemokeshan.Myview;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

/**
 * Created by Administrator on 2018/7/6.
 */

public class FlowView extends ViewGroup {


    public FlowView(Context context) {
        super(context);
    }

    public FlowView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FlowView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(),attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i("info","ViewGroup - dispatchTouchEvent");
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i("info","ViewGroup - onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("info","ViewGroup - onTouchEvent");
        return super.onTouchEvent(event);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);


        int widthMeasure = MeasureSpec.getSize(widthMeasureSpec);
        int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec);

        int heightMeasure = MeasureSpec.getSize(heightMeasureSpec);
        int heightMeasureMode = MeasureSpec.getMode(heightMeasureSpec);


        int lineWidth = 0 ;
        int lineHeight = 0;
        int width = 0 ;
        int height = 0 ;

        int count = getChildCount();

        measureChildren(widthMeasureSpec,heightMeasureSpec);

        for (int i = 0; i < count ; i++) {

            View child = getChildAt(i);

            MarginLayoutParams  lp = (MarginLayoutParams ) child.getLayoutParams();
            int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
            int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;

            if ( (childWidth + lineWidth) > widthMeasure ){
                width = Math.max(lineWidth,childWidth);
                height += lineHeight;
                lineHeight = childHeight;
                lineWidth = childWidth;
            }else{
                lineWidth +=childWidth ;
                lineHeight = Math.max(lineHeight,childHeight);
            }
            if (i == count -1 ){
                height += lineHeight ;
                width = Math.max(width,lineWidth);
            }
        }

        setMeasuredDimension((widthMeasureMode == MeasureSpec.EXACTLY)
                ?widthMeasure:width,(heightMeasureMode==MeasureSpec.EXACTLY)
                ?heightMeasure:height);


    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {

        int count  = getChildCount();

        int lineWidth = 0 ;
        int lineHeight = 0 ;
        int top = 0 ;
        int left = 0 ;


        for (int j = 0; j < count; j++) {

            View child = getChildAt(j);

            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

            int childWidth = child.getMeasuredWidth()+lp.leftMargin +lp.rightMargin;
            int childHeight = child.getMeasuredHeight()+lp.topMargin +lp.bottomMargin;
            if ((childWidth +lineWidth)>getMeasuredHeight() ){
                top += lineHeight;
                left = 0 ;
                lineHeight = childHeight;
                lineWidth = childWidth;
            }else{
                lineHeight = Math.max(lineHeight,childHeight);
                lineWidth += childWidth;
            }

            int lc = left + lp.leftMargin;
            int tc = top+lp.topMargin;
            int rc = lc + child.getMeasuredWidth();
            int bc = tc + child.getMeasuredHeight();

            child.layout(lc,tc,rc,bc);
            left += childHeight;

        }



    }
}