本章要点
- 基于监听的事件处理模型
- 事件与事件监听接口
- 实现事件监听器的方式
- 基于回调的事件处理模型
- 基于回调的事件传播
- 常见的事件回调方法
- 响应系统设置的事件
- 重写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);
}
}
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 );
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 ;
}
});
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 ));
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 ;
}
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;" );
}
});
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: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" );
{
EditText show = (EditText) findViewById(R.id. show );
show.setText( "button onClick clickHandler" );
}