输入是用户交互的一个重要组成部分,触摸屏和键盘是Android系统的标配输入设备,多数游戏、应用都离不开这两个输入设备。本节给出一个小程序,演示基本的触摸屏和按键处理,虽然不能覆盖全部的情况,但至少可以起到抛砖引玉的作用。
4.1.1 Android输入处理
在Android系统中,输入事件一般是由View类来处理的,通过实现一些事件监听接口,就能够处理这些事件,并且决定是否拦截下来。
1)事件监听接口及设置监听
Android系统提供了几个事件监听接口,见表4-1。
表4-1 Android系统事件监听接口
事件监听接口 方  法 功  能
View.OnClickListener onClick(View) 监听在View上面的屏幕单击事件
View.OnLongClickListener onLongClick(View) 监听在View上面的屏幕长按事件
View.OnKeyListener onKey(View) 监听在View上面的按键事件
View.OnTouchListener onTouch(View,MotionEvent) 监听在View上面的屏幕长按事件
  View.OnKeyListener只有在View被选中的情况下才会监听到事件,所以这个按键处理的方法不是很常用。
如果要监听某个View窗口中的输入事件,只要实现对应的接口,并且在View中设置监听即可。比如:
public class TouchDemo extends Activity 
implements OnClickListener {
//..省略部分代码
mTouchView.setOnClickListener(this);
//..省略部分代码
@Override
public void onClick(View arg0) {
if(arg0.getId() == R.id.tv1) {
mLogView.append("onClick event\n");
mLogView.scrollBy(0, 10);
}
}
}
}
上面的代码实现了一个简单的监听接口(interface),并将其添加到View窗口的监听队列中去。
  除了监听接口以外,View类里面也有默认的处理方法,在实现View类的时候进行重载就可以了:
 onKeyDown(int, KeyEvent)——处理按键按下事件;
 onKeyUp(int, KeyEvent)——处理按键弹起事件;
 onTrackballEvent(MotionEvent)——处理轨迹球运动事件;
 onTouchEvent(MotionEvent)——处理触摸事件。
2)按键事件处理
上一节已介绍过可以在View中处理按键事件,只要实现了OnKeyEventListener接口即可,同时也可以使用View类的onKeyDown()或者onKeyUp()。但是我们往往希望按键事件是在整个程序屏幕上都可以响应的,所以这个时候在Activity中进行响应会更加方便。
Activity提供了“boolean onKeyDown(int keyCode,KeyEvent event)”方法,可以监听到按键,而KeyEvent类提供了按键编码的常量定义,例如KeyEvent.KEYCODE_MENU表示菜单按键。
3)输入事件拦截器
在了解了触屏和按键操作的处理以后,我们来完成一个输入事件拦截器的小应用,如图4-1所示。
 
图4-1
程序实现了对click事件longclick、touch轨迹的拦截,从图4-1中可以看出,实现了所有4种拦截全部打开,而触摸的轨迹在屏幕上面也形成了“test”字样。也就是说,这个小程序可以实现单击、长按、触摸事件的拦截,而要实现按键、多点触摸、方向的拦截只需在这个小程序的基础上进行一定的扩充即可。
下面来看具体的程序实现:
(1)布局文件main.xml的片段
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/mainview"
  Android:orientation="vertical"
  Android:layout_width="fill_parent"
  Android:layout_height="fill_parent"
  > <LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
  Android:orientation="horizontal"
  Android:layout_width="fill_parent"
  Android:layout_height="200px"
  > 
<TextView 
Android:id="@+id/tv0"
  Android:layout_width="200px" 
  Android:layout_height="200px" 
  Android:text="event logs\n"
  />

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
  Android:orientation="vertical"
  Android:layout_width="fill_parent"
  Android:layout_height="fill_parent"
  > 
<CheckBox Android:id="@+id/box_click"
 Android:text="拦截click"
 Android:layout_width="fill_parent"
 Android:layout_height="wrap_content"
/>
<CheckBox Android:id="@+id/box_longclick"
 Android:text="拦截longclick"
 Android:layout_width="fill_parent"
 Android:layout_height="wrap_content"
/>
<CheckBox Android:id="@+id/box_touch"
 Android:text="拦截touch"
 Android:layout_width="fill_parent"
 Android:layout_height="wrap_content"
/>
<CheckBox Android:id="@+id/box_touch_track"
 Android:text="touch轨迹"
 Android:layout_width="fill_parent"
 Android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout> 
<devdiv.example.TouchDemo.SimpleView 
Android:id="@+id/tv1"
  Android:layout_width="320px" 
  Android:layout_height="200px" 
  Android:text="@string/hello"
  />
