Android提供了两套事件处理机制:基于监听的事件处理;基于回调的事件处理
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 // 触摸屏事件
注意:不推荐将业务逻辑实现写在事件监听器中,包含业务逻辑的事件监听器将导致程序的显示逻辑与业务逻辑耦合,增加了后期维护难度。
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。
所有基于回调的事件处理的回调方法返回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。
1)Configuration类
专门用于描述手机设备上的配置信息
获取系统的Configuration对象:
Configuration cfg = getResources().getConfiguration();
2)重写onConfigurationChanged方法可以响应系统设置更改
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):延迟发送消息
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();
}
}
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界面编程的补充,而且都是高频常用的一些方法机制,建议重点学习。