首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络游戏 >

Android游戏开发框架(4)按键事件处理

2013-02-19 
Android游戏开发框架(四)按键事件处理以下均转自Android游戏编程入门经典,转载请标明出处先来看看按键处理

Android游戏开发框架(四)按键事件处理

以下均转自Android游戏编程入门经典,转载请标明出处Android游戏开发框架(4)按键事件处理

先来看看按键处理。首先,它必须要与接收键盘事件的View相连接。接下来,在按键被按下时它必须存储各个按键的当前状态以便轮询。它也必须保持一个KeyEvent实例的列表。最后,它必须正确地同步所有这一切工作,因为当从游戏主循环(在一个不同的线程中执行)轮询时,它将从UI线程中接收事件。让我们在查看一下KeyEvent类。

package org.example.androidgames.framework.impl;import java.util.ArrayList;import java.util.List;import org.example.androidgames.framework.Input.KeyEvent;import org.example.androidgames.framework.Pool;import org.example.androidgames.framework.Pool.PoolObjectFactory;import android.view.View;import android.view.View.OnKeyListener;public class KeyboardHandler implements OnKeyListener {boolean[] pressedKeys = new boolean[128];Pool<KeyEvent> keyEventPool;List<KeyEvent> keyEventsBuffer = new ArrayList<KeyEvent>();List<KeyEvent> keyEvents = new ArrayList<KeyEvent>();public KeyboardHandler(View view){PoolObjectFactory<KeyEvent> factory = new PoolObjectFactory<KeyEvent>(){public KeyEvent createObject(){return new KeyEvent();}};keyEventPool = new Pool<KeyEvent>(factory, 100);view.setOnKeyListener(this);view.setFocusableInTouchMode(true);view.requestFocus();}@Overridepublic boolean onKey(View v, int keyCode, android.view.KeyEvent event) {// TODO Auto-generated method stubif(event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE)return false;synchronized (this){KeyEvent keyEvent = keyEventPool.newObject();keyEvent.keyCode = keyCode;keyEvent.keyChar = (char) event.getUnicodeChar();if(event.getAction() == android.view.KeyEvent.ACTION_DOWN){keyEvent.type = KeyEvent.KEY_DOWN;if(keyCode > 0 && keyCode < 127)pressedKeys[keyCode] = true;}if(event.getAction() == android.view.KeyEvent.ACTION_UP){keyEvent.type = KeyEvent.KEY_UP;if(keyCode > 0 && keyCode < 127)pressedKeys[keyCode] = false;}keyEventsBuffer.add(keyEvent);}return false;}public boolean isKeyPressed(int keyCode){if(keyCode < 0 || keyCode > 127)return false;return true;}public List<KeyEvent> getKeyEvents(){synchronized (this){int len = keyEvents.size();for(int i = 0; i < len; i++)keyEventPool.free(keyEvents.get(i));keyEvents.clear();keyEvents.addAll(keyEventsBuffer);keyEventsBuffer.clear();return keyEvents;}}}

KeyboardHandler类实现了OnKeyListener接口,所以它可以从View中接收键盘事件。

第一个成员变量是一个数组,包含128个布尔值。我们将在该数组中存储每一个按键的当前状态(按下或没有被按下)。每一个按键都有一个单独的键盘编码。幸运的是,android.view.KeyEvent.KEYCODE_XXX常量(编码了键盘编码)都在0-127之间,所以可以用一种对垃圾回收器有好的方式存储它们。需要注意的是,我们的KeyEvent类和Android API里的KeyEvent类具有相同的名字。

第二个成员变量是一个Pool,它包含KeyEvent类的实例。我们回收了所有创建的KeyEvent对象,以减轻垃圾回收器的工作。

第三个成员变量是尚未被我们的游戏使用的KeyEvent。每当在UI线程上得到一个新的按键事件时,它就将添加到这个列表。

最后一个成员变量存储调用KeyboardHandler.getKeyEvents()返回的KeyEvent。我们马上会看到,为什么必须双缓冲区按键事件。

该构造函数有一个参数,即想要从中接收按键事件的View。我们用一个适当的PoolObjectFactory创建了Pool实例,将处理程序作为OnKeyListener注册到View中,并最终通过使View获得焦点来确保其可以接收到按键事件。

接下来我们要实现接口方法是OnKeyListener.onKey(),当View每次接收到一个新的按键事件时调用该方法。我们一开始忽略对KeyEvent.ACTION_MULTIPLE事件进行编码的任何(Android)按键事件。我们紧跟着使用了一个同步块。需要注意的是,事件是在UI线程上接收,而在主循环线程中被读取,所以要确保不会并行访问任何成员变量。

在同步块中,首先从Pool类里获取一个KeyEvent实例(我们的KeyEvent实现)。根据Pool类的状态,将得到一个回收的实例或一个全新的实例。接下来基于传递给方法的Android KeyEvent的内容,设置KeyEvent的keyCode和keyChar成员变量。之后,解码Android KeyEvent的类型,并设置KeyEvent的类型以及pressdKey数组中的相应元素。最后在事先定义的keyEventBuffer列表中添加KeyEvent。

isKeyPressed()方法,它实现了Input.isKeyPressed()方法的语义。通过传入一个整型参数来区分按键代码并且返回该键是否被按下。我们在前面的方法(该方法在UI线程中被调用)中设置了这个元素的类型。因为我们仍然使用基本类型,所以没必要进行同步。

最后一个方法getKeyEvent(),它实现了Input.getKeyEvents()方法的语义。我们再次使用一个同步块,记住,该方法将在不同的线程中被调用。

接下来,我们遍历KeyEvent数组,并将该数组中存储的所有KeyEvent插入到Pool类中。请记住。我们通过调用UI线程上的OnKey()方法从Pool类中获取实例。在这里,我们将把它们重新插入Pool类。但KeyEvent的列表不是空的吗?是的,但是仅在第一次调用该方法时才会出现这种情况。

在神秘的Pool插入循环后,我们清空了KeyEvent列表,并用keyEventsBuffer列表中的事件填充了该列表。最后,清空了keyEventsBuffer列表,并将新填充的KeyEvent列表返回给调用程序。这里究竟发生了什么呢?

让我们通过一个简单的实例来解释这一点。我们将探究KeyEvent和keyEventsBuffer列表中发生了什么,以及每次在UI线程上一个新事件达到时或者游戏在主线程中获取事件时Pool池中发生了什么:

UI thread: onKey() -> keyEvents = { }, keyEventsBuffer = {KeyEvent1 }, pool = { }

Main thread: getKeyEvents() -> keyEvents = {KeyEvent1}, keyEventsBuffer = { }, pool = { }

UI thread: onKey() -> keyEvents = {KeyEvent1}, keyEventsBuffer = {KeyEvent2}, pool = { }

Main thread: getKeyEvents() -> keyEvents = {KeyEvent2}, keyEventsBuffer = { }, pool = {KeyEvent1}

UI thread: onKey() -> keyEvents = {KeyEvent2}, keyEventsBuffer = {KeyEvent1}, pool = { }

(1)首先,从UI线程中得到一个新的事件。现在池中什么都没有,所以一个新的KeyEvent实例(KeyEvent1)被创建并插入到keyEventsBuffer列表中。

(2)在主线程中调用getKeyEvents()。getKeyEvents()从keyEventsBuffer列表中得到KeyEvent1,并将返回值放入它返回给调用程序的KeyEvent列表中。

(3)我们在UI线程上得到另一个事件。在池中仍然没有任何对象,所以一个新的KeyEvent实例(KeyEvent2)被创建并插入到keyEventsBuffer列表中

(4)主线程再次调用getKeyEvents()。现在发生了一些有趣的事情。执行方法后,KeyEvent列表中仍然包含KeyEvent1。插入循环将该事件放入池中。然后,它清空KeyEvent列表,并插入一个KeyEvent到keyEventsBuffer中,在此插入的是KeyEvent2。我们只是回收了一个按键事件。

(5)最后,另一个按键事件达到UI线程上。这一次,在Pool类里有一个自由的KeyEvent,我们很乐意重用它,这样就不需要垃圾回收器了!

但这种机制有一点需要注意,我们必须经常调用KeyboardHandler.getKeyEvents(),否则KeyEvent列表将很快被填满,而没有对象返回到池中。只要记住这一点,一切工作都会很顺利。

热点排行