zoukankan      html  css  js  c++  java
  • Android学习笔记-事件处理

    第三章 Android的事件处理
    Android提供两种事件处理方式,基于回调和基于监听器。前者常用于传统图形界面编程中,而后者在AWT/Swing开发中常用。
    3.1 事件处理概述
    对于基于回调的事件处理而言,主要是重写Android组件特定的回调方法,或者重写Activity的回调方法,一般用于处理通用性的事件。
    对于监听的事件处理而言,主要是为Android界面组件绑定特定的事件监听器。
    3.2 基于监听的事件处理
    事件响应的动作通常以方法的形式组织,但Java是面向对象的编程语言,必须用类把这些方法组织起来,所以事件监听器的核心就是这些方法——事件处理器(Event Handler)。普通Java方法是由程序员调用,而事件处理器方法由系统调用。
    事件监听器是特殊的Java对象,注册在事件源上,当用户触发事件时,就会调用事件处理器来响应。
    委派式的事件处理:每个组件可以针对不同的事件注册不同的事件监听器,而每个事件监听器可以监听一个或者多个事件源。
    1. 使用内部类

    // 获取按钮,为按钮注册监听器,在onCreate方法中
    Button btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new MyClickListener());
    // 定义监听器,内部类
    class MyClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            // 事件处理
        }
    }
    

    编程步骤:
    获取界面组件(事件源);
    实现监听器类(编程重点),实现XxxListener接口;
    setXxxListener方法将事件监听器对象注册给事件源。
    在触发事件后,事件会作为参数传递给事件监听器。但在上面的代码中,并没有出现事件的踪迹,原因是,当事件足够简单,事件中封装的信息比较有限,就无须封装事件对象了。但对于键盘事件,触摸屏事件等,就需要详细的信息。Android将其封装成XxxEvent对象。

    // 用了匿名内部类
    planeView.setOnKeyListener(new OnKeyListener() {
        @override
        public boolean onKey(View source, int keyCode, KeyEvent event){
            switch(event.getKeyCode()){
                // 获取由哪个键触发的事件,并处理
            }
            return true;
        }
    });
    

    常用的几个接口:

    View.OnClickListener // 单击事件
    View.OnCreateContextMenuListener // 创建上下文菜单
    View.OnFocusChangeListener // 焦点改变
    View.OnKeyListener // 按键
    View.OnLongClickListener // 长按
    View.OnTouchListener // 触摸屏
    

    使用内部类的好处是:可以在当前类中复用该监听器类;内部类也可以自由访问外部类的界面组件。
    2. 使用外部类
    如果某个事件监听器需要被多个GUI界面共享,且主要是完成某种业务逻辑的实现,则可以定义为外部类。
    但并不推荐将业务逻辑写在事件监听器中,包含业务逻辑的事件监听器将导致程序的显示逻辑与业务逻辑耦合。如果确实有多个监听器需要实现相同的业务逻辑方法,可以考虑使用业务逻辑组件来定义业务逻辑功能,再让事件监听器来调用其方法。

    // SendSms类onCreate方法中
    btn.setOnLongClickListener(new SendSmsListener(this, address, content));
    // SendSmsListener类中
    private Activity act;
    private EditText address, content;
    public SendSmsListener(Activity act, EditText address, EditText context) {
        this.act = act;
        this.address = address;
        this.content = content;
    }
    @Override
    public boolean onLongClick(View source) {
        // 事件处理代码
        return true;
    }
    

    3. Activity本身作为事件监听器
    缺点如下:
    Activity主要是完成界面初始化工作,如果还需包含事件处理方法,会引起程序结构的混乱;
    Activity界面类不该实现监听器接口。

    // onCreate方法中
    btn.setOnClickListener(this);
    // 实现事件处理方法
    @Override
    public void onClick(View v) {
        // 事件处理
    }
    

    4. 使用匿名内部类
    一般来说,事件处理器没有什么复用价值,可复用的代码都被抽象成了业务逻辑方法了,所以应该使用匿名内部类,这是最好的方法。
    示例代码在上面内部类部分已经给出。直接new 监听器接口,或者new 事件适配器。
    5. 直接绑定到标签
    另一种简单的方法是直接在布局文件中指定事件处理方法。

    // xml文件中
    android:onClick="clickHandler"
    // Activity类中
    public void clckHandler(View source) {
        // 事件处理
    }
    

    3.3 基于回调的事件处理
    回调机制中事件源和事件监听器是统一的,组件自己负责处理该事件,可以提高程序的内聚性。
    继承组件类,并重写该类的事件处理方法。常用的有:

    // 简单起见,省略了返回值和参数
    onKeyDown() // 按键
    onKeyLongPress() // 长按
    onKeyShortcut() // 键盘快捷键
    onKeyUp() // 松开按键
    onTouchEvent() // 触摸屏
    onTrackballEvent() // 轨迹球屏
    

    代码示例:

    // 自定义View
    public class MyButton extends Button {
        // 构造方法略
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            super.onKeyDown(keyCode, event);
            // 事件处理
            return true;
        }
    }
    // xml文件中,各种属性略
    <org.crazyit.event.MyButton />
    

    事件传播
    基于回调的事件处理方法都有一个布尔返回值,该返回值标识该方法能否处理该事件,如果不能则返回false,事件将继续传播。
    先监听,后回调,最后到Activity
    当组件上发生某个按键事件时,首先触发的是该按键绑定的事件监听器(Listener),接着触发该组件提供的事件回调方法(自定义组件中重写的),然后还会传播到该组件所在的Activity
    Adnroid事件处理机制保证基于监听的事件监听器被优先触发,基于监听的事件模型中事件源与监听器分工明确,易于维护。
    回调模型更适合于处理那些逻辑比较固定的场景,不需要监听,组件自行处理。
    3.4 响应系统设置的事件
    Configuration可以用于获取用户特定的配置项,以及系统的动态设备配置。可用如下代码:

    Configuration cfg = getResources().getConfiguration();
    

    可以获取屏幕方向,触摸方式,导航设备等。
    监听系统设置的改变可用如下基于回调的方法:

    onConfigurationChanged(Configuration newConfig)
    

    动态更改屏幕方向,可以用setRequestedOrientation(int)方法。
    为了能监听系统设置的改变,在AndroidMenifest文件中配置Activityandroid:configChanges属性,各属性值之间用竖线隔开。
    如果targetSdkVersion设置超过12,则属性值应当为orientation|screenSize,因为转屏时屏幕大小也变了。
    3.5 Handler消息传递机制
    只允许主线程(UI线程)修改UI组件,其他线程借助Handler消息传递机制来实现对界面组件的修改。
    通过回调的方式:开发者重写Handler中处理消息的方法,新线程发送消息到MessageQueue,而Handler不断从中获取并处理消息。Handler类中处理消息的方法被回调。

    void handleMessage(Message msg) // 处理消息,用于被重写
    final boolean hasMessage(int what) // 检查消息队列中有无指定what属性的消息
    final boolean hasMessage(int what, Object obj) // 检查消息队列中object属性为指定对象的消息
    Message obtainMessage() // 多个重载方法,获取消息
    sendEmptyMessage(int what) // 发送空消息
    final boolean sendMessage(Message msg) // 立即发送消息
    final boolean sendEmptyMessageDelayed(int what, long delayMillis) // 指定多少毫秒后发送空消息
    final boolean sendMessageDelayed(Message msg, long delayMillis) // 指定多少毫秒后发送消息
    

    示例代码如下:

    final Handler myHandler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x1233) {
                // do something
            }
        }
    };
    new Timer().schedule(new TimerTask()
    {
        @Override
        public void run() {
            myHandler.sendEmptyMessage(0x1233);
        }
    }, 0, 1200);
    

    当新线程发送消息时,handleMessage方法被自动回调。
    UI线程中,系统已经初始化了一个Looper对象,所以直接创建Handler即可,而在子线程中,需要程序员创建一个Looper对象,并调用其prepare()方法。
    使用Handler的步骤如下:
    调用prepare()方法为当前线程创建Looper对象,之后它的构造器会创建与之配套的MessageQueue
    创建Handler子类的实例,重写handleMessage方法;
    调用loop()方法启动Looper
    如果在UI线程中执行耗时操作,将导致ANR异常(20s),这会导致程序无法响应输入事件和Broadcast
    3.6 异步任务
    一种轻量级的解决方案,不需要借助线程和Handler即可实现。
    AsyncTask<Params, Progress, Result>是抽象类,其中参数:
    Params:启动任务执行的输入参数类型;
    Progress:后台任务完成的进度值类型;
    Result:返回结果的类型。
    实现步骤:
    第一步,创建AsyncTask的子类,并为三个泛型参数指定类型,如不需要指定,用Void
    第二步,根据需要,实现如下方法:
    doInBackground():后台将要执行的任务,该方法可以调用publishProgress()方法更新进度;
    onPrograssUpdate():调用PublishProgress()方法后将触发该方法;
    onPreExecute():一些初始化工作,如显示进度条等;
    onPostExecute():当doInBackground()完成后,将其返回值传给此方法。
    第三步,调用execute()开始执行耗时任务。
    第二步中的方法由系统调用,程序员只需重写。
    阅读学习材料:《疯狂Android讲义》(第二版)
    本章源代码地址:Github

  • 相关阅读:
    Bridage
    国内项目测试培训笔录和小结
    Proxy
    数据库设计
    PDF转Word
    机务维修成本技术点
    MyEclipse10
    MyEclips:Struts 2 + Hibernate 4 + SQL Server2008
    观察者模式
    javascript事件设计模式
  • 原文地址:https://www.cnblogs.com/yanqiang/p/5638311.html
Copyright © 2011-2022 走看看