之前去面试,人家说,我这个事件拦截机制写得太少了,还有一个MotionEvent没写,这个确实也很重要,后来我考虑了一下,决定将这篇文章放到自己定义控件里。

先简单再提一下事件分发,事件分发和拦截主要涉及3个方法:

  • interceptTouchEvent()这个方法,顾名思义是我们的拦截方法,返回true事件就不会向下传递;
  • onTouchEvent()就是触摸事件,返回true就表明当前层消费了这次事件,它会阻止事件向上传递;
  • 而dispatchTouchEvent()是事件分发的方法,它的super方法是事件分发的关键,我们不要去动他,它分发它的事件,我们拦我们的,逻辑上不要乱。

而接下来讲的MotionEvent,MotionEvent其实就是onTouchEvent()和onTouch()这两个方法的参数,要想消费掉事件,就要在这两个方法块里面编辑我们的代码。

常量

先来认识一下这些常量,看一下就好,大概记住一下。

常见的动作常量:

  1. public static final int ACTION_DOWN = 0;单点触摸动作
  2. public static final int ACTION_UP = 1;单点触摸离开动作
  3. public static final int ACTION_MOVE = 2;触摸点移动动作
  4. public static final int ACTION_CANCEL = 3;触摸动作取消
  5. public static final int ACTION_OUTSIDE = 4;触摸动作超出边界
  6. public static final int ACTION_POINTER_DOWN = 5;多点触摸动作
  7. public static final int ACTION_POINTER_UP = 6;多点离开动作

以下是一些非touch事件

  1. public static final int ACTION_HOVER_MOVE = 7;
  2. public static final int ACTION_SCROLL = 8;
  3. public static final int ACTION_HOVER_ENTER = 9;
  4. public static final int ACTION_HOVER_EXIT = 10;

掩码常量

  1. ACTION_MASK = 0X000000ff

动作掩码

  1. ACTION_POINTER_INDEX_MASK = 0X0000ff00

触摸点索引掩码

  1. ACTION_POINTER_INDEX_SHIFT = 8 获取触摸点索引需要移动的位数

加速球的实现

如何使用这个事件?我们就来做一下360加速球一样的东西吧,可以拖着那个加速球到处跑,加速球会自动旋转,此外还可以给他设置点击事件等等。

考虑一下我们的需求和设计方案:

  • 首先就是旋转动画,360加速球是可以旋转的,旋转可以用Canvas、Matrix来做,不过我算法不是特别好,做出来老是跑飞了,就用帧动画了;
  • 然后是拖拽这个控件,这个要在ACTION_MOVE里面处理,每次获取一下触摸的坐标,然后调用postInvalidate()重绘即可;
  • 其中onTouchEvent和onClick是冲突的,因此点击事件得重新设计,我准备如果坐标没发生变化,就在松开手指的时候触发点击事件。

