Input Events(输入事件)

输入事件

在Android中有多种方法可以用来拦截用户与应用交互产生的事件。如果要处理用户交互触发的事件,比较合适的方式是在用户交互的特定View对象中捕获事件。View类提供了处理的方法。

在用来构建布局的各种View类中,你可能注意到一些看起来可用于UI事件的公共回调方法。当这些对象上产生各自的行为时,Android框架就会调用相应的回调方法。举例来说,当View(比如按钮)被触碰时,那么这个对象上的onTouchEvent()方法就会被调用。不管怎样,为了拦截这个事件,你必须扩展这个类并且重写这个方法。然而,为了处理这样的事件就扩展每种View对象是不切实际的。这就是为什么View类还包含一些可以很方便定义回调方法的嵌套接口的原因。这些被称为 event listeners 的接口是你捕获用户交互产生事件的关键。

虽然大多数情况下都是使用事件监听器来监听用户交互,但是有时候为了构建自定义组件,也需要去扩展View类。可能你想要扩展 Button 类来使它功能更强大。在这种情况下,你就可以使用 event handlers 为该类定义默认的事件行为。

事件监听器


事件监听器是View类中包含单一回调方法的接口。为View注册好监听器后,用户通过与UI中的控件交互会触发监听器,这时Android框架就会调用这些方法。

事件监听器接口中包含以下回调方法:

onClick()
来自  View.OnClickListener。当用户触摸这个控件(在触摸模式下),或者通过导航键/轨迹球将焦点移动到这个控件后再按下相应的“enter”键或按下轨迹球时,该方法被调用。
onLongClick()
来自  View.OnLongClickListener。当用户触摸并按住这个控件(在触摸模式下),或者通过导航键/轨迹球将焦点移动到这个控件再按住相应的“enter”键或按住轨迹球时(保持一秒钟),该方法被调用。
onFocusChange()
来自  View.OnFocusChangeListener。当用户使用导航键或轨迹球导航到或离开这个控件时,该方法被调用。
onKey()
来自  View.OnKeyListener。当用户把焦点放在这个控件上并按下或释放设备上的物理键时,该方法被调用。
onTouch()
来自  View.OnTouchListener。当用户执行具备触摸事件的操作时,包括按下,释放或在屏幕上的任何手势操作(该控件所在的区域内),该方法被调用。
onCreateContextMenu()
来自  View.OnCreateContextMenuListener。当上下文菜单被创建时(长按操作的结果),该方法被调用。请查阅  Menus 开发指南中关于上下文菜单的讨论。

这些方法是它们各自接口的唯一成员。如果要定义这些方法来处理事件,就要在Activity中实现这些嵌套的接口,或者定义一个匿名类。然后,把接口实现的实例传递给对应的View.set...Listener()方法。(例如,实现 OnClickListener 接口并作为参数传递给 setOnClickListener() 调用) 

下面的例子演示了如何为按钮注册on-click监听器。

// 创建OnClickListener的匿名实现
private OnClickListener mCorkyListener = new OnClickListener() {
    public void onClick(View v) {
      // 按钮被点击时执行
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // 从布局文件中获取按钮
    Button button = (Button)findViewById(R.id.corky);
    // 使用上面的实现注册onClick监听器
    button.setOnClickListener(mCorkyListener);
    ...
}

可能你也发现了把OnClickListener作为Activity的一部分来实现会更方便。这样可以避免产生额外的类加载负担和对象内存分配。例如:

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // 实现OnClickListener回调
    public void onClick(View v) {
      // 按钮按下时执行
    }
    ...
}

注意上面事例中的 onClick() 回调没有返回值,但是有些其他事件监听器方法必须返回一个布尔值。原因就在于对应的事件。以下是一些情况说明:

  • onLongClick() - 这个方法返回一个布尔值,用来指明你是否已经处理了这个事件且不会被进一步传递。也就是说,返回true表示你已经处理了这个事件,事件传递到此为止;返回false表示你还没有处理这个事件,这个事件会继续传递给其他on-click监听器。
  • onKey() - 这个方法返回一个布尔值,用来指明你是否已经处理了这个事件且不会被进一步传递。也就是说,返回true表示你已经处理了这个事件,事件传递到此为止;返回false表示你还没有处理这个事件,这个事件会继续传递给其他on-key监听器。
  • onTouch() - 这个方法返回一个布尔值,用来指明你的监听器是否已处理了这个事件。重要的是这个事件可以包含彼此跟随的多重操作。因此,如果在接收到按下操作事件时返回false,就表明你不会处理这个事件并且也不会对这个事件的后续操作感兴趣了。如此一来,这个事件里的任何操作都不会调用这个方法了,比如手势动作或最后松开操作事件。

