标签:
我觉得应用程序需要处理最多的就是用户动作,也就是需要为用户动作提供响应,这种为用户动作提供响应的机制就是事件处理。Android提供了两套事件处理机制:
1.Event Source(事件源):事件发生的组件;
2.Event(事件):一次用户的操作;
3.Event Listener(事件监听器):负责监听事件源发生的事件,并对各事件做出相应的响应;
(注:我举个例子来说明一下:当用户单击按钮或单击一个菜单项时,这些动作就会激发一个相应的事件,该事件就会触发事件源上已注册的事件监听器,事件监听器调用相应的事件处理器来做出相应的响应。)
1、事件源最容易创建,任意界面组件都可以作为事件源;
2、事件的产生无需程序员关心,它是由系统自动产生;
3、实现事件监听器是整个事件处理的核心;
第一种:内部类形式作为事件监听器
【实例】当单击按钮时,文本框内容改变
布局文件代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="songsong.com.eventtest.Event_Qs">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮" />
</LinearLayout>
Java文件代码如下:
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button button = (Button) findViewById(R.id.btn1);
button.setOnClickListener(new MyClickListener());
}
class MyClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.tv1);
textView.setText("按钮被单击了");
}
}
}
第二种:匿名内部类形式作为事件监听器
大部分时候,事件处理器都没有什么复用价值,(可复用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器知识临时使用一次,所以使用匿名内部类形式的事件监听器更合适。实际上这种形式是目前使用最广泛的事件监听器。
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button button = (Button) findViewById(R.id.btn1);
button.setOnClickListener(new View.OnClickListener() {
//实现事件处理方法
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.tv1);
textView.setText("按钮被单击了");
}
});
}
}
第三种:Activity本身作为事件监听器
这种形式使用Activity本身作为监听器类,可以直接在Activity类中定义时间处理器方法。这种形式非常简洁,但使用过程一定要注意程序结构。
public class Event_Qs extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button btn1 = (Button) findViewById(R.id.btn1);
Button btn2 = (Button) findViewById(R.id.btn2);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.tv1);
switch (v.getId()){
case R.id.btn1:
textView.setText("按钮1被单击了");
break;
case R.id.btn2:
textView.setText("按钮2被单击了");
break;
}
}
} 第四种:直接绑定到标签作为事件监听器
Android还有一种更简洁的绑定事件监听器方式,那就是直接在界面布局文件为指定标签绑定事件处理方法。
以下是布局文件代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="songsong.com.eventtest.Event_Qs">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnclick"
android:text="按钮" />
</LinearLayout>java文件代码如下:
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button btn1 = (Button) findViewById(R.id.btn1);
}
//定义了一个事件处理方法
public void btnclick(View source) {
TextView textView = (TextView) findViewById(R.id.tv1);
textView.setText("按钮被单击了");
}
}
上面程序中定义了一个btnclick(View source)方法,并在xml文件的按钮中添加了android:onClick="btnclick"的属性,当用户单击按钮时,该方法就会被激发并处理btn1按钮的单击事件。正如文章开头所说基于回调的事件处理的主要做法是重写Android组件特定的回调方法或者重写Activity的回调方法,那么相对于基于监听事件处理来说,事件源与事件监听器就合为一体。或者说,事件监听器就消失了。
为了实现回调机制的事件处理,Android为所有GUI组件都提供了一些事件处理的回调方法,以View为例,该类包含如下方法。
1、boolean onKeyDown(int,KeyEvent):当按下某组件时触发;
2、boolean onKeyLongPress(int,KeyEvent):当长按某组件时触发;
3、boolean onKeyShortcut(int,KeyEvent):当键盘快捷键事件发生时触发;
4、boolean onKeyUp(int,KeyEvent):当组件上松开某个按键时触发;
5、boolean onKeyTouchEvent(event):当用户在组件上触摸时触发;
6、boolean onTrackball(event):当用户在组件上触发轨迹球事件时触发;
【实例】通过自定义View来实现基于回调的事件处理机制,自定义View时重写该View的事件处理方法。
public class MyButton extends Button {
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
Log.v("songsong.com.eventtest", "The onkeydown in MyButton" );
return true;
}
}在上面自定义了MyButton类中,重写Button类的onKeyDown(int keyCode, KeyEvent event)方法。<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="songsong.com.eventtest.Event_Qs">
<songsong.com.eventtest.MyButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮1" />
</LinearLayout>
运行上面程序,先把焦点定位到该按钮上,接着单击模拟器上任意按钮就可以看到效果:
小结:
几乎所有基于回调的时间处理方法都有一个boolean类型的返回值,该返回值用于标识该处理方法是否能完全处理该事件:
【实例】分别在Activity中重写onKeyDown和Button类中重写onKeyDown,最后为按钮添加OnKeyListener处理OnKeyDown事件,返回值皆为false.
public class MyButton extends Button {
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
Log.v("songsong.com.eventtest", "The onkeydown in MyButton");
return false;
}
}
上面Mybutton子类重写了OnKeyDown,由于返回了false,事件还将继续传播。
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button btn1 = (Button) findViewById(R.id.btn1);
btn1.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//只处理按下键的事件
if (event.getAction() == KeyEvent.ACTION_DOWN)
Log.v("songsong.com.eventtest", "The onkeydown in OnKeyListener");
return false;//继续外传
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
Log.v("songsong.com.eventtest", "The onkeydown in Activity");
return false;
}
上面Activity中重写了OnKeyDown,为按钮添加OnKeyListener处理OnKeyDown事件,返回值皆为false。当事件在传播的过程,最先触发的是按钮上绑定的事件监听器,然后才触发组件提供的事件回调方法,最后才会传播到该组件所在的Activity。当然,如果返回值是true,那么就没然后了。
Configuration类专门用于描述手机设备上的配置信息,这些配置信息既包括用户特定的配置项,也包括系统的动态设置配置。
Configuration cf = getResources().getConfiguration();
当获取完Configuration对象就可以调用该对象提供的属性来取得系统配置信息:
1、public int KeyboardHidden:该属性返回布尔值来标识键盘是否可用,如果软键盘可用 KEYBOARDHIDDEN_NO,硬软键盘都不可用就YES;
2、public int mcc:获取移动信号的国家码;
3、public int mnc:获取移动信号的网络码;
4、public int navigation:判断系统上方向导航设备的类型;
5、public intorientation:获取系统屏幕的方向,ORIENTATION_LANDSCAPE (横向),ORIENTATION_PORTRAIT(竖向);
6、public inttouchscreen:获取系统触摸屏的触摸方式;
【实例】获取系统设备状态
首先定义4个文本框来显示系统信息,通过一个按钮触发。布局文件代码因为太简单以至于不贴出来了:
MainActivity.java代码内容:
public class ConfigurationTest extends Activity {
TextView ori, nav, touch, mnc;
Button btncg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_configuration);
ori = (TextView) findViewById(R.id.ori); //屏幕方向
nav = (TextView) findViewById(R.id.nav); //方向导航设备类型
touch = (TextView) findViewById(R.id.touch); //触摸方式
mnc = (TextView) findViewById(R.id.mnc); //移动信号的网络码
btncg = (Button) findViewById(R.id.btncg);
btncg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Configuration cf = getResources().getConfiguration();
String ori_str = cf.orientation == Configuration.ORIENTATION_LANDSCAPE ? "横向屏幕" : "纵向屏幕";
String mnc_str = cf.mnc + "";
String nav_str = cf.navigation == Configuration.NAVIGATION_NONAV ? "没有方向控制" :
cf.navigation == Configuration.NAVIGATION_WHEEL ? "滚轮控制方向" :
cf.navigation == Configuration.NAVIGATION_DPAD ? "方向键控制方向" : "轨迹球控制方向";
String touch_str = cf.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH ? "无触摸屏" : "支持触摸屏";
ori.setText(ori_str);
nav.setText(nav_str);
touch.setText(touch_str);
mnc.setText(mnc_str);
}
});
}
}
效果如下:
如果想监听系统设置的更改,就可以重写Activity的onConfigurationChanged()方法,该方法是一个基于回调的事件处理方法:当系统设置发送更改时,该方法会被自动触发。
【实例】动态更改屏幕方向
布局文件中只有一个按钮,通过对单击按钮事件动态修改系统屏幕的方向。
public class Changecfg extends Activity {
Button btnchange;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_changecfg);
btnchange = (Button) findViewById(R.id.btnchange);
btnchange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Configuration cfg = getResources().getConfiguration();
if (cfg.orientation == Configuration.ORIENTATION_LANDSCAPE) { //如果当前是横屏的话
//设置为竖屏
Changecfg.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
if (cfg.orientation == Configuration.ORIENTATION_PORTRAIT) { //如果当前是竖屏的话
//设置为横屏
Changecfg.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
});
}
@Override
public void onConfigurationChanged(Configuration newConfig) { //用于监听系统设置的更改
super.onConfigurationChanged(newConfig);
String screen = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? "横屏" : "竖屏";
Toast.makeText(this, "当前屏幕方向为:" + screen, Toast.LENGTH_SHORT).show();
}
}在运行之前还要在配置Activity时加上 android:configChanges="orientation|screenSize",为了就是要监听屏幕方向改变的事件。 <activity
android:name=".Changecfg"
android:configChanges="orientation|screenSize"
android:label="@string/title_activity_changecfg">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>效果:出于性能优化的考虑,Android制定了一条简单的规则:只允许UI线程修改Activity里的UI组件。这样的话就会导致新启动的线程无法动态改变界面组件的属性值,但在实际开发中,尤其是涉及动画的游戏开发中,想要新启动的线程周期性的改变界面组件的属性值就要借助Handler的消息传递机制实现了。
根据书上所说,Handler类的主要作用有两个:
那么我们要解决的问题就是:
显然,我们这章节的内容是事件处理,那么我们通过回调的方式来实现。开发者只需重写Handler类中处理消息的方法,当新启动的线程发送消息时,消息会发送到与之关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息。
Handler类包含如下方法用于发送、处理消息:【实例】自动播放图片
通过一个新的线程来周期性地修改ImageVIew所显示的图片,布局文件中只定义了ImageVIew组件。
public class HandlerTest extends Activity {
int[] imagesID = new int[]{
R.drawable.a1,
R.drawable.a2,
R.drawable.a3,
R.drawable.a4,
};
int currentImageID = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
final ImageView show = (ImageView) findViewById(R.id.iv1);
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x1233) {
show.setImageResource(imagesID[currentImageID++ % imagesID.length]); //动态修改所显示的图片
}
}
};
new Timer().schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0x1233); //发送空消息
}
}, 0, 1200);
}
}
上面程序中的代码通过Timer周期性地执行指定任务。由于Android 不允许在新线程中访问Activity里的界面组件,因此程序只能在新的线程里发送一条消息,通知系统更新ImageView组件。
标签:
原文地址:http://blog.csdn.net/qq_26849491/article/details/51830475