[Android]视图的控触操作-MotionEvent

简介: [Android]视图的控触操作-MotionEvent

引入

对屏幕的任何操作,系统都会创建一个触摸事件的对象MotionEvent来应对这个操作。当点击手机屏幕的某一个视图时,最先感应到的是屏幕,因为Activity系统是分层的结构,底层是一些驱动,所以驱动就会得到信息并且把信息传到被点击的应用,应用再交给Activity,Activity通过MotionEvent对象来实现对视图的触控操作,那么接下来我们学习一下MotionEvent对象如何实现对视图的触控操作。

一、事件的操作类型

正常情况下,一次手指触摸屏幕的行为会引起一系列的点击事件,MotionEvent对象存在变量action来反映这一系列点击事件的操作类型,所以我们可以通过MotionEvent对象的action变量的值来得到当前的点击状态。

  • 手指按下时,action的值等于ACTION_DOWN,等于0
  • 手指在屏幕上移动时,action的值等于ACTION_MOVE,等于2
  • 手指离开屏幕,action的值等于ACTION_UP,等于1

单点触控一次简单的交互流程有两种情况:

  • 手指按下,马上离开,action的值的变化为0->1
  • 手指按下,在屏幕上移动一段距离后离开屏幕,action的值的变化为0->2->...->2->1

二、MotionEvent的传递,消耗,处理过程

事件分发的三个重要方法:

      1、dispatchTouchEvent()方法:会从Activity开始一层一层地向子View分发事件,直到没有子           View。(分发的时候,只能是View分发给子View,不能View分发给孙View)

       2、onTouchEvent()方法:会在dispatchTouchEvent()方法调用到最底层View之后,再从最底           层View一层一层地往上回调,如果某个View的onTouchEvent方法返回true,那么就会停止            向上回调。

       3、onInterceptTouchEvent方法:伴随着dispatchTouchEvent()方法存在,它的作用就是拦截          ViewGroup的事件,不让它继续向下分发事件。

Activity,View,ViewGroup和MotionEvent的主要方法:

Activity:

dispatchTouchEvent(ev: MotionEvent?): Boolean 分发事件
onTouchEvent(ev: MotionEvent?): Boolean 处理事件的回调

View:

dispatchTouchEvent(ev: MotionEvent?): Boolean 分发事件
onTouchEvent(ev: MotionEvent?): Boolean 处理事件的回调
setOnTouchListener(l:OnTouchListener) 设置事件监听器
setOnClickListener(l:OnClickListener) 设置点击监听
setOnLongClickListener(l:OnClickListener) 设置长按监听
setOnCreateContextMenuListener(l:OnCreateContextMenuListener) 用于创建菜单

注意:OnTouchListener中的onTouch()事件优先级高于onTouchEvent()事件,如果onTocuh()的返回结果为ture,那么该View的onTouchEvent()事件将不会被调用。

ViewGroup:

dispatchTouchEvent(ev: MotionEvent?): Boolean 分发事件
onInterceptTouchEvent(ev: MotionEvent?): Boolean
拦截事件

注意:onInterceptTouchEvent方法只在ViewGroup中可以重写。

MotionEvent:

同时通过MotionEvent对象我们可以得到点击事件的x和y轴坐标。

系统提供的方法如下:

getX() 得到事件发生的x轴坐标(相对于当前视图)
getY() 得到事件发生的y轴坐标(相对于当前视图)
getRawX() 得到事件发生的x轴坐标(相对于屏幕左顶点)
getRawY() 得到事件发生的y轴坐标(相对于屏幕左顶点)

下面我们来理解一下MotionEvent的传递,消耗,处理过程

如图所示,当点击事件产生之后,事件首先会传递给当前的Activtiy,Activity会调用分发事件方法dispatchTouchEvent将事情传递给最大的View,然后再一层层地向下传递给子View,直到传递到最小的view,调用最小的view的onTouchEvent方法,向上传递,直到有一个view的onTouchEvent方法返回true,消耗这个点击事件,消耗这个点击事件之后就不会向上传递了。如果没有,那么事件最终会被activity消耗。

通俗点说就是:爷爷(Activity)得到了一个苹果(点击事件),爷爷把苹果给了爸爸(View),爸爸把苹果给了我(子View),如果我选择不吃苹果,那么就把苹果给爸爸,如何爸爸如果选择吃了苹果,就是把苹果消耗了,如果爸爸选择不吃苹果,就把苹果给爷爷,爷爷只能吃掉苹果,结束。

通过代码加深理解MotionEvent的传递,消耗,处理过程

当View的onTouch和onTouchEvent方法都返回false时

点击一下View,产生的点击事件如下:

点击View并移动,产生的点击事件如下:

