本章要点


  • 基于监听的事件处理模型
  • 事件与事件监听接口
  • 实现事件监听器的方式
  • 基于回调的事件处理模型
  • 基于回调的事件传播
  • 常见的事件回调方法
  • 响应系统设置的事件
  • 重写onConfigurationChanged方法响应系统设置更改
  • Handler类功能与用法
  • 使用Handler更新程序界面
  • Handler、Looper、MessageQueue工作原理
  • 异步任务的功能与用法

Android事件处理有两套机制:


  1. 监听
  2. 回调

回调处理代码简洁,常处理通用性事件;某些事件无法使用回调处理,只能监听处理




3.2 监听处理


3.2.1 处理模型


模型中涉及以下三类对象:


EventSource

事件发生源(各组件)

Event

通常是用户一次操作,一般通过Event对象来获得所发生事件的相关信息

EventListener

负责监听事件源发生的事件,并对各种事件做出相应响应

基于监听事件处理模型的编程步骤如下:


  1. 获取事件源
  2. 实现事件监听器,该监听器是一个特殊的Java类,必须实现一个XxxListener
  3. 调用事件源的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类中定义事件处理器方式,这种方式虽然简洁,但有两个明显缺点:


  1. 可能会造成程序结构混乱
  2. 用法不伦不类





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" );


}