其他分享
首页 > 其他分享> > Android 事件处理探险

Android 事件处理探险

作者:互联网

Android提供了两套事件处理机制:基于监听的事件处理;基于回调的事件处理

1.基于监听的事件处理

Android的事件处理是一种委派式事件处理方式(事件源将整个事件处理委托给事件监听器),事件监听的处理模型主要涉及:Event Source(事件源)、Event(事件)、Event Listener(事件监听器)。

小技巧:
requestWindowFeature(Window.FEATURE_NO_TITLE); // 去掉窗口标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); // 全屏显示

在基于监听的事件处理模型中,事件监听器必须实现事件监听器接口(通常以内部类的形式存在),以View为例,提供了如下几个接口

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

注意:不推荐将业务逻辑实现写在事件监听器中,包含业务逻辑的事件监听器将导致程序的显示逻辑与业务逻辑耦合,增加了后期维护难度。

2.基于回调的事件处理

Android事件处理的回调方法,以View为例,View类包含如下方法

boolean onKeyDown(int keyCode, KeyEvent event)    // 按下
boolean onKeyLongPress(int keyCode, KeyEvent event)    // 长按
boolean onKeyUp(int keyCode, KeyEvent event)    // 松开
boolean onKeyShortcut(int keyCode, KeyEvent event)        // 键盘快捷键触发时
boolean onTouchEvent(MotionEvent event)        // 触摸屏事件

基于回调的事件处理机制可通过自定义View来实现,自定义View时重写该View的事件处理方法即可。
对比Android提供了两套事件处理机制,基于监听的事件处理更有优势:可维护性高、保证监听的事件监听器会被优先触发。
基于回调的事件处理更适合于那些比较固定的View。

3.事件传递

所有基于回调的事件处理的回调方法返回true,表明已处理完成,不会继续传递;返回false,表明未处理完成,该事件继续传递下去。

Button bn = (Button) findViewById(R.id.bn);
// 为bn绑定事件监听器
bn.setOnKeyListener(new OnKeyListener() {
    @Override
    public boolean onKey(View source, int keyCode, KeyEvent event) {
        // 只处理按下键的事件
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            Log.v("-Listener-", "the onKeyDown in Listener");
        }
        return true;  // 返回true,表明该事件不会向外传递
    }
});

当某个键被按下时候,Android最先触发的是该按键上绑定的事件监听器,然后触发该组件提供的事件回调方法,最后传递到该组件所在的Activity。

4.响应系统设置的事件

1)Configuration类
专门用于描述手机设备上的配置信息
获取系统的Configuration对象:

Configuration cfg = getResources().getConfiguration();

2)重写onConfigurationChanged方法可以响应系统设置更改

5.Handler消息传递机制

Handler类的主要机制有两个:在子线程中发送消息;在主线程中获取处理消息。
Handler包含如下方法用于发送处理消息:

void handleMessage(Message msg):处理消息
final boolean hasMessages(int what, Object object):检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息
boolean sendEmptyMessage(int what):发送空消息
boolean sendEmptyMessageDelayed(int what, long delayMillis):延迟发送空消息
boolean sendMessage(Message msg):发送消息
boolean sendMessageDelayed(Message msg, long delayMillis):延迟发送消息

6.Handler、Looper、MessageQueue

Handler、Looper、MessageQueue各自作用如下:
Handler:能发送消息给MessageQueue,并能处理Looper分发给它的消息;
Looper:每个线程只有一个Looper,负责管理MessageQueue,从MessageQueue中取出消息分发给对应的Handler;
MessageQueue:采用先进先出管理Message;

注意:避免在主线程中执行耗时操作,否则会引发ANR异常。

