本章要点
- 基于监听的事件处理模型
- 事件与事件监听接口
- 实现事件监听器的方式
- 基于回调的事件处理模型
- 基于回调的事件传播
- 常见的事件回调方法
- 响应系统设置的事件
- 重写onConfigurationChanged方法响应系统设置更改
- Handler类功能与用法
- 使用Handler更新程序界面
- Handler、Looper、MessageQueue工作原理
- 异步任务的功能与用法
Android事件处理有两套机制:
- 监听
- 回调
回调处理代码简洁,常处理通用性事件;某些事件无法使用回调处理,只能监听处理
3.2 监听处理
3.2.1 处理模型
模型中涉及以下三类对象:
EventSource | 事件发生源(各组件) |
Event | 通常是用户一次操作,一般通过Event对象来获得所发生事件的相关信息 |
EventListener | 负责监听事件源发生的事件,并对各种事件做出相应响应 |
基于监听事件处理模型的编程步骤如下:
- 获取事件源
- 实现事件监听器,该监听器是一个特殊的Java类,必须实现一个XxxListener
- 调用事件源的setXxxListener方法将事件监听器对象注册给事件源
监听器处理有以下规则:
- 事件源(任何组件都可以作为事件源)
- 事件监听器:监听器类必须有程序员实现
- 注册监听器:调用setXxxListener即可
3.2.2 事件和事件监听器
实现监听器是核心
对于包含大量信息的事件,Android同样会将事件信息封装成XxxEvent对象,并把该对象作为参数传入事件处理器
示例程序代码:(飞机移动程序)
plane.java
public
class
PlaneView
extends
View{
public
float
currentX
;
public
float
currentY
;
Bitmap
plane
;
public
PlaneView(Context context)
{
super
(context);
plane
= BitmapFactory.decodeResource(context.getResources(),
R.drawable.
plane
);
setFocusable(
true
);
}
@Override
public
void
onDraw(Canvas canvas)
{
super
.onDraw(canvas);
Paint p =
new
Paint();
canvas.drawBitmap(
plane
,
currentX
,
currentY
, p);
}
}
main.java
public
class
MainActivity
extends
ActionBarActivity {
private
int
speed
= 10;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
requestWindowFeature(Window.
FEATURE_NO_TITLE
);
getWindow().setFlags(WindowManager.LayoutParams.
FLAG_FULLSCREEN
,
WindowManager.LayoutParams.
FLAG_FULLSCREEN
);
final
PlaneView planeView =
new
PlaneView(
this
);
setContentView(planeView);
planeView.setBackgroundResource(R.drawable.
back
);
windowManager = getWindowManager();
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrics =
new
DisplayMetrics();
display.getMetrics(metrics);
planeView.
currentX
= metrics.
widthPixels
/ 2;
planeView.
currentY
= metrics.
heightPixels
- 40;
planeView.setOnKeyListener(
new
OnKeyListener()
{
@Override
public
boolean
onKey(View source,
int
keyCode, KeyEvent event)
{
switch
(event.getKeyCode())
{
case
KeyEvent.
KEYCODE_S
:
planeView.
currentY
+=
speed
;
break
;
case
KeyEvent.
KEYCODE_W
:
planeView.
currentY
+=
speed
;
break
;
case
KeyEvent.
KEYCODE_A
:
planeView.
currentX
+=
speed
;
break
;
case
KeyEvent.
KEYCODE_D
:
planeView.
currentX
+=
speed
;
break
;
default
:
break
;
}
planeView.invalidate();
return
true
;
}
});
}
}
对应不同组件,Android提供了不同的接口,这些接口以内部类形式存在,以View类为例:
View.OnClickListener | 单击事件的事件监听器必须实现的接口 |
View.OnCreateContextMenuListener | 创建上下文菜单事件的事件监听器 |
View.onFocusChangeListener | |
View.OnKeyListener | |
View.OnLongClickListener | 长单击 |
View.OnTouchListener | 触摸屏幕 |
在程序中实现事件监听器:
- 内部类
- 外部类
- Activity本身
- 匿名内部类
3.2.3 内部类
3.2.4 外部类
使用外部类比较少见,因为
- 事件监听器通常属于特定GUI界面
- 外部类不能自由访问GUI界面中的组件
但如果某个事件监听器需要被多个GUI界面共享,而且主要是实现业务逻辑,则可以考虑
发送短信示例代码:
public
class
MainActivity
extends
ActionBarActivity {
EditText
address
;
EditText
content
;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.
activity_main
);
address
= (EditText) findViewById(R.id.
address
);
content
= (EditText) findViewById(R.id.
content
);
Button bn = (Button) findViewById(R.id.
send
);
bn.setOnLongClickListener(
new
SendSmsListener(
this
,
address
,
content
));
}
}
send class
public
class
SendSmsListener
implements
OnLongClickListener{
private
Activity
act
;
private
EditText
address
;
private
EditText
content
;
public
SendSmsListener(Activity act, EditText address,
EditText content)
{
this
.
act
= act;
this
.
address
= address;
this
.
content
= content;
}
@Override
public
boolean
onLongClick(View source)
{
String addressStr =
address
.getText().toString();
String contentStr =
content
.getText().toString();
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(
act
,0,
new
Intent(), 0);
smsManager.sendTextMessage(addressStr,
null
, contentStr, sentIntent,
null
);
Toast.makeText(
act
,
"sms sent"
, Toast.
LENGTH_SHORT
).show();
return
false
;
}
}
3.2.5 Activity本身作为事件监听器
这种方法使用Activity本身作为监听器,可以直接在Activity类中定义事件处理器方式,这种方式虽然简洁,但有两个明显缺点:
- 可能会造成程序结构混乱
- 用法不伦不类
3.2.6 匿名内部类 作为事件监听器
大部分事件处理器都没有复用价值。所以使用匿名内部类更合适,实际上这种形式是目前使用最广泛的事件监听器
示例代码:
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.
activity_main
);
show
= (EditText) findViewById(R.id.
show
);
bn
= (Button) findViewById(R.id.
bn
);
bn
.setOnClickListener(
new
OnClickListener()
{
@Override
public
void
onClick(View v)
{
show
.setText(
"button click;"
);
}
});
}
3.2.7 直接绑定到标签
Android还有一种更简单的绑定事件监听器的方式,直接在界面布局中为制定标签绑定处理方式
对于很多Android界面组件标签而言,他们都支持onClick属性,该属性的属性值就是一个形如xxx(View source)的方法
<
Button
android:id
=
"@+id/bn"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"press me"
android:onClick="clickHandler"/>
Java代码:
public
void
clickHandler(View source)
{
EditText show = (EditText) findViewById(R.id.
show
);
show.setText(
"button onClick clickHandler"
);
}