写点什么

Android 扩大 View 的点击区域 (1),安卓软件开发面试题

用户头像
Android架构
关注
发布于: 17 小时前

下面我将简单介绍一下 View 的事件分发机制,方便大家理解后面的解决办法。


为了更清楚的说明整个机制,采用如下的视图来说明点击的事件分发机制。下图是一个 FrameLayout (ViewGroup) 里面包含着一个 ImageView (View)。


![](https://img-blog.csdnimg.cn/img_convert/187e7b3650027b8


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


c68377a1a467b8afb.png)


先自定义一个?MyFrameLayout,继承 FrameLayout,并实现两个点击相关的接口;具体代码如下:


public class MyFrameLayout extends FrameLayout implements OnClickListener, OnTouchListener {


private static final String TAG = "Event";


public MyFrameLayout(Context context, AttributeSet attrs) {


super(context, attrs);


Log.d(TAG, "MyFrameLayout init");


setOnClickListener(this);


setOnTouchListener(this);


}


@Override


public boolean dispatchTouchEvent(MotionEvent event) {


Log.d(TAG, "MyFrameLayout dispatchTouchEvent " + event.getAction());


return super.dispatchTouchEvent(event);


}


@Override


public boolean onTouchEvent(MotionEvent event) {


Log.d(TAG, "MyFrameLayout onTouchEvent " + event.getAction() );


return super.onTouchEvent(event);


}


@Override


public void onClick(View view) {


Log.d(TAG, "MyFrameLayout onClick");


}


@Override


public boolean onTouch(View view, MotionEvent event) {


Log.d(TAG, "MyFrameLayout onTouch " + event.getAction());


return true;


}


@Override


public boolean onInterceptTouchEvent(MotionEvent ev) {


Log.d(TAG, "MyFrameLayout onInterceptTouchEvent " + ev.getAction());


return super.onInterceptTouchEvent(ev);


}


}


接着,对于 ImageView 也做类似的操作,具体代码如下:


public class MyImageView extends ImageView implements OnClickListener, OnTouchListener {


private static final String TAG = "Event";


public MyImageView(Context context, AttributeSet attrs) {


super(context, attrs);


Log.d(TAG, "MyImageView init");


setOnClickListener(this);


setOnTouchListener(this);


}


@Override


public boolean dispatchTouchEvent(MotionEvent event) {


Log.d(TAG, "MyImageView dispatchTouchEvent "+ event.getAction());


return super.dispatchTouchEvent(event);


}


@Override


public boolean onTouchEvent(MotionEvent event) {


Log.d(TAG, "MyImageView onTouchEvent "+ event.getAction());


return super.onTouchEvent(event);


}


@Override


public boolean onTouch(View arg0, MotionEvent arg1) {


Log.d(TAG, "MyImageView onTouch " + arg1.getAction());


return false;


}


@Override


public void onClick(View arg0) {


Log.d(TAG, "MyImageView onClick");


}


}


这里要说明的是,只有 ViewGroup 才有?onInterceptTouchEvent 方法的,普通的 View 是没有的,它是不能对事件进行拦截的。


那这时候,如果我们点击里面的 ImageView,会有怎样的输出呢?结果如下图。



那如果点击外层呢?



0,1,2 分别是代表?ACTION_DOWN,ACTION_UP,ACTION_MOVE;从中也可以看出一个点击动作包含一个 Down,一个 Up,还有多个 Move 操作。


再来看一段源码:


public boolean dispatchTouchEvent(MotionEvent ev){


boolean consume = false;


if(onInterceptTouchEvent(ev)){


consume = onTouchEvent(ev);


} else{


consume = child.dispatchTouchEvent(ev);


}


return consume;


}


上述的代码把三者的关系说得很清楚了,对于一个对于一个 ViewGroup 来说,点击事件产生后,首先会传递给它,这时候会调用 dispatchTouchEvent,如果这个?ViewGroup 的?onInterceptTouchEvent 返回 true ,则表示它要拦截该事件,也就会交给它的?onTouchEvent 来进行处理。如果这个?ViewGroup 的?onInterceptTouchEvent 返回 false 则会传给子元素,子元素的?dispatchTouchEvent 就会被调用,如此反复循环。这与上面一张图打出的结果是一致的。


这里还有说明的是,如果代码设置了?OnTouchListener,那么就会先调用?onTouch 方法,然后在调用?onTouchEvent。OnClickListener 是优先级最低的,所以最后才会调用?onClick。


因此,从第二张结果图也可以看出,当存在 onTouch 之后,onTouchEvent 和?onClick 两个方法都不会在调用了。


相信到这里,大家对于 View 的事件分发机制有一定的了解了。


这里回到开头提的那个问题,那么有什么办法可以扩大 View 的点击区域呢?


答案:在父 View 设置 OnTouchListener 对点击事件进行拦截,通过判断点击的位置,来决定是相应子 View 的事件,还是父 View 的事件。


具体实现代码如下:


public class TouchFactory {


/** 扩展垂直方向点击区域尺寸 */

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android 扩大 View 的点击区域(1),安卓软件开发面试题