Android Event事件分发:子View优先获取滑动事件
注:本文章不讲事件分发理论,直接给实现先方案。
滑动冲突
如视频中,我的条形统计图可以左右滑动,统计图和左右滑动的ViewPage冲突,也和上下滚动的ScrollView冲突。我们的需求是:子View优先获取滑动事件
ViewParent接口中有个方法叫做requestDisallowInterceptTouchEvent,即请求ViewGroup不允许侦听触摸事件。解决思路:我们判断,当子View获取到ACTION_DOWN时,我们通知父布局设置不允许侦听触摸事件,子View获取到ACTION_UP时,我们通知父布局设置允许侦听触摸事件,即可轻松解决滑动冲突问题。
由于考虑到多个滑动冲突同时存在的情况,因此我把通知写成“观察者模式”,无论你的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
}
案例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)
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()方法中,通知父布局设置。