在安卓开发中,当一个clickable为true的ViewGroup控件包含了一个HorizontalScrollView的时候,点击HorizontalScrollView的位置既不能响应ViewGroup控件的点击事件,当然也不能使ViewGroup控件设置的selector背景变色,经典的用法是 Adapter View 的 item中包含一个HorizontalScrollView,item的点击就不能响应,必须点除了HorizontalScrollView的别的位置,很不友好。
因为HorizontalScrollView将事件的很多动作都拦截消耗掉了,包括但不限于:
MotionEvent.ACTION_DOWN
MotionEvent.ACTION_MOVE
MotionEvent.ACTION_UP
MotionEvent.ACTION_CANCEL
其中一般点击事件需要响应的三个动作DOWN、UP、CANCEL全被拦截掉了,如何解决呢?
遇事不决问百度,百度不决问谷歌。网上提供了一种方法,就是给包含HorizontalScrollView的父控件设置android:descendantFocusability属性,值有三个选择:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
遇到点击冲突时,给包含HorizontalScrollView的父控件设置android:descendantFocusability=“blocksDescendants”,据说可以解决冲突。但本人亲测无效,原因不明,可能是API版本或者手机问题(?)
下面我自己写了几行代码,定义了一个很简单但很有效的自定义HorizontalScrollView,完美结局了冲突,item点击事件和HorizontalScrollView本身的左右滑动还有RecyclerView的上下滑动,再外面是ViewPager的左右滑动都没有冲突,令人神清气爽
package com.yc.ui.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.HorizontalScrollView;
/**
* 兼容父容器点击事件的HorizontalScrollView
*
* created by yc on 2018-12-27
*/
public class ParentClickHorizontalScrollView extends HorizontalScrollView {
private View parentView;
public ParentClickHorizontalScrollView(Context context) {
super(context);
}
public ParentClickHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ParentClickHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public View getParentView() {
return parentView;
}
public void setParentView(View parentView) {
this.parentView = parentView;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//注意 getParent() 是 ViewParent 但不一定是 View
if (parentView == null
&& getParent() != null
&& getParent() instanceof View) {
parentView = (View) getParent();
}
if (parentView != null) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
parentView.onTouchEvent(ev); //使父容器也能响应本次按下事件
break;
case MotionEvent.ACTION_MOVE: //当触发滑动时,将父容器的按下响应失效
//修改动作为ACTION_CANCEL
ev.setAction(MotionEvent.ACTION_CANCEL);
parentView.onTouchEvent(ev);
//父容器响应后恢复事件原动作
ev.setAction(MotionEvent.ACTION_MOVE);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
parentView.onTouchEvent(ev); //将松手事件,先行传递给父容器响应
break;
}
}
return super.onTouchEvent(ev); //无论如何 本身都响应事件
}
}
简单吧,几行代码就搞定了,不知道为什么网上这么难找,可能是我组织不好关键字所以搜不到
使用的话布局中直接用ParentClickHorizontalScrollView 替换HorizontalScrollView即可,父控件和ParentClickHorizontalScrollView本身无需设置额外其他属性