Android系统编程入门系列之界面Activity交互响应

上篇文章中已经了解到界面Activity的绘制彻底依赖其加载的视图组件View,不只如此,用户的每次触摸操做均可以在界面Activity内接收并响应,也能够直接传递给其中的某个视图View响应。那么对于用户的操做,应该如何响应,而同一个操做究竟是做用于界面,仍是界面中的某一个子视图?针对用户的操做对象所产生的交互方式不一样,本文将分别展开介绍。html

界面内交互

界面响应

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

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

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

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

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

  • 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轴值。spa

  • 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音量增长键等。code

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

视图响应

相对来讲,界面内的视图响应要繁琐一些,而能实现的效果也更多样化。当把视图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)设置返回参数便可。

相关文章
相关标签/搜索