在Android的自定义View中,往往需要处理一系列的事件,如触摸事件、双击事件、缩放事件等。本文将这些事件及其处理进行总结。本文将持续更新,将我在自定义View的实践中用到的事件及其处理进行总结。

1、触摸事件

  触摸事件用于处理用户在触摸屏幕时触发的事件。

  触摸事件通常在 onTouchEvent() 方法中处理,有时也会实现 OnTouchListener 进行处理。

  用户的触摸可以分为三类:手指按下、手指滑动和手指抬起,对应 MotionEvent 类中的事件常量依次为 ACTION_DOWN 、 ACTION_MOVE 和 ACTION_UP 。

  以下是一个处理触摸事件的示例,在这个例子中,我们通过分析用户的触摸来让布局控件滚动。代码如下:

/**
* 滚动事件的处理,当布局可以滚动(内容高度大于测量高度)时,对手势操作进行处理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
// 只有当布局可以滚动的时候(内容高度大于测量高度的时候),才会对手势操作进行处理
if (scrollable) {
int currY = (int) event.getY();
switch (event.getAction()) {
// 因为ACTION_DOWN手势可能是为了点击布局中的某个子元素,因此在onInterceptTouchEvent()方法中没有拦截这个手势
// 因此,在这个事件中不能获取到startY,也因此才将startY的获取移动到第一次滚动的时候进行
case MotionEvent.ACTION_DOWN:
break;
// 当第一次触发ACTION_MOVE事件时,视为手指按下;以后的ACTION_MOVE事件才视为滚动事件
case MotionEvent.ACTION_MOVE:
// 用pointerDown标志位只是手指是否已经按下
if (!pointerDown) {
startY = currY;
pointerDown = true;
} else {
offsetY = startY - currY; // 下滑大于0
// 布局中的内容跟随手指的滚动而滚动
// 用scrolledHeight记录以前的滚动事件中滚动过的高度(因为不一定每一次滚动都是从布局的最顶端开始的)
this.scrollTo(0, scrolledHeight + offsetY);
}
break;
// 手指抬起时,更新scrolledHeight的值;
// 如果滚动过界(滚动到高于布局最顶端或低于布局最低端的时候),设置滚动回到布局的边界处
case MotionEvent.ACTION_UP:
scrolledHeight += offsetY;
if (scrolledHeight + offsetY < 0) {
this.scrollTo(0, 0);
scrolledHeight = 0;
} else if (scrolledHeight + offsetY + measuredHeight > realHeight) {
this.scrollTo(0, realHeight - measuredHeight);
scrolledHeight = realHeight - measuredHeight;
}
// 手指抬起后别忘了重置这个标志位
pointerDown = false;
break;
}
}
return super.onTouchEvent(event);
}

2、加载完成事件

  某些时候,我们需要在这个控件加载完成之后做一些事情,比如,我们需要在一个ImageView加载完成之后拿到它加载的图片并获取其宽高,此时,我们就需要处理控件加载完成的事件。

  加载完成的事件通常需要实现 ViewTreeObserver.OnGlobalLayoutListener 接口并在 onGlobalLayout() 方法中处理,然后在 onAttachedToWindow() 方法中注册这个事件,在 onDetachedFromWindow() 方法中移除这个事件。

  以下是一个处理加载完成的事件的示例,在这个示例中,我们在ImageView加载完成之后获取其加载的图片,然后让图片居中铺满屏幕的宽或高。代码如下:

/**
* 当视图被加载到Activity的Window中时,添加监听器
*/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnGlobalLayoutListener(this);
} /**
* 当视图从Activity的Window中移除时,移除监听器
*/
@SuppressLint("NewApi")
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
} /**
* 视图加载完成后获取图片的大小
* 在onMeasure()、onLayout()、onDraw()方法或构造方法中都获取不到ImageView中设置的图片的大小
* 因此需要实现ViewTreeObserver.OnGlobalLayoutListener接口,在onGlobalLayout()方法中实现
* 注意:这个方法在程序运行期间可能执行多次,我们可以控制它只执行一次(设置一个标识符)
*/
@Override
public void onGlobalLayout() {
if (!once) {
// 得到控件的宽高
int width = getWidth();
int height = getHeight();
// 得到控件中加载的图片及其宽高
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
int dw = drawable.getIntrinsicWidth();
int dh = drawable.getIntrinsicHeight();
// 获取到图片缩放比例的几个阈值
initScale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
maxScale = initScale * 2f;
// 将图片居中在控件中显示
scaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
scaleMatrix.postScale(initScale, initScale, width / 2, height / 2);
setImageMatrix(scaleMatrix);
once = true;
}
}

3、缩放事件

  缩放事件通常用于处理用户多点触控的缩放事件。当用户用两个或多个手指在屏幕上进行放大或缩小的动作时,我们就需要处理缩放事件。

  缩放事件通常需要实现 ScaleGestureDetector.OnScaleGestureListener 接口并实现 onScaleBegin() 、 onScale() 和 onScaleEnd() 三个方法。这三个方法中都有一个 ScaleGestureDetector 类型的参数,通过这个参数我们可以得到在这个缩放瞬间用户缩放的倍数,以及缩放的中心点等。

  以下是一个处理缩放事件的示例。在这个示例中,我们对一个ImageView中的图片进行缩放,并在用户松开手指后回缩图片大小。代码如下:

