疯狂android讲义(第三版)——第三章:Android的事件处理

android的事件处理---为用户动作提供响应的机制(基于监听和回调两种)

监听---为组件绑定事件监听器;某些特定的事件。---委托式
    onClick属性指定事件监听方法---Activity中定义事件监听方法(view类型的形参)
    监听的处理模型---外部动作激发一个事件,该事件触发事件源上注册的事件监听器,事件监听器调用对应的事件处理器做出相应的响应。
        Event Source(事件源):事件发生的场所,通常就是各个组件。
        Event(事件):事件封装了界面组件上发生的特定事情。
        Event Listener(事件监听器):负责监听事件源所发生的事件,并对各种事件作出相应的响应。
        实现步骤:
            获取事件源,也就是被监听的对象
            实现事件监听器类,该监听器类是一个特殊的Java类,必须实现XxxListener接口
            调用事件源的setXxxListener方法将事件监听器对象和注册给事件源
        规则:
            事件源:任何组件都可以作为事件源
            事件监听器:关键是实现处理器方法
            注册监听器:调用事件源的setXxxListener(XxxListener)即可


    事件和事件监听器
        如果事件发生时有比较多的信息需要传给事件监听器,就需要将事件信息封装成Event对象,并作为参数传入事件处理函数。
          

  planeView.setOnKeyListener(new OnKeyListener)(){
                public boolean onKey(View source,int keyCode,KeyEvent event)
                {
                    switch(event.getKeyCode())
                    {
                        case KeyEvent.KEYCODE_S:
                            planeView.currentY+=speed;
                            break;
                        case ...
                        planeView.invalidate();
                        returen true;
                    }
                }
            });


        View包含如下内部接口:
            View.OnClickListener
            View.OnCreateContextMenuListener
            View.onFocusChangeListener
            View.OnKeyListener
            View.OnLongClickListener
            View.OnTouchListener


        所谓事件监听器就是实现了特定接口的Java类的实例,通常有如下几种形式:
            内部类
            外部类
            Activity本身作为事件监听器类
            匿名内部类
    内部类---当前类中复用,自由访问外部类的所有界面组件
    外部类---被多个GUI界面共享,只要是完成某种业务逻辑的实现。
        实际上不推荐将业务逻辑实现写在监听器中,包含业务逻辑的事件监听器将导致程序的显示逻辑和业务逻辑耦合,增加后续维护难度。
        如果确实有多个事件监听器需要实现想用的业务逻辑功能,可以考虑使用业务逻辑组件来定义业务逻辑功能,再让事件监听器来调用业务逻辑组件的业务逻辑方法
    Activity本身作为事件监听器类--简洁
        直接使用this作为事件监听器对象即可
        缺点:
            可能造成程序结构混乱
            感觉比较怪异
    匿名内部类---可复用代码通常都被抽象成业务逻辑方法,因事件处理器都没有复用价值,大部分只是临时使用一次,故使用匿名内部类更合适。
        new 监听器接口      ||  new 事件适配器
        缺点是语法不易掌握
    直接绑定到标签
        android:onClick
        void xxx(View source)


回调---重写Android组件特定的回调方法或Android回调方法;具有通用性的事件。
    回调机制与监听机制
        基于监听的事件处理模型:事件源和事件监听器是分离的,事件源发生特定事件时,该事件交给事件监听器处理;
        基于回调的事件处理模型:事件源和事件监听器是统一的,当事件源发生特定事件时,该事件还是有事件源本身负责处理。可通过自定义View来实现,自定义View时重写该View的事件处理方法即可
        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)


    基于回调的事件传播
        基于回调的事件处理方法的Boolean类型的返回值,用于标识该处理方法是否能完全处理该事件
            true:已完全处理该事件,该事件不会传播出去
            false:并未完全处理该事件,该事件会传播
        某组件上的所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity
        Android系统最先触发的是绑定的事件监听器,然后才触发该组件提供的事件回调方法,最后还会传播到该组件所在的Activity——但如果任何一个事件处理方法返回了true,那么该事件将不会向外传播。
    重写onTouchEvent方法
        Android提供的两种事件处理模型:监听具有更大的优势
            分工明确,更好的可维护性
            优先触发
        某些特定情况下,基于回调的事件处理机制会更好的提高程序的内聚性
            通过为View提供事件回调方法,可以很好地把事件处理方法封装在该View内部,从而提高程序的内聚性——基于回调的事件处理更适合应付那种事件处理逻辑比较固定的View


