Android:事件处理机制
一、Android 中的事件处理方式
Android中的事件处理方式有以下几种,如:
- 基于监听器的事件处理
- 基于回调的事件处理
- 响应系统设置的事件
- Handler消息传递机制
- 异步任务操作
二、处理机制
1、基于监听器的事件处理
相比于基于回调的事件处理,这是更具“面向对象”性质的事件处理方式。在监听器模型中,主要涉及三类对象:
1)事件源Event Source:产生事件的来源,通常是各种组件,如按钮,窗口等。
2)事件Event:事件封装了界面组件上发生的特定事件的具体信息,如果监听器需要获取界面组件上所发生事件的相关信息,一般通过事件Event对象来传递。
3)事件监听器Event Listener:负责监听事件源发生的事件,并对不同的事件做相应的处理。
实现事件监听器的方法:
1)内部类形式:将事件监听器类定义在当前类的内部。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_sendmsg = (Button)findViewById(R.id.btn_sendmsg);
btn_sendmsg.setOnClickListener(new MyListener());
}
class MyListener implements View.OnClickListener{
@Override
public void onClick(View v){
Intent intent = new Intent();
intent.setClass(MainActivity.this,Main2Activity.class);
startActivity(intent);
}
}
}
2)外部类形式:将事件监听器类定义成一个外部类。
使用外部顶级类定义事件监听器的形式比较少见,主要原因如下:
1)事件监听器通常属于特定的GUI界面,定义成外部类不利于提高程序的内聚性。
2)外部类形式的事件监听器不能自由访问创建GUI界面的类中的组件,编程不简介。
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);
try {
smsManager.sendTextMessage(addressStr, null, contentStr, sentIntent, null);
}
catch(Exception e){
System.out.print(e.toString());
}
Toast.makeText(act, "短信发送完成", Toast.LENGTH_LONG).show();
return false;
}
}
public class Main2Activity extends AppCompatActivity {
EditText address;
EditText content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sendmsg);
address = (EditText)findViewById(R.id.txtAddress);
content = (EditText)findViewById(R.id.txtContent);
Button bn = (Button)findViewById(R.id.btn_send);
bn.setOnLongClickListener(new SendSmsListener(this , address, content));
}
}
3)Activity本身作为事件监听器类:让Activity本身实现监听器接口,并实现事件处理方法。
public class ActivityListener extends Activity implements View.OnClickListener {
EditText show;
Button btn;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
show=(EditText)findViewById(R.id.show);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(this);
}
@Override
public void onClick(View v)
{
show.setText("btn is clicked!!!");
}
}
4)匿名内部类形式:使用匿名内部类创建事件监听器对象。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_mybutton = (Button) findViewById(R.id.myButton);
btn_mybutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "匿名内部类的方式实现事件处理", Toast.LENGTH_SHORT).show();
}
});
}
}
5)直接绑定到标签
直接在布局文件中为指定标签绑定事件处理方法。
格式如: android:onClick="clickButton"
并在类中实现事件处理方法clickButton(View view)。
有个有意思的比喻:说android的事件处理机制是一种委派式事件处理方式,普通UI组件将发生的事件交给监听器去处理,自己不处理。好比如:发生火灾了,交给消防局处理,发生打架了交给公安局处理,并且消防局和公安局又可以同时处理多个火灾和打架事件。 这样的委派式处理方式提高了程序的可维护性。挺好。
2、基于回调的事件处理
相比基于监听器的事件处理模型,基于回调的事件处理模型要简单些,该模型中,事件源和事件监听器是合一的,也就是说没有独立的事件监听器存在。当用户在GUI组件上触发某事件时,由该组件自身特定的函数负责处理该事件。通常通过重写Override组件类的事件处理函数实现事件的处理。
所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于标识该处理方法能否完全处理该事件(基于回调的事件处理多用在自定义的UI组件中)
True : 表示该处理方法已完全处理该事件,事件不会传播出去
False: 没有完全处理该事件,事件会传播出去
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("-ddd.org-", "the onKeyDown in MyButton");
return true;
}
}
3、响应系统设置的事件
public class ConfigrationTest extends Activity
{
EditText ori;
EditText navigation;
EditText touch;
EditText mnc;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.configration);
// 获取应用界面中的界面组件
ori = (EditText)findViewById(R.id.ori);
navigation = (EditText)findViewById(R.id.navigation);
touch = (EditText)findViewById(R.id.touch);
mnc = (EditText)findViewById(R.id.mnc);
Button bn = (Button)findViewById(R.id.bn);
bn.setOnClickListener(new OnClickListener()
{
// 为按钮绑定事件监听器
@Override
public void onClick(View source)
{
// 获取系统的Configuration对象
Configuration cfg = getResources().getConfiguration();
String screen = cfg.orientation ==
Configuration.ORIENTATION_LANDSCAPE
? "横向屏幕": "竖向屏幕";
String mncCode = cfg.mnc + "";
String naviName = cfg.orientation ==
Configuration.NAVIGATION_NONAV
? "没有方向控制" :
cfg.orientation == Configuration.NAVIGATION_WHEEL
? "滚轮控制方向" :
cfg.orientation == Configuration.NAVIGATION_DPAD
? "方向键控制方向" : "轨迹球控制方向";
navigation.setText(naviName);
String touchName = cfg.touchscreen ==
Configuration.TOUCHSCREEN_NOTOUCH
? "无触摸屏" : "支持触摸屏";
ori.setText(screen);
mnc.setText(mncCode);
touch.setText(touchName);
}
});
}
}
4、Handler消息传递机制
在Android系统中,类Handler主要有以下两个作用
- 在新启动的线程中发送消息
- 在主线程中获取、处理消息
主程序处理新启动线程所发的消息,只能通过回调的方式来实现,只要重写Handler类中处理消息的方法,当新线程启动时,消息会发送到与之关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息。
public class HandlerTest extends Activity
{
// 定义周期性显示的图片的ID
int[] imageIds = new int[]
{
R.drawable.java,
R.drawable.ee,
R.drawable.ajax,
R.drawable.xml,
R.drawable.classic
};
int currentImageId = 0;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final ImageView show = (ImageView) findViewById(R.id.show);
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, 1200);
}
}
Handler、Loop、MessageQueue的工作原理:
Message:Handler接收和处理的消息对象
Looper:每个线程只能拥有一个Looper。它的looper()方法负责读取MessageQueue中的消息,读取到消息后就把消息交给Handler处理
MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时,会在它的构造器中创建MessageQueue对象。
Handler:要Handler正常工作,必须在当前线程中有一个Looper对象,为了保证当前线程中有looper对象分两种情况
- 在主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Hanler即可。
- 程序员自己启动的子线程,必须自己创建一个Looper对象,并启动它。创建Looper对象调用它的prepare()方法即可。
Prepare方法保证每个线程最多只有一个Looper对象。接下来调用Looper的静态方法loop()方法来启动它。
public class CalPrime extends Activity {
static final String UPPER_NUM = "upper";
EditText etNum;
Button bn;
CalThread calThread;
String num = null;
class CalThread extends Thread
{
public Handler mHandler;
public void run()
{
Looper.prepare();
mHandler = new Handler()
{
public void handleMessage(Message msg)
{
if(msg.what == 0x123)
{
int upper = msg.getData().getInt(UPPER_NUM);
List<Integer> nums = new ArrayList<Integer>();
outer:
for(int i = 2; i <= upper; i++)
{
for(int j =2; j <=Math.sqrt(i); j++)
{
if(i != 2 && i % j ==0)
{
continue outer;
}
}
nums.add(i);
}
Toast.makeText(CalPrime.this, nums.toString(), Toast.LENGTH_LONG).show();
}
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
etNum = (EditText)findViewById(R.id.etNum);
bn = (Button)findViewById(R.id.bn);
bn.setOnClickListener(new View.OnClickListener(){
public void onClick(View v)
{
cal();
}
}
);
calThread = new CalThread();
calThread.start();
}
public void cal()
{
Message msg = new Message();
msg.what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
calThread.mHandler.sendMessage(msg);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.cal_prime, menu);
return true;
}
}
5、异步任务操作
Android中实现异步任务有两种方式,分别是Handler和AsyncTask.
Handler方式需要为每一个任务创建一个线程,这种方式对于整个过程控制比较精细,缺点是代码臃肿,多个任务同时执行时不易对线程进行精确控制。
AsyncTask适用于简单的异步处理,不需要借助于线程和Handler即可实现。
public class AsynchronousTest extends Activity {
private static final String TAG = "ASYNC_TASK";
private Button execute;
private Button cancel;
private ProgressBar progressBar;
private TextView textView;
private MyTask mTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handlertest);
execute = (Button) findViewById(R.id.execute);
execute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTask = new MyTask();
mTask.execute("http://yc.xjlz365.com/zhongcaoyao/");
execute.setEnabled(false);
cancel.setEnabled(true);
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTask.cancel(true);
}
});
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
textView = (TextView) findViewById(R.id.text_view);
}
private class MyTask extends AsyncTask<String, Integer, String> {
@Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute() called");
textView.setText("loading...");
}
@Override
protected String doInBackground(String... params) {
Log.i(TAG, "doInBackground(Params...params)called");
try {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(params[0]);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
long total = entity.getContentLength();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int count = 0;
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
count += length;
publishProgress((int) ((count / (float) total) * 100));
Thread.sleep(500);
}
return new String(baos.toByteArray(), "gb2312");
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return null;
}
@Override
protected void onProgressUpdate(Integer... progresses) {
Log.i(TAG, "onProgressUpdate(Progress... progresses)called");
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
}
@Override
protected void onPostExecute(String result) {
Log.i(TAG, "onPostExecute(Result result)called");
textView.setText(result);
execute.setEnabled(true);
cancel.setEnabled(false);
}
@Override
protected void onCancelled() {
Log.i(TAG, "onCancelled()called");
textView.setText("cancelled");
progressBar.setProgress(0);
execute.setEnabled(true);
cancel.setEnabled(false);
}
}
}