上篇文章中已经了解到界面Activity的绘制完全依赖其加载的视图组件View,不仅如此,用户的每次触摸操作都可以在界面Activity内接收并响应,也可以直接传递给其中的某个视图View响应。本文将针对这两种用户交互方式分别展开介绍。

界面内交互

界面响应

说到界面交互,很容易想到用户在设备屏幕上的触摸操作。可是屏幕那么大要怎么确定用户触摸的位置呢?Android系统定义了一套屏幕坐标规则,该规则不仅适用于当前的屏幕交互,在后文提及的动画绘制及其他屏幕相关操作等都同样适用。该规则将屏幕的左上角作为屏幕坐标的原点,从左上角往右上角延伸的方向作为屏幕坐标的x轴,从左上角往左下角延伸的方向作为屏幕坐标的y轴

比如针对一款 1024x512 尺寸的TV设备,其左下角的屏幕坐标值为 (0, 512),右下角的屏幕坐标值为 (1024, 512),右上角的屏幕坐标值为 (1024, 0),左上角的屏幕坐标值为 (0, 0)。

对屏幕的触摸位置有了衡量标准,是不是就可以根据不同的位置做触摸操作了呢?说到触摸操作,也需要细化之后单独处理。Android系统将用户操作行为,大致分为三种:按下行为滑动行为抬起释放行为。这样系统就可以根据每一个操作行为做单独的响应处理了。

另外,用户的操作对象,除了上文提到的硬件设备屏幕以外,还有硬件设备的按键(包括硬件按键和虚拟按键)。只不过对按键的操作行为只有按下行为抬起释放行为两种,而且按键的操作不需要用到屏幕坐标相关内容。

基于上文的介绍,可以在界面Activity中可以分别重写下边三个方法对用户的界面操作交互做出响应。

  • boolean onTouchEvent(MotionEvent event)

    在子视图没有处理的情况下,用户对硬件设备屏幕的每一个操作,都会回调一次该方法。


    其参数android.view.MotionEvent事件类的实例化对象event。

    event.getAction()方法可以获取当前事件行为,包括MotionEvent.ACTION_DOWN按下行为MotionEvent.ACTION_MOVE滑动行为MotionEvent.ACTION_UP抬起释放行为等。

    event.getX()方法获取当前操作的屏幕坐标x轴值。

    同理event.getY()方法获取当前操作的屏幕坐标y轴值。

  • boolean onKeyDown(int keyCode, KeyEvent event)

    在子视图没有处理的情况下,用户对硬件设备按键的每一次按下行为,都会回调一次该方法。


    参数一int类型的keyCode指定按键类型,一般其值与参数二event.getKeyCode()相等。

    参数二android.view.KeyEvent类的实例化对象event。

    event.getAction()方法同样可以获取当前事件行为,只有KeyEvent.ACTION_DOWN按下行为KeyEvent.ACTION_UP抬起释放行为两个行为值。

    event.getKeyCode()方法可以获取触发当前事件的按键类型,其值包括KeyEvent.KEYCODE_HOMEHOME键KeyEvent.KEYCODE_POWER电源键KEYCODE_VOLUME_UP音量增加键等。

  • boolean onKeyUp(int keyCode, KeyEvent event)

    在子视图没有处理的情况下,用户对硬件设备按键的每一次抬起释放行为,都会回调一次该方法。其两个参数与上述onKeyDown()中的两个参数类似。

视图响应

相对来说,界面内的视图响应要繁琐一些,而能实现的效果也更多样化。当把视图View作为用户的操作对象时,仍然可以重写上述界面响应的三个方法,但是系统视图往往也封装了一层更加简单粗暴的响应方法。

在视图中重写界面响应的三个方法后,如果返回的结果为true,则上文界面响应中的三个方法将不会被回调。

为什么需要封装一层响应方法呢?用户对视图的操作,往往就是点击(短时间内执行按下行为抬起释放行为),长按(在执行按下行为后等待一段时间再执行抬起释放行为),拖拽(在执行按下行为后执行一段滑动行为之后再执行抬起释放行为)这些固定操作类型。如果每个视图都要细分用户的操作行为,就会有大量冗余的操作类型判断代码,所以AndroidSDK定义了一系列接口分别对应用户的操作类型。视图如果需要响应某个操作,只需要设置其操作类型接口的实例化对象,并在该对象中实现相关方法即可。而这些接口主要有以下三个。

  • View.OnClickListener接口

    需要实现onClick(View view)方法,在该方法内响应响应视图View被用户点击后的代码逻辑。
  • View.OnLongClickListener接口

    需要实现onLongClick(View view)方法,在该方法内响应响应视图View被用户长按后的代码逻辑。
  • View.OnDragListener接口

    需要实现onDrag(View v, DragEvent event)方法,在该方法内响应视图View被用户拖拽后的代码逻辑。