public class MainActivity extends Activity {
    EditText etNum;
    ChildThread childThread;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // ...
        ChildThread = new ChildThread();
        childThread.start();
    }
    /**
     * 定义一个线程类
     */ 
    class ChildThread extends Thread {
        public Handler mHandler;
        public void run() {
            Looper.prepare(); // 创建Looper对象
            mHandler = new Handler() {
                // 定义处理消息的方法
                @Override
                public void handleMessage(Message msg) {
                    if(msg.what == 1) {
                        // 执行操作
                        // ...
                    }
                }
            };
            Looper.loop(); // 启动Looper
        }
    }
    /**
     * 按钮的点击事件
     */ 
    public void down(View view) {
        Message msg = new Message(); // 创建消息
        msg.what = 1;
        Bundle bundle = new Bundle();
        // num = ...
        bundle.putInt("num" ,num);
        msg.setData(bundle);
        childThread.mHandler.sendMessage(msg); // 向新线程中的Handler发送消息
    }
}

Android为了解决子线程不能更新UI组件的问题,已提供了如下解决方案:

使用Handler实现线程间通信
runOnUiThread方法
View.post方法
View.postDelayed方法

用法如下:

/**
 * 1.使用Handler实现线程间通信的用法
 */
public class MainActivity extends Activity {
    protected static final int MSG_ONE = 1;
    protected static final int MSG_TWO = 2;
    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ONE:
                    // 执行1的操作
                    break;
                case MSG_TWO:
                    // 执行2的操作
                    break;
            };
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 耗时操作要在子线程中操作
        new Thread() {
            public void run() {
                Message message = Message.obtain(); //获取消息的载体
                if (condition) {
                    message.what = MSG_ONE;
                } else {
                    message.what = MSG_TWO;
                }
                handler.sendMessage(message);
            };
        }.start();
    }
}

/**
 * 2.runOnUiThread方法的用法
 */
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 耗时操作要在子线程中操作
        new Thread() {
            public void run() {
                //执行耗时操作
                //更新主线程UI
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                    }
                });
            };
        }.start();
    }
}

7.异步加载(AsyncTask)

1)先自己手写一个异步加载框架,其中涵盖了异步加载框架核心原理,如下:

public abstract class MyAsycnTask {
    private Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            postTask();
        };
    };
    /**
     * 在子线程之前执行的操作
     */
    public abstract void preTask();
    /**
     * 在子线程之中执行的操作
     */
    public abstract void doinBack();
    /**
     * 在子线程之后执行的操作
     */
    public abstract void postTask();
    /**
     * 执行
     */
    public void execute(){
        preTask();
        new Thread(){
            public void run() {
                doinBack();
                handler.sendEmptyMessage(0);
            };
        }.start();
    }
}

调用手写异步加载框架

new MyAsycnTask() {
    @Override
    public void preTask() {
        //在子线程之前执行的操作
    }
    @Override
    public void postTask() {
        //在子线程之中执行的操作
    }
    @Override
    public void doinBack() {
        //在子线程之后执行的操作
    }
}.execute();

2)系统异步加载框架AsycnTask

系统异步加载框架AsycnTask三个参数:提高兼容性(如果某个泛型参数不需要指定类型,可将其指定为void)
参数1:子线程执行所需的参数;参数2:显示当前的加载进度;参数3:子线程执行的结果;

接下来开始调用系统异步加载框架,用法如下:

new AsyncTask<String, Integer, String>(){
    //在子线程之中执行的操作
    @Override
    protected String doInBackground(String... params) {
        // TODO Auto-generated method stub
        return null;
    }
    //在子线程之前执行的操作
    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        super.onPreExecute();
    }
    //在子线程之后执行的操作
    @Override
    protected void onPostExecute(String result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
    }
    //显示当前加载进度
    @Override
    protected void onProgressUpdate(Integer... values) {
        // TODO Auto-generated method stub
        super.onProgressUpdate(values);
    }
}.execute();

本篇是对Android界面编程的补充,而且都是高频常用的一些方法机制,建议重点学习。


标签:事件处理,void,探险,Handler,线程,Android,public,View
来源: https://blog.51cto.com/u_15127664/2801900