事件相关概述

    |     2015年4月20日   |   Android UI界面   |     0 条评论   |    1486

图形界面开发有两件大事:控件布局,以及控件的事件处理。Android 的事件处理秉承了 Java SE 图形界面的风格,提供两套机制:基于监听,以及基于回调。

基于监听:为界面组件绑定特定的事件监听器。基于回调:重写组件的回调方法,或重写 Activity 的回调方法。


一、基于监听的事件处理

监听模型里主要有三类对象:

  • Event Source(事件源):事件发生的场所,通常是按钮、窗口、菜单等组件。
  • Event(事件):封装界面组件上发生的特定事情(通常是一次用户操作)。
  • Event Listener(事件监听器):监听事件源上的事件,并做出响应。

事件处理流程示意图:

21

内部类可以作为事件监听器类;如果某个监听器要被多个 GUI 界面共享,可以用外部类;还有一种是匿名内部类作为事件监听器类。


二、三种事件处理模型

通常有三种写法:接口实现、内部类、匿名内部类。下面都以响应 Button 单击、在 TextView 里弹出结果为例。通过 DroidDraw 设计的带 Button 布局如图 1;运行前后对比如图 2。

1. 接口实现

单击 Button 时,屏幕上的 TextView 输出「你好!」。实现接口处理事件,主要是完成 OnClickListener 的 onClick,并绑定到事件源。布局与 Activity 代码(同属原文第一个代码块):

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

         android:id="@+id/userlayout"

         android:layout_width="fill_parent"

         android:layout_height="fill_parent"

         android:orientation="vertical"

         xmlns:android="http://schemas.android.com/apk/res/android">

<TextView

         android:id="@+id/mytext"

         android:layout_width="165dp"

         android:layout_height="38dp"

         android:text="TextView" />

<Button

         android:id="@+id/mybutton"

         android:layout_width="172dp"

         android:layout_height="52dp"

         android:text="Button" />

</LinearLayout>

         以下代码为在Activity中实现接口,并且绑定在Button按钮上:

package com.wjy.usersrc;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

public class TestActivity extends Activity implements OnClickListener {

         @Override

         public void onCreate(Bundle savedInstanceState) {

                   super.onCreate(savedInstanceState);

                   setContentView(R.layout.main);

                   Button mybutton = (Button) findViewById(R.id.mybutton);

                   mybutton.setOnClickListener(this);

         }

         @Override

         public void onClick(View v) {

                   switch (v.getId()) {

                   case R.id.mybutton:

                            TextView mytext = (TextView) findViewById(R.id.mytext);

                            mytext.setText("你好!");

                   }

         }

}

配置文件 AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.wjy.usersrc"

    android:versionCode="1"

    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name" >

        <activity

            android:name=".TestActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

</manifest>

2. 内部类

布局和配置文件与上面相同,主要改造 Activity 里的事件处理。Clicklistener 也可以做成普通类,效果与内部类一样。原文内部类名为 Clicklistener,setOnClickListener 处写成了 clicklistener;switch 里写的是 R.id.mytext。下面按原文保留全部代码。

package com.wjy.usersrc;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

public class TestActivity extends Activity {

         TextView mytext = null;

         @Override

         public void onCreate(Bundle savedInstanceState) {

                   super.onCreate(savedInstanceState);

                   setContentView(R.layout.main);

                   Button mybutton = (Button) findViewById(R.id.mybutton);

                   mytext = (TextView) findViewById(R.id.mytext);

                   mybutton.setOnClickListener(new clicklistener());

         }

         class Clicklistener implements OnClickListener {

                   @Override

                   public void onClick(View v) {

                            switch (v.getId()) {

                            case R.id.mytext:

                                     mytext.setText("你好");

                            }

                   }

         }

}

3. 匿名内部类

官方源码里也很常见。布局和配置不变,只改 Activity 中的绑定代码。三种方式都是实现 onClick 并绑定到事件源:接口实现和内部类通过实现 OnClickListener;匿名内部类则直接覆盖 onClick。

package com.wjy.usersrc;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

public class TestActivity extends Activity {

         @Override

         public void onCreate(Bundle savedInstanceState) {

                   super.onCreate(savedInstanceState);

                   setContentView(R.layout.main);

                   Button mybutton = (Button) findViewById(R.id.mybutton);

                   final TextView mytext = (TextView) findViewById(R.id.mytext);

                   mybutton.setOnClickListener(new OnClickListener() {

                            @Override

                            public void onClick(View v) {

                                     switch (v.getId()) {

                                     case R.id.mytext:

                                               mytext.setText("你好!");

                                     }

                            }

                   });

         }

}

还有一种更简单的绑定方式:直接在布局里为标签写 android:onClick="clickHandler",对应 Activity 中定义 void clickHandler(View source) 处理单击。该方法必须是 Activity 的成员方法,不能写在 onCreate 里面(原文示例嵌套在 onCreate 中,按原样保留以便对照):

public class Ex003_01Activity extends Activity {

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        //定义一个事件的处理方法

        //轻重source参数代表事件源

        public void clickHandler(View source){

            TextView show=(TextView)findViewById(R.id.tv);

            show.setText("bn按钮被点击了");

        }

    }

}

三、基于回调的事件处理

从代码看,回调模型更简单。如果说监听是委托式,回调恰好相反:事件源与事件监听器是统一的。Java 是静态语言,无法给对象动态加方法,因此只能继承 GUI 组件类并重写事件处理方法。Android 为所有 GUI 组件提供了回调,以 View 为例:

  • boolean onKeyDown(int keyCode, KeyEvent event):按下某个键。
  • boolean onKeyLongPress(int keyCode, KeyEvent event):长按某个按钮。
  • boolean onKeyShortcut(int keyCode, KeyEvent event):快捷键事件。
  • boolean onKeyUp(int keyCode, KeyEvent event):松开某个按键。
  • boolean onTouchEvent(MotionEvent event):触摸屏事件。
  • boolean onTrackballEvent(MotionEvent event):轨迹球事件。

重写 Button 的 onKeyDown,负责处理按钮上的键盘事件:

public class MyButton extends Button

{

    public MyButton(Context context , AttributeSet set)

    {

        super(context , set);

        // TODO Auto-generated constructor stub

    }

    @Override

    public boolean onKeyDown(int keyCode, KeyEvent event)

    {

        super.onKeyDown(keyCode , event);

        Log.v("-crazyit.org-" , "the onKeyDown in MyButton");

        //返回true,表明该事件不会向外扩散

        return true;

    }

}

回调事件的传播

几乎所有基于回调的事件处理方法都有 boolean 返回值,标识是否完全处理该事件:

  • 返回 true:已完全处理,事件不会向外扩散。
  • 返回 false:未完全处理,事件会传播出去。

某组件上发生的事情不仅激发该组件的回调,也会触发该组件所在 Activity 的回调——只要事件能传播到该 Activity。

方式 核心做法 适用
接口实现 Activity implements OnClickListener,setOnClickListener(this) 多个控件共用一个监听器
内部类 / 外部类 独立 Listener 类 要被多个界面共享时用外部类
匿名内部类 new OnClickListener() { onClick } 官方源码里很常见
布局 onClick android:onClick 对应 Activity 方法 最简单的单击绑定
回调 继承组件并重写 onKeyDown / onTouchEvent 等 事件源与监听器合一

一句话总结:先给组件绑监听器(接口、内部类或匿名类),需要拦截按键或触摸时再重写回调,并用返回值控制事件是否继续传播。

转载请注明来源:事件相关概述
本文链接地址:https://ai.zhousir.top/?p=495
回复 取消