请记住硬件按键事件总是会传递给当前焦点所在的View。它们会从View层次结构的顶部开始向下派发,直到抵达合适的目的地。如果你的View(或View的子节点)当前获得了焦点,那么你可以通过 dispatchKeyEvent() 方法查看事件的派发路线。通过View捕获按键事件的另外一种选择是,你也可以在Activity里通过 onKeyDown() 和 onKeyUp() 接收所有事件。

另外,在为你的应用考虑文本输入时,请注意许多设备只有软键盘输入法。这些输入法不要求是实体按键的;有些可能使用语音输入,手写输入等等。尽管输入法是以实体键盘外观的形式展示给用户的,通常它会触发 onKeyDown() 一类的事件。你绝对不要构建需要按下特定按键来控制的UI,除非你想要限制你的应用只能在具备物理键盘的设备上使用。不要依赖这些方法来验证输入,尤其是在用户按下返回键时;而应该使用类似  IME_ACTION_DONE 的操作去提示输入法你的应用期望的反应,这样就能用一个有意义的方式改变它的UI了。不用去猜测软键盘输入法的工作方式,只需信任它能够为你的应用提供格式化的文本。

注解:Android首先会调用事件处理程序,然后再调用类定义的合适的默认处理程序。正因如此,如果这些事件监听器返回true,那么将会中止事件向其他事件监听器的传播,并且也会阻止View中的默认事件处理程序的回调调用。所以当你确定要中止这个事件时才返回true。

事件处理程序


如果你要在View的基础上构建一个自定义组件,那么你可以定义一些回调方法用作默认的事件处理程序。在关于 Custom Components

另外还有一些其他你需要注意的方法,它们不属于View类,但是能够直接影响你处理事件的方式。所以,在布局中管理更复杂的事件时,可以考虑下这些方法:

触摸模式


当用户使用方向键或轨迹球导航到用户界面上时,很有必要把焦点给可交互的控件(比如按钮)以便用户可以看到哪些控件可以接受输入。然而,如果这个设备支持触摸并且用户通过触摸来与界面交互,那么就没必要高亮这些控件或把焦点交给特定的Viewe。因此,就有个名为“触摸模式”的交互模式了。

对于一个支持触摸的设备,一旦用户触摸了屏幕,设备就会进入触摸模式。从这点看来,只要View的 isFocusableInTouchMode() 返回true,那它就可以获得焦点,比如文本编辑组件。其他可触摸的View,比如按钮,在被触摸时不会获得焦点;它们仅仅是在被按下时触发它们的on-click监听器。

一旦用户点击方向键或滚动轨迹球时,设备就会退出触摸模式并寻找一个View来获取焦点。现在用户在不触摸屏幕的情况下继续与用户界面交互。

触摸模式的状态是由整个系统维护的(所有的window和activity)。想要查询当前设备的触摸模式状态,你可以调用 isInTouchMode() 来查看设备当前是否处于触摸模式。

处理焦点


Android框架会处理常规焦点的移动来响应用户的输入。包含View被删除或隐藏,甚至新的View变得可见时的焦点变化。View通过 isFocusable() 方法来表明它们是否愿意获得焦点。调用 setFocusable() 可以设置View能否获得焦点。在触摸模式下,你还可以通过 isFocusableInTouchMode() 查询View是否能获取焦点。同样你可以使用 setFocusableInTouchMode() 改变设置。

焦点的移动是基于在给定的方向上查找最近的邻居的算法。在某些很少见的情况下,这种默认的算法可能无法匹配开发者期望的结果。在这种情况下,你可以在布局文件中使用下面这些XML属性来提供精确的选择:nextFocusDownnextFocusLeftnextFocusRightnextFocusUp。给要失去焦点的View添加上面属性中的一种,在属性值中指定将要获取焦点的View的id。例如:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

通常,在这样的垂直不居中,从第一个按钮向上导航不会到达任何地方,同样从第二个按钮向下导航也不会到达任何地方。现在top按钮已经定义了bottom作为它的nextFocusUp(反之亦然),这样导航焦点就会在两个按钮之间循环了。

如果你想要在UI中声明View是可获得焦点的(通常不这样做),就需要在布局声明文件中给这个View添加android:focusable属性并设置为true。你也可以使用android:focusableInTouchMode声明View在触摸模式下是可获得焦点的。

使用 requestFocus() 可以让指定的View获得焦点。

如同上面 Event Listeners 章节中讨论的,使用 onFocusChange() 来监听焦点事件(当View获得或失去焦点时收到通知)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值