Android 的事件处理
概括:在自学Android的进程中,今天正式步入事件处理。下面将对事件处理的学习过程做一个随学笔记。
一.事件处理概括
android事件处理机制:
- 基于监听的事件处理
- 基于回调的事件处理 ->处理通用事件,代码比较简洁
注:基于监听的事件处理比基于回调的事件处理更具优势。
- 基于监听的事件处理分工明确,事件源和事件监听有两个类分开执行,因此具有更好的可维护性
- Android的事件处理机制保证基于监听的事件监听器会被优先触发
**注:**android还允许用户在界面布局文件中为UI组件的android:onClick属性指定事件监听方法。该方法要在Activity中定义,该方法的参数view代表该UI组件。
XML文件中的实现代码
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:layout_below="@+id/textview"
android:onClick="star"
android:text="被监听按键"/>
Activity中代码
public void star(View souce){
System.out.println("按键被点击了");
}
//方法实现
star(findViewById(R.id.button));
二.基于监听的事件处理
Event source:各个组件
Event :用户操作
-
Event Listener:监听事件,对事件作出响应
事件监听器的核心就是所包含的方法-事件处理器(Event Handler)
基于监听的事件处理的步骤:
-
获取事件源:如
Button button = (Button) findViewById(R.id.button)
-
注册监听器:调用事件源的setXxxListener(XxxListener),XxxListener为事件监听器。如
button.setOnClikListener(new myClickListener)
-
事件监听器:为一个类,类似上面的myClickListener类。实现监听器的关键就是实现处理器方法onClick(View v)
class myClickListener implements View.OnClickListener{
public void onClick{
//事件处理方法
}
}
Android为不同的组件提供不同的监听器接口,这些接口以内部类的方式存在。以View为例:
- View.OnClickListener:单击事件的事件监听器必须实现的接口
- View.OnCreateContextMenuListener:创建上下文菜单事件的事件监听放弃必须实现的接口
- View.OnFocusChangeListener:焦点改变事件监听器必须实现的接口
- View.OnKeyListener:按键事件的事件监听器必须实现的接口
- View.OnLOngClickListener:长单击事件的事件监听器必须实现的接口
- View.OnTouchListener:触摸屏事件的事件监听器必须实现的接口
实现事件监听器的几种形式
-
内部类形式:事件监听器为当前类的内部类
优点:1可以在当前类中复用该监听器
2可以*访问类中的所有组件
-
外部类形式:事件监听器定义成一个外部类
优点:时间监听器能够被多个多个GUI所共享
缺点:1时间监听器通常属于特定的GUI界面,定义成外部类不利于提高程序的内聚性
2不能*访问GUI创建类中组件,编程不够简洁
-
Activity本身作为一个时间监听器
优点:简洁
缺点:容易造成程序结构混乱
实现实例:
public class xxx extends Activity implements OnClickListener{
.....
button.setOnClickListener(this);
public void onClick(View V){
//事件处理方法
}
}
-
匿名内部类:使用匿名内部类创建事件监听器对象
实现实例:
button.setOnClickListener(new OnClickListener(){
public void onClick(){
//事件处理方法
}
});
三.基于回调的事件处理
回调机制:当用户在GUI组件上激发某个事件时,组件自己特定的方法将会处理该事件
为了实现回调机制的事件处理,Android为所有的GUI组件提供了一些事件处理的回调方法,其中View类包含如下方法:
- boolean onKeyDown(int keyCode , KeyEvent event):当用户在该组件上按下某个按键时触发该方法
- boolean onKeyLongPress(int keyCode , KeyEvent event):当用户在该组件上长按某个按键时触发该方法
- boolean onKeyup(int keyCode , KeyEvent event):当用户在该组件上松开某个按键时触发该方法
- boolean onTouchEvent(MotionEvent event):当用户在该组件上触发触摸屏事件时触发该事件
- boolean onTackballEvent(MotionEvent event)当用户在该组件上触发轨迹球屏事件时触发该方法
回调事件处理实例:
public class MyButton extends Button
{
public MyButton(Context context , AttributeSet set)
{
super(context , set);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
super.onKeyDown(keyCode , event);
Log.v("-MyButton-" , "the onKeyDown in MyButton");
System.out.println("按键被点击了");
// 返回false,表明并未完全处理该事件,该事件依然向外扩散
return true;
}
}
XML文件
<org.crazyit.event.MyButton
android:id="@+id/bn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="单击我"
/>
基于回调的事件传播
回调事件处理方法的boolean类型返回值:
- true:表明该方法已完全处理该事件,该事件不会向外传播
- false:表明该方法未完全处理该事件,该事件会传播出去
四.响应的系统设置的事件
Configuration类:专门用于描述手机设备上的配置信息调用方法:
Configuration cfg = new getResources().getConfiguration();
获得配置的属性有:
- public float fontScale 获得当前用户设置的字体的缩放因子
- public int keyboard 获取当前设备所关联的键盘类型。返回值:KEYBOARD_NOKEYS、KEYBOARD_QWERTY(普通电脑键盘)、KEYBOARD_12KEY(小键盘)
- public int keyboardHidden 返回一boolean值用于标示当前键盘是否可用。系统的硬件或软件键盘可用,返回KEYBOARDHIDDEN_NO,当两者都不可用,返回KEYBOARDHIDDEN_YES
- public Locale locale 返回用户当前的Locale
- public int mcc 获取移动信号的国家码
- public int mnc 获取移动信号的网络码
- public int navigation 判断系统上方向导航设备的类型。返回:NAVIGATION_NONAV(无导航)、NAVIGATION_DPAD(DPAD导航)、NAVIGATION_TRACKBALL(轨迹球导航)、NAVIGATION_WHEEL(滚轮导航)等
- public int orientation 获取系统屏幕的方向,返回值:ORIENTATION_LANDSCAPE(横向屏幕)、ORIENTATION_PORTAIT(竖向屏幕)、ORIENTATION_SQUARE(方形屏幕)
- public int touchscreen 获取系统触摸屏的触摸方式,返回值:TOUCHSCREEN_NOTOUCH(无触摸屏)、TOUCHSCREEN_STYLUS(触摸笔式的触摸屏)、TOUCHSCREEN_FINGER(接收手指的触摸屏)
例如监听屏幕方向的改变:
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
String screen = newConfig.orientation ==
Configuration.ORIENTATION_LANDSCAPE ? "横向屏幕" : "竖向屏幕";
Toast.makeText(this, "系统的屏幕方向发生改变" + "\n修改后的屏幕方向为:"
+ screen, Toast.LENGTH_LONG).show();
}
在AndroidManifest.xml文件中配置android:configChanges属性
android:configChanges="orientation"
五.Handle消息传递机制
出现的原因:为了解决多个线程并发操作UI组件所引起的线程安全问题,android指定了一条简单的规则:只允许UI线程修改Activity中的UI组件。这样就导致了新启动的线程无法动态的改变改变界面组件的属性值。在android应用开发中,需要让新的线程周期性的改变界面组件的属性值,就需要借助Handler的消息传递机制来实现。
Handler类的主要作用:
- 在新启动的线程中发送消息
- 在主线程中获取、传递消息
Handler类包括如下方法用于发送、处理消息
- void handleMessage(Message msg) 处理消息的方法,通常被重写
- final boolean hasMessages(int what) 检查消息队列中是否包含what属性为指定值的消息
- final boolean hasMessages(int what, Object object) 检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息
- 多个重载的Message obtainMessage() 获取消息
- sendEmptyMessage(int what) 发送空消息
- final boolean sendEmptyMessageDelayed(int what,long delayMills) 指定多少毫秒之后发送空消息
- final boolean sendMessage(Message msg) 立即发送消息
- final boolean sendMessageDelayed(Message msg,long delayMills) 定多少毫秒之后发送消息
自动播放动画实例
final Handler myHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// 如果该消息是本程序所发送的
if (msg.what == 0x1233)
{
// 动态地修改所显示的图片
show.setImageResource(imageIds[currentImageId++
% imageIds.length]);
}
}
};
// 定义一个计时器,让该计时器周期性地执行指定任务
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
// 发送空消息
myHandler.sendEmptyMessage(0x1233);
}
}, 0, 5000);
和Handler一起工作的组件:
- Message:Handler接收和处理的消息对象
- Looper:读取MessageQueue中的消息,每个线程只能有一个Looper
- MessageQueue:消息队列
- Handler:发送消息和处理消息
**注:**Handler发送和处理的消息必须保存在MessageQueue中,而MessageQueue又由Looper负责管理。因此线程中必须有Looper对象
在线程中使用Handler的步骤:
- 调用Looperde prepare()方法为当前线程创建Looper对象,同时它的构造其中创建出与之匹配的MessageQueue
- 创建Handle子类的实例,重写handleMessage()方法,该方法负责处理来自其他线程的消息
- 调用Looper的loop()方法启动Looper
Looper.prepare();
mHandler = new Handler(){
public void handleMesssage(Message msg){
}
};
Looper.loop();
六、异步任务(Async Task)
AsyncTack**