</LinearLayout>
(2)主类(实现了所有的事件监听器)
public class TouchDemo extends Activity 
implements OnClickListener, OnLongClickListener, OnTouchListener
,OnCheckedChangeListener{
private TextView mLogView;
private SimpleView mTouchView;
private CheckBox mBoxClick;
private CheckBox mBoxLongClick;
private CheckBox mBoxTouch;
private CheckBox mBoxTouchTrack;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mLogView = (TextView)findViewById(R.id.tv0);
    mTouchView = (SimpleView)findViewById(R.id.tv1);
    mTouchView.setBackgroundColor(Color.CYAN);
    mTouchView.setOnClickListener(this);
    mTouchView.setOnLongClickListener(this);
    mTouchView.setOnTouchListener(this);
    mLogView.setTextSize((float) 10.0);
    
    mBoxClick = (CheckBox)findViewById(R.id.box_click);
    mBoxLongClick = (CheckBox)findViewById(R.id.box_longclick);
    mBoxTouch = (CheckBox)findViewById(R.id.box_touch);
    mBoxTouchTrack = (CheckBox)findViewById(R.id.box_touch_track);
    mBoxTouchTrack.setOnCheckedChangeListener(this);
  } @Override
public void onClick(View arg0) { // 单击
if(mBoxClick.isChecked()) { // 确定用户是否选择了监听单击事件
if(arg0.getId() == R.id.tv1) {
mLogView.append("onClick event\n"); // 记录到日志窗口
mLogView.scrollBy(0, 10); // 滚动日志
}
}
} @Override
public boolean onLongClick(View arg0) { // 长按
if(mBoxLongClick.isChecked()) {
if(arg0.getId() == R.id.tv1) {
mLogView.append("onLongClick event\n");
}
// 下面进行随机返回处理,以便于观察事件处理之间的关系,请参考后面的小结
if(System.currentTimeMillis() % 2 == 0) {
mLogView.append("onLongClick return true\n");
return true;
} else {
mLogView.append("onLongClick return false\n");
return false;
}
}
return false; // 默认不处理,交由后面的环节去处理
} @Override
public boolean onTouch(View arg0, MotionEvent ev) {
if(mBoxTouchTrack.isChecked()) {
// FIXME: 多点触摸版本
// 参考后面对多点触摸API的描述,很容易就能够修改成支持多点触摸的版本
final int historySize = ev.getHistorySize();
for (int h = 0; h < historySize; h++) {
mTouchView.DrawPoint(ev.getHistoricalX(h), ev.getHistoricalY(h));
}
mTouchView.DrawPoint(ev.getX(), ev.getY());
}
if(mBoxTouch.isChecked()) {
if(arg0.getId() == R.id.tv1) {
mLogView.append("onTouch event point(" + ev.getX() +"," + ev.getY() + ")" );
mLogView.scrollBy(0, 10);
}
// 同样,随机返回处理,只是用于实验,真实的场景不必如此
if(System.currentTimeMillis() % 2 == 0) {
mLogView.append("onTouch return true\n");
mLogView.scrollBy(0, 10);
return true;
} else {
mLogView.append("onTouch return false\n");
mLogView.scrollBy(0, 10);
return false;
}
}
return false;
} @Override
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
if(arg0 == mBoxTouchTrack) {
if(!arg1) {
mTouchView.InitCache(); // 清空轨迹面板
}
}
} @Override
public boolean onKeyDown(int keyCode, KeyEvent event)
  {
mLogView.append("keyCode " + keyCode + " action " + event.getAction());
if (keyCode == KeyEvent.KEYCODE_MENU) { // 按下菜单按键的处理
mLogView.append("按下菜单");
return true;
}
    return super.onKeyDown(keyCode, event);
  }
}
(3)触摸类(负责接收触摸事件并画出轨迹)
public class SimpleView extends View {
private final static int WIDTH = 320;
private final static int HEIGHT = 200;
private Bitmap mBitmapCache;
private Canvas mCanvasCache;
private Paint mPaint;

public SimpleView(Context context) {
super(context);
Construct(context);
}

public SimpleView(Context context,AttributeSet attr) { 
super(context,attr); 
Construct(context);
} private void Construct(Context context) {
// FIXME: onMesured的时候要重新创建cache
mBitmapCache = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
mCanvasCache = new Canvas(mBitmapCache);

mPaint = new Paint(); 
mPaint.setAlpha(0x40); 
mPaint.setColor(Color.BLUE);
}
// 在缓冲中划一个点,并且通知刷新画面
public void DrawPoint(float x, float y)
{
mCanvasCache.drawPoint(x, y, mPaint);
invalidate();
}

public void InitCache() {
mCanvasCache.drawColor(Color.WHITE);
invalidate();
}

protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmapCache, 0, 0, mPaint);
}}4.1.2 Android多点触摸与手势更多代码下载地址:http://www.devdiv.com/thread-85477-1-1.html