最终都是Activity消费了点击事件。

当View的onTouch方法返回true时:

点击一下View,产生的点击事件如下:

点击View并移动,产生的点击事件如下:

最终都是View消费了点击事件,并且可以看出OnTouchListener中的onTouch()事件优先级高于onTouchEvent()事件,如果onTocuh()的返回结果为ture,那么该View的onTouchEvent()事件将不会被调用。

小项目

先看效果图:

 

  • 功能描述

           通过手指移动来拖动图片

          控制图片不能超过屏幕显示区域

  • 技术点

        MotionEvent处理

        对View进行动态定位(layout)

代码:

class MainActivity : AppCompatActivity(),View.OnTouchListener{
    var lastX=0
    var lastY=0
    lateinit var imageView: ImageView
    lateinit var parentView:RelativeLayout
    var maxRight=0
    var maxBottom=0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        imageView=findViewById(R.id.imageView4)
        parentView=imageView.parent as RelativeLayout
        imageView.setOnTouchListener(this)
    }
    override fun onTouch(p0: View?, event: MotionEvent): Boolean {
        val eventX=event.rawX
        val eventY=event.rawY
        when(event.action){
            MotionEvent.ACTION_DOWN->{
                if(maxRight==0){
                    maxRight=parentView.right
                    maxBottom=parentView.bottom
                }
                lastX= eventX.toInt()
                lastY=eventY.toInt()}
            MotionEvent.ACTION_MOVE->{
                var dx:Int=(eventX-lastX).toInt()
                var dy:Int=(eventY-lastY).toInt()
                var left=imageView.left+dx
                var top=imageView.top+dy
                var right=imageView.right+dx
                var bottom=imageView.bottom+dy
                //限制left>=0
                if(left<0){
                    right+=-left
                    left=0
                }
                //限制top>=0
                if(top<0){
                    bottom+=-top
                    top=0
                }
                //限制right<maxRight
                if(right>maxRight){
                    left-=right-maxRight
                    right=maxRight
                }
                //限制bottom>=0
                if(bottom>maxBottom){
                    top-=bottom-maxBottom
                    bottom=maxBottom
                }
                imageView.layout(left, top, right, bottom)
                lastX=eventX.toInt()
                lastY=eventY.toInt()
            }
        }
        return true
    }
}
目录
相关文章
|
6月前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
141 5
|
6月前
|
Java Android开发
Android系统 获取用户最后操作时间回调实现和原理分析
Android系统 获取用户最后操作时间回调实现和原理分析
172 0
|
6月前
|
Linux Android开发
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
100 0
|
28天前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
31 3
|
3月前
|
供应链 物联网 区块链
未来触手可及:探索新兴技术的趋势与应用安卓开发中的自定义视图:从基础到进阶
【8月更文挑战第30天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实正在重塑我们的世界。本文将深入探讨这些技术的发展趋势和应用场景,带你领略未来的可能性。
|
3月前
|
测试技术 Android开发 Python
探索软件测试的艺术:从基础到高级安卓应用开发中的自定义视图
【8月更文挑战第29天】在软件开发的世界中,测试是不可或缺的一环。它如同艺术一般,需要精细的技巧和深厚的知识。本文旨在通过浅显易懂的语言,引领读者从软件测试的基础出发,逐步深入到更复杂的测试策略和工具的使用,最终达到能够独立进行高效测试的水平。我们将一起探索如何通过不同的测试方法来确保软件的质量和性能,就像艺术家通过不同的色彩和笔触来完成一幅画作一样。
|
2月前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
56 10
|
2月前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
38 3
|
2月前
|
前端开发 Android开发 开发者
安卓应用开发中的自定义视图基础
【9月更文挑战第13天】在安卓开发的广阔天地中,自定义视图是一块神奇的画布,它允许开发者将想象力转化为用户界面的创新元素。本文将带你一探究竟,了解如何从零开始构建自定义视图,包括绘图基础、触摸事件处理,以及性能优化的实用技巧。无论你是想提升应用的视觉吸引力,还是追求更流畅的交互体验,这里都有你需要的金钥匙。
|
2月前
|
前端开发 搜索推荐 Android开发
探索安卓开发中的自定义视图##
【9月更文挑战第6天】 在安卓应用开发的世界里,自定义视图如同绘画艺术中的色彩,它们为界面设计增添了无限可能。通过掌握自定义视图的绘制技巧,开发者能够创造出既符合品牌形象又提升用户体验的独特界面元素。本文将深入浅出地介绍如何从零开始构建一个自定义视图,包括基础框架搭建、关键绘图方法实现、事件处理机制以及性能优化策略。准备好让你的安卓应用与众不同了吗?让我们开始吧! ##