Android Event事件分发

文章介绍了如何利用ViewParent的requestDisallowInterceptTouchEvent方法和观察者模式解决Android中子View与父View的滑动冲突问题。通过创建一个事件拦截管理器(EventInterceptManager),注册回调接口并在子View的dispatchTouchEvent中通知父布局允许或禁止拦截触摸事件,从而实现滑动优先级控制。
摘要由CSDN通过智能技术生成
前言

Android Event事件分发:子View优先获取滑动事件

注:本文章不讲事件分发理论,直接给实现先方案。

最终效果

滑动冲突

如视频中,我的条形统计图可以左右滑动,统计图和左右滑动的ViewPage冲突,也和上下滚动的ScrollView冲突。我们的需求是:子View优先获取滑动事件

解决思路

在这里插入图片描述

  1. ViewParent接口中有个方法叫做requestDisallowInterceptTouchEvent,即请求ViewGroup不允许侦听触摸事件。解决思路:我们判断,当子View获取到ACTION_DOWN时,我们通知父布局设置不允许侦听触摸事件,子View获取到ACTION_UP时,我们通知父布局设置允许侦听触摸事件,即可轻松解决滑动冲突问题。

  2. 由于考虑到多个滑动冲突同时存在的情况,因此我把通知写成“观察者模式”,无论你的APP中有多少个冲突,都可以统一用这个工具解决。

代码实现:观察模式的通知类
package com.hmongsoft.merchant.util.eventsUtil;

import java.util.HashMap;

/**
 * 事件拦截管理器(观察者模式)
 */
public class EventInterceptManager {

    //单例
    private static class Holder{
        private static final EventInterceptManager eventInterceptManager=new EventInterceptManager();
    }
    public static EventInterceptManager INSTANCE(){
        return Holder.eventInterceptManager;
    }
    private EventInterceptManager() {}

    //用于存储ViewAction的Hash
    private HashMap<ViewActionTag,CallBack> viewHash=new HashMap<>();

    /**
     * 第一步: 父布局监听子View事件
     * @param tag  View回调接口的标签
     * @param callback 回调的接口实现类
     */
    public void register(ViewActionTag tag, CallBack callback){
        viewHash.put(tag,callback);
    }

    /**
     * 第二步:子View请求父布局设置
     * @param tag View回调接口的标签
     * @param isReleaseEvent 是否需要
     */
    public void request(ViewActionTag tag, Boolean isReleaseEvent){
        CallBack actionCallback=viewHash.get(tag);
        if (actionCallback!=null)actionCallback.todo(tag,isReleaseEvent);
    }

    /**
     * 回调接口
     */
    public interface CallBack{
        void todo(Object tag,Object data);
    }

}

代码实现:枚举标签

注:同一个APP中,解决一处滑动冲突,就手动在这里增加一个枚举标签即可

package com.hmongsoft.merchant.util.eventsUtil;

public enum ViewActionTag {
    INDEX_VIEW_PAGE,//对应:案例2
    ACCOUNT_SCROLL_VIEW//对应:案例1
}
使用方法——第一步: 父布局监听子View通知

案例1:

ScrollView my_account_scroll_view=(ScrollView) view.findViewById(R.id.my_account_scroll_view);
EventInterceptManager.INSTANCE().register(ViewActionTag.ACCOUNT_SCROLL_VIEW, new EventInterceptManager.CallBack() {
    @Override
    public void todo(Object tag, Object data) {
        if (tag ==ViewActionTag.ACCOUNT_SCROLL_VIEW){
            //请求ScrollView允许或者不允许侦听触摸事件
            my_account_scroll_view.requestDisallowInterceptTouchEvent((Boolean) data);
        }
    }
});

案例2:

ViewPager viewPager = findViewById(R.id.viewPager);
EventInterceptManager.INSTANCE().register(ViewActionTag.INDEX_VIEW_PAGE, new EventInterceptManager.CallBack() {
    @Override
    public void todo(Object tag, Object data) {
        //请求ScrollView允许或者不允许侦听触摸事件
        if (tag ==ViewActionTag.INDEX_VIEW_PAGE)viewPager.requestDisallowInterceptTouchEvent((Boolean) data);
    }
});
使用方法——第二步:子View请求父布局设置
//自定的可以左右滚动的View(即:子View)
public class ScrollBarChart extends View {

    public ScrollBarChart(Context context) {
        this(context,null);
    }

    public ScrollBarChart(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                //通知父布局:要求父布局不允许拦截事件
                EventInterceptManager.INSTANCE().request(ViewActionTag.ACCOUNT_SCROLL_VIEW,true);//对应:案例1
                EventInterceptManager.INSTANCE().request(ViewActionTag.INDEX_VIEW_PAGE,true);//对应:案例2
                break;
            case MotionEvent.ACTION_UP:
                //通知父布局:恢复默认设置
                EventInterceptManager.INSTANCE().request(ViewActionTag.ACCOUNT_SCROLL_VIEW,false);//对应:案例1
                EventInterceptManager.INSTANCE().request(ViewActionTag.INDEX_VIEW_PAGE,false);//对应:案例2
               
                break;
        }
        return super.dispatchTouchEvent(event);
    }

}

在子View的dispatchTouchEvent()方法中,通知父布局设置。

案例到此结束!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝命三郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值