9.1 增加触摸反馈(Adding Touch Support to Our Activity)
这里将会拦截Android事件系统中的事件,首先持有渲染器的一个引用,打开AirHockeyActivity然后修改setRenderer(),代码如下:
//AirHockeyTouch/src/com/airhockey/android/AirHockeyActivity.java
final AirHockeyRenderer airHockeyRenderer = new AirHockeyRenderer(this);
if (supportsEs2) {
// ...
glSurfaceView.setRenderer(airHockeyRenderer);
// ...
}
我们将会使用这个引用把相关事件通知到渲染器。
9.1.1 事件监听(Listening to Touch Events)
现在开始写事件处理器,在setContentView()之前增加如下代码:
//AirHockeyTouch/src/com/airhockey/android/AirHockeyActivity.java
glSurfaceView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event != null) {
// Convert touch coordinates into normalized device
// coordinates, keeping in mind that Android's Y
// coordinates are inverted.
final float normalizedX = (event.getX() / (float) v.getWidth()) * 2 - 1;
final float normalizedY = -((event.getY() / (float) v.getHeight()) * 2 - 1);
// ...
}
在Android里面我们可以调用函数setOnTouchListener()设置一个事件监听器,当用户触摸相应view的时候我们就可以在onTouch()中处理相应的事件。
首先检测是否有要处理的事件;在Android中,触摸事件坐标位于view坐标空间中,因此左上角对应于顶点(0, 0),右下角对应于view的尺寸。比如我们的view为480x800(宽x高),所以右下角坐标将会映射到(480, 800)。
由于在渲染器中我们都是使用规范化坐标的,所以我们需要通过反转y坐标然后把每一个坐标规范化到范围[-1, 1]中。
9.1.2 事件转发(Forwarding Touch Events to Our Renderer)
下面是事件转发的完整代码:
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event != null) {
// Convert touch coordinates into normalized device
// coordinates, keeping in mind that Android's Y
// coordinates are inverted.
final float normalizedX = (event.getX() / (float) v.getWidth()) * 2 - 1;
final float normalizedY = -((event.getY() / (float) v.getHeight()) * 2 - 1);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
glSurfaceView.queueEvent(new Runnable() {
@Override
public void run() {
airHockeyRenderer.handleTouchPress(normalizedX, normalizedY);
}
});
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
glSurfaceView.queueEvent(new Runnable() {
@Override
public void run() {
airHockeyRenderer.handleTouchDrag(normalizedX, normalizedY);
}
});
}
return true;
} else {
return false;
}
}
这里分区分事件是点击还是滑动,因为针对这两种情况有不同的事件处理,点击事件对应于MotionEvent.ACTION_DOWN,滑动事件对应于MotionEvent.ACTION_MOVE。
需要注意的一点Android的UI运行于主线程中,而GLSurfaceView在一个独立的线程中,因此我们需要在这两个线程之间使用一种安全的方式交互。我们可以使用方法queueEvent()把调用分派到OpenGL 线程中,然后在OpenGL线程中调用方法airHockeyRenderer.handleTouchPress() 处理点击事件,调用方法 airHockeyRenderer.handleTouchDrag() 处理滑动事件。由于还没有创建这两个方法,所以接下来将会创建他们。
上面的方法中通过传入true表示我们将会消耗事件,假如事件为null我们将会返回false。
打开渲染器AirHockeyRenderer,并加入如何存根方法:
//AirHockeyTouch/src/com/airhockey/android/AirHockeyRenderer.java
public void handleTouchPress(float normalizedX, float normalizedY) {
}
public void handleTouchDrag(float normalizedX, float normalizedY) {
}
作为一个小的练习,你可以在上面的方法中加入一些log打印方法,运行程序并观察触摸屏幕的时候输出的log信息。
下一节,我们将学习如何增加相交测试(点击进入下一节)