另外,不同的系统视图也可能有单独设置的响应方法,或者自定义视图也会提供单独的响应方法,例如列表视图中的某一行数据被单独点击后如何响应,这些都要根据具体的视图类查找并使用对应的响应方法,这里不再赘述。

事件传递机制

在上文界面响应的三个方法中,关于他们被回调的时机,有个前提是子视图没有处理,即子视图的界面响应方法返回结果为false。这就涉及到Android系统的事件传递机制了。

我们知道界面Activity在创建之后会调用setContentView(int layoutId)加载根视图View,而根视图里边则可以内嵌一层层的子视图。那么,如果用户将手指触摸到屏幕上,会触发按下行为,该行为作为事件首先传递到根视图中,之后根视图再将该事件传递给子视图,子视图再将该事件传递给子视图的子视图,这样按照加载时的嵌套顺序一层层传递事件,称之为事件分发

直到该事件传递到最后一层子视图,或者某一层视图不再继续传递该事件,那么该事件将在最后传递到的这层视图中被首先处理。而每层视图在收到传递进来的事件后,都有两条路可以选择,要么将该事件继续传递给子视图,要么自己处理该事件,如果选择第二条路不再继续传递子视图而是自己处理该事件,称之为事件拦截

一旦某层视图处理了该事件,那么其父层视图将继续处理该事件,之后是父层的父层视图处理该事件,事件被这样一层层处理,直到根视图处理该事件结束,称之为事件处理

在经历了事件分发事件处理之后,这样的一个事件传递机制就算完成了。而上文提到的每一个事件,都是如此。

上述过程在代码中的实现,只需要针对事件分发事件拦截事件处理分别定义一个可重写的方法即可。能够重写该方法的位置主要是android.app.Acitivtyandroid.view.View中,由于事件拦截只会发生在子视图的传递过程中,在界面中并不需要,所以事件拦截对应的方法只在android.view.GroupView中重写。

  • boolean dispatchTouchEvent (MotionEvent event)

    当某个事件被分发到该视图时,系统回调视图中的该方法。返回结果表示当前事件是否被处理。
  • boolean onInterceptTouchEvent(MotionEvent event)

    当某个事件被分发到该视图后,系统会回调视图中的该方法,根据其返回结果判断是否拦截该事件交由当前视图处理。默认返回结果为false,表示不拦截该事件,将会继续回调子视图的dispatchTouchEvent()。返回结果为true时,表示拦截该事件,将会回调当前视图的onTouchEvent().
  • boolean onTouchEvent (MotionEvent event)

    当某个事件轮到该视图被处理时,系统会回调视图中的该方法。返回结果表示当前事件是否被处理。

界面间交互

上文介绍了针对一个界面Activity的交互响应,那么两个界面Activity之间如何交互呢?这就用到在加载界面一文中启动Activity所使用的android.content.Intent意图类了。不同于用户与界面的交互,界面间交互主要是变量数据的共享,所以通过Intent支持的交互数据类型是有限的。

发送数据界面

在启动一个界面Activity之前要先创建意图对象,在该意图对象调用putExtras(Bundle bundle)方法,可以将要发送的数据打包成android.os.Bundle类型的实例存入。

而该Bundle对象可以存储的数据类型支持包括booleancharbyteshortintfloatdoublelong八种基本数据类型,String类型和实现Parcelable接口的任意类型,及其[]数组或ArrayList数组,和其他一些不常用类型。这些数据都是以key-value键值对的形式保存在Bundle对象中。对于要保存的不同数据类型,分别调用对应的putT(String key, T value)系列方法即可以参数一key和参数二value的形式存入,同样可以调用对应的getT(String key)系列方法取出指定参数一key对应的value数据,这里的T泛指支持的不同数据类型。

另外也可以在创建的意图对象中直接调用putExtra(String key, T value)系列方法,将要发送的数据直接以key-value键值对的形式存入,同样也可以使用getTExtra(String key)系列方法取出指定参数一key对应的value数据,这里的T同样泛指Bundle可支持的不同数据类型。

在打包所有的数据后,就可以在当前界面Activity中继续调用startActivity(Intent intent)系列方法启动Intent意图参数中指定的另一界面Activity了。

这里的startActivity(Intent)方法是最简单的启动方法,另外还有startActivity(Intent, Bundle)在启动时将要发送的数据打包作为参数二传入。

或者startActivityForResult(Intent intent, int requestCode)在启动时传入一个唯一值作为参数二,以区分启动不同界面的意图,在启动的界面Activity返回后,系统会调用当前界面Activity中的onActivityResult(int requestCode, int resultCode, Intent data)方法,因此可以重写该方法。并根据参数一的唯一性对之前启动的不同界面意图做区分处理。参数二是根据启动界面不同关闭状态所返回的结果值,默认为android.app.Activity.RESULT_CANCELED,另外也可以为android.app.Activity.RESULT_FIRST_USERandroid.app.Activity.RESULT_OK,其值需要在启动界面返回时设置。参数三是从启动界面返回的Intent类型,主要使用其中的Bundle打包数据类型对象,同样其值可以在启动界面返回时设置。

