事件冲突原因与解决方案

目录

MontionEvent

View继承关系

分发,处理流程

onTouch与onClick之间会产生事件冲突吗?

事件在控件中是如何传递的?

事件冲突产生的根本原因,如何解决事件冲突


MontionEvent

View继承关系

分发,处理流程

事件分发 : ViewGroup.java  -> dispatchTouchEvent

事件处理 : View.java -> dispatchTouchEvent

onTouch与onClick之间会产生事件冲突吗?

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Log.e(TAG,"onClick");
    }
});

btn.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Log.e(TAG,"onTouch");
        return false;
    }
});

若onTouch() 方法return false,则事件由onClick消费,否则由onTouch消费,onClick不会有反应。

以下为源码,解释原因

View -> dispatchTouchEvent -> performOnTouchCallback

事件在控件中是如何传递的?

Down事件

分发流程

若进行拦截  ViewGroup -> dispatchTouchEvent 

分发或者处理(拦截相当于你是最后一个,事件到底处不处理)

dispatchTransformedTouchEvent//是否处理
chlid为空说明说明给自己处理

handled为true说明View处理了事件,为false则说明View未处理事件

若不进行拦截(分发)

final ArrayList<View> preorderedList = buildTouchDispatchChildList();

把子view进行排序

将View按顺序正着插入,再倒序取出

调用dispatchTransformedTouchEvent方法,传入chlid,调用孩子的dispatchTouchEvent(分发流程)

若子view全部不处理,则流程和拦截一样(流到前面拦截的方法)

进入if则进行三个条件的处理

newTouchTarget = mFirstTouchTarget

target.next = null
alreadyDispatchedToNewTouchTarget = true;

ViewGroup -> dispatchTouchEvent 

Move事件  ->  不会再分发事件

move拦截 --- 处理事件冲突 --- 只能在move的时候处理

事件冲突产生的根本原因,如何解决事件冲突

内部拦截

子view不可以抢父容器的事件,父容器可以抢子view的事件。

内部拦截:getParent().requestDisallowInterceptTouchEvent(false);

子view一旦拿到事件,事件再由谁处理,就是子view说的算。

内部拦截:getParent().requestDisallowInterceptTouchEvent(true);

若为内部拦截,父容器必须不拦截Down事件

public class MyListView extends ListView {

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

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

//     内部拦截法:子view处理事件冲突
    private int mLastX, mLastY;

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;

            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }
}

getParent().requestDisallowInterceptTouchEvent(true)意味着事件完全由ListVew掌控。

外部拦截

public class BadViewPager extends ViewPager {

    private int mLastX, mLastY;

    public BadViewPager(@NonNull Context context) {
        super(context);
    }

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

    // 外部拦截法:父容器处理冲突
    // 我想要把事件分发给谁就分发给谁
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
//        if (event.getAction() == MotionEvent.ACTION_DOWN){
//            super.onInterceptTouchEvent(event);
//            return false;
//        }
//        return true;

        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                mLastX = (int) event.getX();
                mLastY = (int) event.getY();
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    return true;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }

        return super.onInterceptTouchEvent(event);

    }
}

以上,ViewPager左右滑动时ListView不能上下滑动。ListView上下滑动时,ViewPager可以左右滑动。原因是

当用户明显想要水平滑动时,拦截事件让 ViewPager 处理

当用户明显想要垂直滑动时,不拦截事件让 ListView 处理

ViewPager拦截之后子View(ListView)便拿不到后续事件了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值