源码

  1. /**
  2. * 拖拽按钮,直接继承Button,可以省去很多代码,关于帧动画的代码我就不贴了,因为没什么技术含量
  3. * Created by ChenSS on 2017/1/2.
  4. */
  5. public class DragButton extends Button {
  6. private Point point = new Point();
  7. //记录之前的坐标
  8. private Point pointBefore = new Point();
  9. //记录之后的坐标
  10. private Point pointAfter = new Point();
  11. //将点击事件获取下来,在松开手指的时候调用
  12. private OnClickListener listener;
  13. public DragButton(Context context) {
  14. this(context, null);
  15. }
  16. public DragButton(Context context, AttributeSet attrs) {
  17. this(context, attrs, android.R.attr.buttonStyle);
  18. }
  19. public DragButton(Context context, AttributeSet attrs, int defStyleAttr) {
  20. super(context, attrs, defStyleAttr);
  21. }
  22. @Override
  23. public void setOnTouchListener(OnTouchListener l) {
  24. super.setOnTouchListener(l);
  25. }
  26. @Override
  27. public boolean onTouchEvent(MotionEvent event) {
  28. //图片旋转,我使用的是帧动画,其实直接通过onDraw直接重绘更好
  29. AnimationDrawable animationDrawable = (AnimationDrawable) getBackground();
  30. int eventAction = event.getAction();
  31. int x = (int) event.getRawX();
  32. int y = (int) event.getRawY();
  33. Log.e("point", x + "=========" + y);
  34. switch (eventAction) {
  35. case MotionEvent.ACTION_DOWN:
  36. //记录触摸时的坐标
  37. pointBefore.x = getLeft();
  38. pointBefore.y = getTop();
  39. point.x = (int) event.getX();
  40. point.y = (y - getTop());
  41. //启动动画
  42. animationDrawable.start();
  43. break;
  44. case MotionEvent.ACTION_MOVE:
  45. //这一步主要就是获取触摸的坐标,重新绘制View
  46. int left = x - point.x;
  47. int top = y - point.y;
  48. int right = left + getWidth();
  49. int bottom = top + getHeight();
  50. layout(left, top, right, bottom);
  51. //重新绘制
  52. postInvalidate();
  53. break;
  54. case MotionEvent.ACTION_UP:
  55. pointAfter.x = getLeft();
  56. pointAfter.y = getTop();
  57. //我们要判断一下,松开手指的时候,坐标是不是和原先一样
  58. //如果坐标变化了,说明用户在拖动按钮,这个时候就不要执行点击事件
  59. //如果没变化,就说明用户点击了一下这个按钮
  60. if (pointAfter.equals(pointBefore.x, pointBefore.y)) {
  61. listener.onClick(this);
  62. } else {
  63. pointBefore.set(pointAfter.x, pointAfter.y);
  64. }
  65. //结束动画
  66. animationDrawable.stop();
  67. break;
  68. default:
  69. break;
  70. }
  71. return true;
  72. }
  73. @Override
  74. public void setOnClickListener(OnClickListener listener) {
  75. //这里就不要调用super了,因为我们的点击事件比较特殊,不能交给父类处理
  76. this.listener = listener;
  77. }
  78. }
  1. /**
  2. * 我使用的是相对布局,然后就是上面的Button了
  3. */
  4. public class TestActivity extends AppCompatActivity {
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_test);
  9. DragButton button = (DragButton) findViewById(R.id.test_btn);
  10. button.setOnClickListener(new View.OnClickListener() {
  11. @Override
  12. public void onClick(View v) {
  13. // 点击的时候,给一个弹窗
  14. new AlertDialog.Builder(TestActivity.this)
  15. .setNeutralButton("OK", new DialogInterface.OnClickListener() {
  16. public void onClick(DialogInterface arg0, int arg1) {
  17. }
  18. })
  19. .setTitle("test button")
  20. .setMessage("test test test!!!")
  21. .show();
  22. }
  23. });
  24. }
  25. }

多点触摸————图片放大

多点触摸最常见的使用方式,我觉得应该是在下拉刷新。无聊的小伙伴应该都玩过微信或者QQ的下拉刷新,把它一直拉到屏幕底部,拉到不能再拉了,然后再松开。这个过程,我们一个手指肯定无法一次性将ListView拉到最底部,这个过程应当是:用一个手指一直拉,然后换另一个手指拉,然后再换别的手指。这个换的过程,需要捕捉多个触摸点。不过下拉刷新都被写烂了,我也不想再写了,来简单地实现以下图片放大的功能吧。

  1. /**
  2. * 代码我写得尽量精简,但是显得有些粗糙,用的还是帧布局,然后一个ImageView
  3. */
  4. public class ZoomImgActivity extends AppCompatActivity implements View.OnTouchListener {
  5. private ImageView myImageView;
  6. //两个触点的间距
  7. private float currentDistance = 0;
  8. //旧的两个触点的间距
  9. private float lastDistance = 0;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState) {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.activity_zoom_img);
  14. myImageView = (ImageView) findViewById(R.id.activity_zoom_img);
  15. myImageView.setOnTouchListener(this);
  16. }
  17. @Override
  18. public boolean onTouch(View v, MotionEvent event) {
  19. ImageView myImageView = (ImageView) v;
  20. switch (event.getAction()) {
  21. case MotionEvent.ACTION_MOVE:
  22. //获取LayoutParams
  23. RelativeLayout.LayoutParams layoutParams =
  24. (RelativeLayout.LayoutParams) myImageView.getLayoutParams();
  25. switch (event.getPointerCount()) {
  26. case 1:
  27. break;
  28. case 2:
  29. //两个的话就放大或者缩小
  30. float offsetX = event.getX(0) - event.getX(1);
  31. float offsetY = event.getY(0) - event.getY(1);
  32. currentDistance = (float) Math.sqrt(offsetX * offsetX + offsetY * offsetY);
  33. //5px有误差
  34. if (currentDistance - lastDistance > 5) {
  35. //放大
  36. layoutParams.width = (int) (1.01f * myImageView.getWidth());
  37. layoutParams.height = (int) (1.01f * myImageView.getHeight());
  38. myImageView.setLayoutParams(layoutParams);
  39. } else if (lastDistance - currentDistance > 5) {
  40. //缩小
  41. layoutParams.width = (int) (0.99f * myImageView.getWidth());
  42. layoutParams.height = (int) (0.99f * myImageView.getHeight());
  43. myImageView.setLayoutParams(layoutParams);
  44. }
  45. lastDistance = currentDistance;
  46. }
  47. }
  48. return true;
  49. }
  50. }