接收数据界面

作为接收数据的启动界面Activity,在其绑定上下文环境之后,一般是在onCreate(Bundle savedInstanceState)方法中,可以使用getIntent()方法获取传递进来的Intent意图对象,获取该对象之后自然就可以通过getBExtras()或一系列getTExtra(String key)获取到打包的数据,这样在启动界面中就可以使用在启动之前上一个界面Activtiy中的变量数据了。

而当启动界面Activity在被用户操作返回时,系统将回调该启动界面的onBackPressed()方法,之后将该Activity从栈中移出并销毁。所以可以重写onBackPressed()方法,在该方法中调用setResult(int resultCode, Intent data)设置上文提到的返回时参数。

或者在启动界面Activity代码中也可以主动调用finish()方法,以关闭当前界面。因此在调用finish()方法之前先调用setResult(int resultCode, Intent data)设置返回参数即可。

Android系统编程入门系列之界面Activity交互响应的更多相关文章

  1. Android系统编程入门系列之界面Activity绘制展示

    上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...

  2. Android系统编程入门系列之界面Activity响应丝滑的传统动画

    上篇文章介绍了应用程序内对用户操作响应的相关方法位置,简单的响应逻辑可以是从一个界面Activity跳转到另一个界面Activity,也可以是某些视图View的相对变化.然而不管是启动一个界面执行新界 ...

  3. Android系统编程入门系列之界面Activity响应多元的属性动画

    在响应丝滑动画一篇文章中,分别介绍了作用于普通视图.绘制视图的绘制对象.和界面这三种对象的动画效果,但是都有一些使用的局限性.比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的 ...

  4. Android系统编程入门系列之加载界面Activity

    上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...

  5. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  6. Android系统编程入门系列之应用内键值对数据的简单保存

    在应用程序间及与用户的通信交互过程中,会产生并传递一系列数据.针对这些数据,有部分是只在应用程序中使用的缓存数据,还有一部分是在不同位置多次或长时间使用的持久化数据. 对于缓存数据来说,通常以代码中定 ...

  7. Android系统编程入门系列之硬件交互——多媒体摄像头

    多媒体系列硬件 多媒体包括图片.动画.音频.视频,这些多媒体素材的采集(输入)主要依靠摄像头和麦克风等硬件设备转化为基础数据,而他们的播放渲染(输出),则需要依靠具有相关功能的编解码软件.当然随着硬件 ...

  8. Android系统编程入门系列之加载服务Service

    之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始 ...

  9. Android系统编程入门系列之服务Service齐头并进多线程任务

    在上篇文章中初步了解了Android系统的四大组件之一的服务Service,在服务内可以执行无用户交互的耗时操作任务,但是包括之前关于界面系列文章在内,生命周期方法都是在主线程内被系统回调的.如果直接 ...

随机推荐

  1. 痞子衡嵌入式:关于i.MXRT中FlexSPI外设lookupTable里配置Normal read的一个小误区

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT中FlexSPI外设lookupTable里配置Normal read的一个小误区. 关于串行四线NOR Flash,当其作 ...

  2. Sparse R-CNN: End-to-End Object Detection with Learnable Proposals 论文解读

    前言 事实上,Sparse R-CNN 很多地方是借鉴了去年 Facebook 发布的 DETR,当时应该也算是惊艳众人.其有两点: 无需 nms 进行端到端的目标检测 将 NLP 中的 Transf ...

  3. GO学习-(14) Go语言基础之接口

    Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类 ...

  4. Go语言网络通信---tcp群发消息

    server package main import ( "fmt" "net" "os" "time" ) func ...

  5. jupyter notebook快捷键使用的注意点

    来源:https://zhidao.baidu.com/question/1800695798976401387.html 本文做进一步的阐释: 1.使行出现,但是光标要点击到有line空白区域 直接 ...

  6. A100计算能力

    A100计算能力 A100 GPU支持新的计算功能8.0.表1比较了NVIDIA GPU架构的不同计算功能的参数. 表1.计算能力:GP100 vs. GV100 vs. GA100. MIG架构 尽 ...

  7. 【NX二次开发】Block UI 微定位

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...

  8. 【VBA】字符串处理

    InStr 函数:查找字符串 1 Sub InStr函数() 2 Dim strTemp As String 3 strTemp = "=AAA=BBB=C" 4 Debug.Pr ...

  9. docker基本底层原理

    docker是怎么工作的 Docker是一个Client-Server结构的系统,Docker的守护进程运行在主机上,通过Socket客户端进行访问 DockerServer接收到DockerClie ...

  10. Java基础面试题(史上最全、持续更新、吐血推荐)

    文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...