在Android里Touch是很常用的事件,尤其实在自定义控件中,要实现一些动态的效果,往往要对Touch进行处理。Android中主要有3个地方可以处理Touch事件:
一、在View里,有两个回调函数
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
二、在ViewGroup里,有三个回调函数
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
三、在Activity里,有两个回调函数
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
在这三个不同的地方,它们对Touch事件的处理流程很相似,但也有不同的地方。
在本节,就先研究View对Touch的处理过程。首先,Touch事件先到达dispatchTouchEvent(),我们来看看View.dispatchTouchEvent()的源码,这里面涉及到View的onTouchListener,和onTouchEvent()。
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
return false;
}
从源码中,我们可以看出View的onTouchListener和onTouchEvent都是在这里被调用的。如果View的touchListener返回true,dispatchTouchEvent()直接就返回,连onTouchEvent都不会被调用了。只有View没有设置onTouchListener,或者touchListener.onTouch()返回false,才会调用onTouchEvent()。
我们还可以看出,如果onTouchEvent()被执行了的话,dispatchTouchEvent()的返回值就是onTouchEvent()的返回值。事实上,真正起作用的也就是dispatchTouchEvent(),onTouchEvent()只是被dispatchTouchEvent()调用了而已。关于这个返回值的作用,请往下看。
我们需要一些实验,自定义控件还使用之前的画板
package com.ipjmc.vgdemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class CustomView extends View {
private static final String TAG = "CustomView";
private int mLastX, mLastY;
private int mCurrX, mCurrY;
private Bitmap mBitmap;
private Paint mPaint;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setStrokeWidth(6);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
if (mBitmap == null) {
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
Canvas tmpCanvas = new Canvas(mBitmap);
tmpCanvas.drawLine(mLastX, mLastY, mCurrX, mCurrY, mPaint);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Utils.log(TAG, "dispatchTouchEvent", event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mLastX = mCurrX;
mLastY = mCurrY;
mCurrX = (int) event.getX();
mCurrY = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = mCurrX;
mLastY = mCurrY;
break;
default:
break;
}
invalidate();
return true;
}
}
我们在画板上画一条线,看一下日志
如果我们把dispatchTouchEvent的返回值改为true,无论onTouchEvent()的返回值是什么,都不影响。
现在,我们把dispatchTouchEvent的返回值,改为false看看会怎么样
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Utils.log(TAG, "dispatchTouchEvent", event.getAction());
super.dispatchTouchEvent(event);
return false;
}
我们在画板上画一条线,看一下日志
我们可以看到,只在Touch事件是ACTION_DOWN的时候,打印了一条日志,而且画板上也没有画出一条线。这是说明在ACTION_DOWN的时候,如果dispatchTouchEvent返回false,那么这个View就接收不到后面的触屏事件了。
如果我们这样改呢?dispatchTouchEvent()只在ACTION_DOWN的时候,返回true,其他时候返回false。答案是一切又恢复正常了。
public boolean dispatchTouchEvent(MotionEvent event) {
Utils.log(TAG, "dispatchTouchEvent", event.getAction());
super.dispatchTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
return true;
}
return false;
}
可以这样理解。每一个触屏事件都必须是以ACTION_DOWN作为开头,后面跟一系列的ACTION_MOVE,最后再有一个ACTION_UP(或ACTION_CANCEL),标识触屏事件结束。所以Android就在ACTION_DOWN的时候做文章,官方文档对dispatchTouchEvent的返回值的解释是:True if the event was handled by the view, false otherwise。我们可以简单的理解为如果返回true,就说明它需要处理这个事件,就让它接收所有的触屏事件,否则,说明它不用处理,也就不让它接收后续的触屏事件了。
- 大小: 62.7 KB
- 大小: 9.7 KB
分享到:
相关推荐
Android里有两个类android.view.GestureDetectorandroid.view.GestureDetector.SimpleOnGestureListener(另外android.widget.Gallery好像是更牛x的OnGestureListener )1)新建一个类继承SimpleOnGestureListener,...
第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层开发过程,融会贯通,介绍一些比较深入的知识点;第三,介绍一些核心技术和Android的性能优化思想。 第1章 Activity的生命周期和...
第1章 Activity的生命周期和启动模式 / 1 1.1 Activity的生命周期全面分析 / 1 1.1.1 典型情况下的生命周期分析 / 2 1.1.2 异常情况下的生命周期分析 / 8 1.2 Activity的启动模式 / 16 1.2.1 Activity的...
(1) 所有 Touch 事件都被封装成了 MotionEvent 对象,包括 Touch 的位置、时间、历史记录以及第几个手指(多指触摸)等 (2) 事件类型
第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层开发过程,融会贯通,介绍一些比较深入的知识点;第三,介绍一些核心技术和Android的性能优化思想。, 《Android开发艺术探索》侧重...
Android自定义View研究-- 一个小Demo Android调用相册拍照实现系统控件缩放切割图片 Android SQLite的实例汇总大全 两分钟彻底让你明白Android Activity生命周期(图文)! Android 图形系统剖析 Android 立体效果图片...
之前开发任务中有涉及到年月日日历的切换效果,由于是需要联动,想到的方向大概有3种,要么通过处理view的touch事件,要么是通过自定义behavior去实现,要么是通过ViewDragHelper这个神器去实现,网上比较多的是通过...
其中Touch的第一个状态肯定是ACTION_DOWN,表示按下了屏幕。之后,touch将会有后续事件,可能是: ACTION_MOVE //表示为移动手势 ACTION_UP //表示为离开屏幕 ACTION_CANCEL //表示取消手势,不会由用户产生,而是由...
// 初始化选择第一个 mContext = context; // 指定只能有三个缓存View mViews = new FlingGalleryView[3]; mViews[0] = new FlingGalleryView(0, this); mViews[1] = new FlingGalleryView(1, this); ...
滑动删除的部分主要包含两个部分, 一个是内容区域(用于放置正常显示的view),另一个是操作区域(用于放置删除按钮)。...滑动效果的控制,主要就是通过检测SwipeLayout的touch事件来实现,Android sup
弃用(可参考)toasts2:丰富多彩的Toastupdateapk:XML配置升级APKuploadpic:最强微信图片上传(无bug版 修改了loaderManager两次finish的问题)usersdk:登录回调处理逻辑跳转widget: 1.AlertView:仿IOS弹窗 2...