@Override
public boolean onScale(ScaleGestureDetector detector) {
float factor = detector.getScaleFactor(); // 缩放的比例(相对于手指按下时的大小的缩放比例)
if (getDrawable() == null) {
return true;
}
scaleMatrix.postScale(factor, factor, detector.getFocusX(), detector.getFocusY());
checkBorderAndCenterWhileScale();
setImageMatrix(scaleMatrix);
return true;
} @Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
} @Override
public void onScaleEnd(ScaleGestureDetector detector) {
float currScale = getScale();
float factor = detector.getScaleFactor(); // 缩放的比例(相对于手指按下时的大小的缩放比例)
if (currScale <= initScale) {
factor = initScale / currScale;
} else if (currScale >= maxScale) {
factor = maxScale / currScale;
}
scaleMatrix.postScale(factor, factor, detector.getFocusX(), detector.getFocusY());
checkBorderAndCenterWhileScale();
setImageMatrix(scaleMatrix);
}

4、双击事件

  当用户在很短的一段时间内在控件上点击了两下,我们就需要对用户的双击事件进行处理了。

  双击事件和其他的一些列事件(如:长按事件、滚动事件、单机事件、惯性滑动事件等)都可以通过实现 GestureDetector.SimpleOnGestureListener 类并重写其中的相应方法来实现,例如,处理双击事件需要重写 onDoubleTap() 方法。

  下面是一个处理用户双击事件的示例。在这个示例中,当用户双击图片后,将图片放大到最大大小或缩小到初始大小。代码如下:

@Override
public boolean onDoubleTap(MotionEvent e) {
final float x = e.getX();
final float y = e.getY();
final float finalScale;
float scale = getScale();
if (scale < maxScale) {
finalScale = maxScale / scale;
} else {
finalScale = initScale / scale;
}
ZoomImageView.this.post(new Runnable() {
@Override
public void run() {
if (finalScale > 1) {
if (getScale() * LARGER_SCALE < maxScale) {
scaleMatrix.postScale(LARGER_SCALE, LARGER_SCALE, x, y);
checkBorderAndCenterWhileScale();
setImageMatrix(scaleMatrix);
postDelayed(this, 15);
} else {
scaleMatrix.postScale(maxScale / getScale(), maxScale / getScale(), x, y);
checkBorderAndCenterWhileScale();
setImageMatrix(scaleMatrix);
}
} else {
if (getScale() * SMALLER_SCALE > initScale) {
scaleMatrix.postScale(SMALLER_SCALE, SMALLER_SCALE, x, y);
checkBorderAndCenterWhileScale();
setImageMatrix(scaleMatrix);
postDelayed(this, 15);
}
}
}
});
return super.onDoubleTap(e);
}

【Android - 自定义View】之不同事件的处理的更多相关文章

  1. Android 自定义View——自定义点击事件

    每个人手机上都有通讯录,这是毫无疑问的,我们通讯录上有一个控件,在通讯录的最左边有一列从”#”到”Z”的字母,我们通过滑动或点击指定的字母来确定联系人的位置,进而找到联系人.我们这一节就通过开发这个控 ...

  2. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  3. (转)[原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  4. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  5. [原] Android 自定义View步骤

    例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...

  6. [原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  7. Android自定义View

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...

  8. android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索

    我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...

  9. Android 自定义View (一)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...

  10. Android 自定义View(button)

    很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章.先总结下自定义View的步骤 ...

随机推荐

  1. NOIP模拟 2

    大概就是考试的时候慌的一批,因为一道正解也没想出来,T1,T3只会暴搜,听见天皇在旁边的窃喜声本渣内心是崩溃的 会打暴搜的我先打了暴搜,大多数时间都用在第二题上,妄想自己能拿50多分- 最后半小时万念 ...

  2. NOIP模拟34

    考试的时候被T2卡了一年....考虑了一下正解的式子,然后没去给左边分解因数,去给后面乘倍数...理论复杂度O(n^2),实际好像卡不掉的样子.但是由于我智障的打了一棵主席树,他M了.... 预计得分 ...

  3. Hive数据仓库你了解了吗

    在工作中我们经常使用的数据库,数据库一般存放的我们系统中常用的数据,一般为百万级别.如果数据量庞大,达到千万级.亿级又需要对他们进行关联运算,该怎么办呢? 前面我们已经介绍了HDFS和MapReduc ...

  4. Java I/O系统学习系列五:Java序列化机制

    在Java的世界里,创建好对象之后,只要需要,对象是可以长驻内存,但是在程序终止时,所有对象还是会被销毁.这其实很合理,但是即使合理也不一定能满足所有场景,仍然存在着一些情况,需要能够在程序不运行的情 ...

  5. unittest加载测试用例名称必须以test开头,是否可以定制化

    ​ 前几天,在一个群里,一个人问了,这样一个问题.说他面试遇到一个面试官,问他,为啥unittest的测试用例要用test 开头,能不能定制化.他不知道为啥. 看到这个题目,我回答当然可以了,可以用l ...

  6. Linux | 性能分析系列学习 (2)

    常分析方法: 1.监控大盘,是否异常报警 2..平均负载情况,(top    /    htop )平均负载体现的是系统的一个整体情况,他应该是cpu.内存.磁盘性能的一个综合,一般是平均负载的值大于 ...

  7. H5+app -- 自动更新

    一.最近做了一个app自动更新功能,用的基本都是网上找得到的. 1.h5+ 规范 :  http://www.html5plus.org/doc/zh_cn/maps.html 2.环形进度条插件:h ...

  8. springboot封装JsonUtil,CookieUtil工具类

    springboot封装JsonUtil,CookieUtil工具类 yls 2019-9-23 JsonUtil public class JsonUtil { private static Obj ...

  9. count的一些用法

    count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL  count(1)包括了所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL  count(列名)只包 ...

  10. php的精度计算问题(bcadd和bcsub)

    一.前言 我们在进行php开发的时候经常会遇到浮点型的问题,特别是涉及金额的部分,常常需要进行加减运算.当小数点的位数比较多的时候,往往容易犯一些很低级的错误.这里记录一下php的精度计算和封装的小d ...