安卓自定义控件(五)触控基础MotionEvent的更多相关文章

  1. 多点触控之MotionEvent.ACTION_MASK作用

    ACTION_MASK在Android中是应用于多点触摸操作,字面上的意思大概是动作掩码的意思吧. 在onTouchEvent(MotionEvent event)中,使用switch (event. ...

  2. android触控,先了解MotionEvent(一)

    http://my.oschina.net/banxi/blog/56421 这是我个人的看法,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent常用的API要 ...

  3. 【朝花夕拾】Android自定义View篇之(八)多点触控(上)MotionEvent简介

    前言 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2 ...

  4. 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)

    前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...

  5. Android多点触控手势基础

    处理多点触控手势 多点触控就是同时把一根以上的手指放在屏幕上. 再继续往下以前需要补充一些名词: 触控手势:就是把一根或者几根手指放在屏幕上做各种动作,其中包括保留一根手指的前提下,拿起或者放下其余的 ...

  6. android触控,先了解MotionEvent

    MotionEvent源代码可以在ocs看到,当然你也可以在SDK中下载源代码,或者其他地方,如: https://github.com/CyanogenMod/android_frameworks_ ...

  7. (五)多点触控之兼容ViewPager

    在上一篇文章中,自定义的ZoomImageView已经实现了自由缩放,自由移动以及双击放大与缩小的功能.已经可以投入使用这个控件了.下面我们就在ViewPager中使用这个控件.如果你还没读过上一篇文 ...

  8. (一)自定义ImageView,初步实现多点触控、自由缩放

    真心佩服那些一直专注于技术共享的大神们,正是因为他们无私的分享精神,我才能每天都有进步.近日又算是仔细学了android的自定义控件技术,跟着大神的脚步实现了一个自定义的ImageView.里面涉及到 ...

  9. 【朝花夕拾】Android自定义View篇之(九)多点触控(下)实践出真知

    前言 在上一篇文章中,已经总结了MotionEvent以及多点触控相关的基础理论知识和常用的函数.本篇将通过实现单指拖动图片,多指拖动图片的实际案例来进行练习并实现一些效果,来理解前面的理论知识.要理 ...

随机推荐

  1. win10 uwp 上传Nuget 让别人用我们的库

    Nuget 我们的开发经常使用别人的dll,那么我们需要每次都从网上下载,然后复制到我们的项目, 而不知道我们的dll是否安全? 当我们的库更新的时候,我们又需要从网上搜索,这样不好,于是我们就用Nu ...

  2. win10 & Ubuntu16 双系统安装

    忽然心血来潮吧,本机在已经安装了win10的背景下,想要再加一个linux系统学习学习,几经波折,终于成功. 博主笔记本里有两块固态,一个250G的装了win10,装的时间不久,镜像是在msdn上下载 ...

  3. Quartz格式设置说明

    这些星号由左到右按顺序代表 :     *    *     *     *    *     *   *                                             格式 ...

  4. 【转】话说C语言const用法

    原文:话说C语言const用法 const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,意即其所修饰的对象为常量(immutable). 我们来分情况看语法上它该如何被使用. 1.函数体内修 ...

  5. 备忘:有MTU值设置不当导致的部分网站无法访问问题

    如题,有时候突然weibo.com,webQQ等网站网络连接超时,怎么找也没得原因,今天管理电信的光猫,发现设置的MTU的1400,突然想起之前电脑和路由器上设置的MTU是1500,感觉可能是这个问题 ...

  6. oracle建表权限问题和JSP连接oracle数据库基本操作

    JSP连接oracle数据库相关操作 1.创建表 打开Enterprise Manager Console,为用户添加权限CREATE ANY TABLE和分配一定的表空间USERS限额1024k. ...

  7. 域名系统DNS

    一.域名系统是什么 域名系统其实就是一个把主机名解析为IP地址的名字系统. 因特网使用层次树状结构的命名方法,并使用分布式的域名系统DNS.因特网的域名系统DNS被设计成一个联机分布式数据库系统,并采 ...

  8. ES6中的Promise用法

    Node的产生,大大推动了Javascript这门语言在服务端的发展,使得前端人员可以以很低的门槛转向后端开发. 当然,这并不代表迸发成了全栈.全栈的技能很集中,绝不仅仅是前端会写一些HTML和一些交 ...

  9. TIDB技术文档翻译

    http://blog.csdn.net/antony9118/article/details/60470115

  10. Elasticsearch集群调优

    系统调优 禁用swap 使用swapoff命令可以暂时关闭swap.永久关闭需要编辑/etc/fstab,注释掉swap设备的挂载项. swapoff -a 如果完全关闭swap不可行,可以试着降低s ...