[Android]视图的控触操作-MotionEvent
创始人
2024-05-03 02:16:11

 引入

对屏幕的任何操作,系统都会创建一个触摸事件的对象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=0var lastY=0lateinit var imageView: ImageViewlateinit var parentView:RelativeLayoutvar maxRight=0var maxBottom=0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)imageView=findViewById(R.id.imageView4)parentView=imageView.parent as RelativeLayoutimageView.setOnTouchListener(this)}override fun onTouch(p0: View?, event: MotionEvent): Boolean {val eventX=event.rawXval eventY=event.rawYwhen(event.action){MotionEvent.ACTION_DOWN->{if(maxRight==0){maxRight=parentView.rightmaxBottom=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+dxvar top=imageView.top+dyvar right=imageView.right+dxvar bottom=imageView.bottom+dy//限制left>=0if(left<0){right+=-leftleft=0}//限制top>=0if(top<0){bottom+=-toptop=0}//限制rightmaxRight){left-=right-maxRightright=maxRight}//限制bottom>=0if(bottom>maxBottom){top-=bottom-maxBottombottom=maxBottom}imageView.layout(left, top, right, bottom)lastX=eventX.toInt()lastY=eventY.toInt()}}return true}
}

相关内容

热门资讯

北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...