响应系统设置
    Configuration类
        专门用于描述设备上的配置信息
        Configuration cfg = getResources().getConfiguration();
        属性
            public float fontScale
            public int keyboard
                KEYBOARD_NOKEYS
                KEYBOARD_QWERTY
                KEYBOARD_12KEY
            public int keyboardHidden
                KEYBOARDHIDDEN_NO
                KEYBOARDHIDDEN_YES
            public Locale locale
            public int mcc
            public int mnc
            public int navigation
                NAVIGATION_NONAV
                NAVIGATION_DPAD
                NAVIGATION_TRACKBALL
                NAVIGATION_WHEEL
            public int orientation
                ORIENTATION_LANDSCAPE
                ORIENTATION_PORTRAIT
                ORIENTATION_SQUARE
            public int touchscreen
                TOUCHSCREEN_NOTOUCH
                TOUCHSCREEN_STYLUS
                TOUCHSCREEN_FINGER
        获取系统的Configuration对象
        调用Configuration对象的属性来获取设备状态


    onConfigurationChanged
        监听系统设置的更改,重写onConfuguration(Configuration newConfig),该方法是一个基于回调的事件处理方法
        动态的更改系统设置,可调用Activity的setRequestedOrientation(int)方法来修改屏幕方向
        监听屏幕方向的改变:
            MaInActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            onConfiguration(Configuration newConfig),屏幕发生改变时自动回调。
            Activity指定android:configChanges属性
                android:configChanges="orientation|screenSize"---指定该Activity可以监听屏幕方向改变事件。


Handler消息传递机制---只允许UI线程修改Activity的UI组件,程序第一次启动时,Android会同时启动一条主线程(Main Thread),主要负责处理与UI相关的事件,所以主线程也叫UI线程。
    Handler类
        作用:
            在新启动的线程中发送消息
            在主线程中获取、处理消息
        方法:
            void handleMessage(Message msg)
            final boolean hasMessages(int what)
            final boolean hasMeaasges(int what,Object object)
            多个重载的Message obtainMessage()
            sendEmptyMessage(int what)
            final boolean sendEmptyMessageDelayed(int what,long delayMillis)
            final boolean sendMesssge(Message msg)
            final boolean sendMessageDelayed(Message msg,long delayMillis)
        java,util.Timer:Timer对象可调度TimerTask对象,TimerTask对象的本质就是启动一条新线程


    Handler、Loop、MessageQueue
        Meaasge:Handler接收和处理的消息对象
        Looper:每个线程只能拥有一个Looper,负责管理MessageQueue,他的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给发送该消息的Handler进行处理
        MessageQueue:消息队列,有Looper负责管理,采用先进先出的方式管理Message。程序在初始化时会创建一个与之关联的MessageQueue,这个MessageQueue就负责管理消息。
        Handler: 发送消息和处理消息。能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
            如果希望Handler正常工作,必须在当前线程中有一个Looper对象
                在主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过handler来发送、处理消息了。
                程序员启动的子线程,必须自己创建一个Looper对象,并启动它。创建Looper对象调用它的prepare()方法即可
                    prepare()方法保证每个线程最多只有一个Looper对象
                    调用Looper的静态loop()方法来启动它。loop()方法是一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。
        在线程中使用Handler的步骤:
            调用Looper的perpare()
            创建Handler子类,重写handleMessage()方法
            调用Looper的loop()方法启动Looper
    ANR:只要在UI线程中执行耗时操作,都会引发ANR,因为这会导致Android应用无法响应输入事件和Broadcast。


AsyncTask---实现线程通讯
    解决子线程不能更新UI组件的问题,Android提供了如下方法:
        使用Handler实现线程间通信
        Activity.runOnUiThread(Runnable)
        View.post(Runnable)
        View.postDelayed(Runnable,long)
    AsyncTask适用于简单的异步处理,不需要借助线程和Handler即可实现
        AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承,需要指定三个泛型参数:
            Params:后台任务执行的输入参数的类型
            Progress:后台任务完成的进度值的类型
            Result:后台执行任务完成后的返回结果的类型


    使用AsyncTask
        创建AsyncTask的子类,并为三个泛型参数指定类型。
        实现AsyncTask的方法:
            doInBackground(Params...):后台线程将要完成的任务。可以调用publishProgress(Progress... Values)方法更新任务的执行进度
            onProgressUpdate(Progress... Values):在doInBackground(Params...)方法中调用publishProgress(Progress... Values)方法更新任务的执行进度后,将会触发该方法。
            onPreExecute():执行后台耗时操作前被调用。初始化的准备工作,例在界面显示进度条等。
            onPostExecute():当doInBackground()完成后,自动调用该方法,并将oInBackground()返回值传给该方法。
        调用AsyncTask子类的实例的execute(Params... params)开始执行耗时任务。
    使用AsyncTask必须遵循如下规则:
        必须在UI线程中创建AsyncTask的实例
        必须在UI线程中调用AsyncTask的execute()方法
        AsyncTask的方法是由Android系统负责调用
        每个AsyncTask只能执行一次,多次调用将会引发异常

 

小结:

第三章的主要内容是响应用户操作,响应的动作就是事件处理来完成。Android的事件处理机制:基于回调和基于响应;前者是需要掌握处理模式以及对不同事件对应的监听器接口,而后者则要掌握不同事件对应的回调方法。重写onConfigurationChanged方法,来响应系统的更改。借助Handler对象来实现子线程更新UI。Handler、Looper、MessageQueue之间的关系及工作原理。

 

生活感悟:

  • 总会在某些时刻你会觉得共同语言是多么重要,从而让你感慨门当户对,共同的价值观在很多时候还是很重要的。
     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值