模拟Android的事件传递机制

本文参考源码,简单模拟了View事件的传递过程。

package com.log.touchevent;

import com.log.touchevent.listener.MotionEvent;
import com.log.touchevent.listener.OnClickListener;
import com.log.touchevent.listener.OnTouchListener;

public class View {

    protected String name;
    protected int left;
    protected int top;
    protected int right;
    protected int bottom;

    protected OnClickListener onClickListener;
    protected OnTouchListener onTouchListener;

    public View() {
    }

    public View(String name, int left, int top, int right, int bottom) {
        this.name = name;
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

    public boolean dispatchTouchEvent(MotionEvent event) {
        System.out.println(name + "======>dispatchTouchEvent");
        boolean result = false;
        if (onTouchListener != null && onTouchListener.onTouch(this, event)) {
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }

        return result;
    }

    public boolean onTouchEvent(MotionEvent event) {
        System.out.println(name + "======>onTouchEvent");
        if (onClickListener != null) {
            onClickListener.onClick(this);
            return true;
        }

        return false;
    }


    // 点击坐标是否在view身上
    protected boolean isContainer(int x, int y) {
        if (x >= left && x <= right && y <= bottom && y >= top) {
            return true;
        }
        return false;
    }

    public void setOnClickListener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    public void setOnTouchListener(OnTouchListener onTouchListener) {
        this.onTouchListener = onTouchListener;
    }
}
package com.log.touchevent;

import com.log.touchevent.listener.MotionEvent;

import java.util.ArrayList;
import java.util.List;

public class ViewGroup extends View {

    private List<View> childList = new ArrayList<>();
    private View[] mChildren = new View[0];
    private TouchTarget mFirstTouchTarget;

    public ViewGroup(String name, int left, int top, int right, int bottom) {
        super(name, left, top, right, bottom);
    }

    public void addView(View v) {
        childList.add(v);
        mChildren = childList.toArray(new View[childList.size()]);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        boolean intercept = onInterceptTouchEvent(event);

        boolean handled = false;
        TouchTarget newTouchTarget = null;
        int aciton = event.getAction();
        // 如果不是取消以及本ViewGroup没来拦截事件
        if (aciton != MotionEvent.ACTION_CANCEL && !intercept) {
            if (aciton == MotionEvent.ACTION_DOWN) {
                final View[] children = this.mChildren;
                // 扫描所有个子View,找到哪个子View应该会被分发到事件(检查子View是否在点击区域内)。
                // 从后往前遍历,是因为最上层的子View处理Touch事件的概率更大些,
                // 还有就是如果事件在上层被消费了,那么就不会继续把事件传递回父容器
                for (int i = children.length - 1; i >= 0; i--) {
                    View child = children[i];
                    // 如果该View不在点击的区域,那么说明它不能处理点击事件
                    if (!child.isContainer(event.getX(), event.getY())) {
                        continue;
                    }

                    // 把事件分发给能够接收事件的子View
                    if (dispatchTransformedTouchEvent(event, child)) {
                        // 如果事件被该子View消费了
                        handled = true;
                        newTouchTarget = addTouchTarget(child);
                        break;
                    }
                }
            }
        }

        // 如果没有子View处理事件,那么就调用自己的处理方法
        if (mFirstTouchTarget == null) {
            handled = dispatchTransformedTouchEvent(event, null);
        }

        return handled;
    }

    private TouchTarget addTouchTarget(View child) {
        TouchTarget touchTarget = TouchTarget.obtain(child);
        touchTarget.next = mFirstTouchTarget;
        mFirstTouchTarget = touchTarget;
        return touchTarget;
    }

    private boolean dispatchTransformedTouchEvent(MotionEvent event, View child) {
        System.out.println(name + "======>dispatchTransformedTouchEvent");
        boolean handled;
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        return handled;
    }

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        System.out.println(name + "======>onInterceptTouchEvent");
        return false;
    }


    public static final class TouchTarget {
        // 最大的链表数量(缓存数量)
        private static final int MAX_RECYCLED = 32;
        private static final Object sRecycleLock = new Object[0];
        public static TouchTarget sRecycleBin; // 链表表头
        public static int sRecycledCount;  // 当前链表元素个数

        public View child;
        public TouchTarget next; // 指向下一个元素

        private TouchTarget() {
        }

        public static TouchTarget obtain(View child) {
            final TouchTarget target;
            synchronized (sRecycleLock) {
                if (sRecycleBin == null) {
                    target = new TouchTarget();
                } else {
                    target = sRecycleBin;
                    sRecycleBin = target.next;
                    sRecycledCount--;
                    target.next = null;
                }
                target.child = child;
            }
            return target;
        }

        // 回收当前TouchTarget,重新放入链表
        public void recycle() {
            if(child == null){
                throw new IllegalStateException("already recycled once");
            }

            if (sRecycledCount < MAX_RECYCLED) {
                next = sRecycleBin;
                sRecycleBin = this;
                sRecycledCount++;
            } else {
                next = null;
            }
            child = null;
        }

    }

}
package com.log.touchevent.listener;

public class MotionEvent {

    public static final int ACTION_DOWN             = 0;
    public static final int ACTION_UP               = 1;
    public static final int ACTION_MOVE             = 2;
    public static final int ACTION_CANCEL           = 3;

    private int mAction;
    private int x;
    private int y;

    public MotionEvent(int action, int x, int y) {
        this.mAction = action;
        this.x = x;
        this.y = y;
    }

    public int getAction() {
        return mAction;
    }

    public void setAction(int mAction) {
        this.mAction = mAction;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}
package com.log.touchevent.listener;

import com.log.touchevent.View;

public interface OnClickListener {

    void onClick(View v);

}
package com.log.touchevent.listener;

import com.log.touchevent.View;

public interface OnTouchListener {

    boolean onTouch(View v, MotionEvent event);

}

Activity点击事件触发顺序:

Activity.dispatchTouchEvent()  ==>  PhoneWindow.superDispatchTouchEvent()  ==>  mDecorView.dispatchTouchEvent()  ==>  containerView.dispatchTouchEvent()  ==>  Activity.setContentView()设置的View.dispatchTouchEvent()

 

我们这里只是简单模拟下顶层触发传递点击事件:

package com.log.touchevent;

import com.log.touchevent.listener.MotionEvent;
import com.log.touchevent.listener.OnClickListener;
import com.log.touchevent.listener.OnTouchListener;

public class Activity {

    public static void main(String[] args) {
        MotionEvent event = new MotionEvent(MotionEvent.ACTION_DOWN, 200, 200);

        ViewGroup viewGroup = new ViewGroup("顶级容器", 0, 0, 1000, 1000);
        ViewGroup viewGroup2 = new ViewGroup("二级容器", 100, 100, 600, 600);
        View view = new View("子View", 200, 200, 500, 500);
        viewGroup.addView(viewGroup2);
        viewGroup2.addView(view);

        view.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                System.out.println(v.name + "=====***>OnClickListener");
            }
        });

        view.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                System.out.println(v.name + "=====***>setOnTouchListener");
                return true;
            }
        });

        // 模拟顶层触发传递点击事件
        viewGroup.dispatchTouchEvent(event);
    }
}

打印结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值