libgdx游戏引擎教程(七) 在libgdx中拦截Android按键事件

转自:http://www.apkbus.com/android-58030-1-1.html

 

今天是#Testin杯#Mkey libgdx教程的第七讲,在第二讲我就曾经对大家承诺过写一个关于Libgdx内多屏幕显示的内容,可是我脑子一时糊涂竟然忘了这事,这里向大家道歉,从第五到第七讲我们就来八一八libgdx中的多游戏容器的实现。我在我的Libgdx教程中尽量给大家介绍一些实用的功能,至于那些libgdx API基本的使用方法则不过多侧重,想来大家通过查询API文档也能掌握。这里再提醒一下大家,API文档可以在官网上看到,也可以在下载下来的libgdx包中的docs下的index.html中查看。

至于多容器,简单的理解就是多个游戏界面,那么大家一定联想到SDK 中的多界面是用Activity实现的,通过Intent跳转来实现多界面之间的切换。


 

第六讲的最后我们留下了一个问题,就是在完成了界面的切换后如何实现按下返回键界面也返回上一个界面?我们先介绍一下Android系统的Activity栈机制。




 

Activity 是Android程序的表现层。程序的每一个显示屏幕就是一个Activity。正在运行的Activity处在栈的最顶端,它是运行状态的。




 

当有新的Activity进入屏幕最上端时,原来的Activity就会被压入第二层。如果他的屏幕没有被完全遮盖,那么他处于Paused状态,如果他被遮盖那么处于Stop状态。




 

不管处于任何一层,都可能在系统觉得资源不足时被强行关闭,当然关闭时栈底的程序最先被关闭。




 

act.jpg
2012-7-8 12:01 上传
下载附件(58.58 KB)




 

就像上图中我们看到的那样,所谓的Activity栈就是一个Activity队列,后来触发的Activity覆盖在原来的Activity上,等到栈顶Activity退出(如按返回键时),上一层的Activity就变成了栈顶,也就变成了当前显示的Activity



 

再来看看我们这个程序的情况。虽然我们在 libgdx 中设置的是两个 Screen, 但对于 Android 而言只有一个 Activity ,即 LibgdxActivity (其实是一个 AndroidApplication , 那么在只有一个 Activity 的程序中按返回键,栈顶的 Activity 被销毁,那么当前程序的 Activity 栈就为空,当然程序就直接退出了。



 

为了解决这个问题,我们先尝试怎么捕捉到 Back 键按下的事件。就像我们在进度条的制作中用到的那样, Gdx.input 中提供了很多关于捕捉输入输出事件的函数。除了我们上次用到的 Gdx.input.isTouched() 外, Gdx 中还有众多类似的方法,今天我们就要使用另一个函数, Gdx.input.isKeyPressed() ,我们通过传入一个代表键值的 int 值来判断该键值代表的按键是否按下了。



 

同样的,就和如何判断触摸事件一样,我们将判断返回键的语句也放在 render 函数里,修改过后的代码如下:
  1. public class Progress implements Screen{
  2. ProgressBar bar;
  3. AnimalActor animal;
  4. Stage stage;
  5. AssetManager manager;
  6. boolean hasini;
  7. @Override
  8. public void hide() {
  9. // TODO Auto-generated method stub

  10. }

  11. @Override
  12. public void render(float arg0) {
  13. // TODO Auto-generated method stub
  14. Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  15. Gdx.gl.glClearColor(1f,1f,1f,0f);
  16. stage.act(Gdx.graphics.getDeltaTime());
  17. stage.draw();

  18. if(Gdx.input.isKeyPressed(Input.Keys.BACK))
  19. System.out.println("Back Pressed");

  20. if(!manager.update()){
  21. bar.setProgress(manager.getProgress()*100);
  22. }
  23. //加载完成且之前没有初始化过AnimalActor,且在手触摸屏幕时初始化AnimalActor,并将进度条从舞台中移除,并加入AnimalActor对象
  24. if(!hasini&&manager.update()){
  25. bar.setProgress(100);
  26. if(Gdx.input.isTouched()){
  27. stage.removeActor(bar);
  28. animal.iniResource();
  29. stage.addActor(animal);
  30. hasini=true;
  31. }
  32. }
  33. //我们做一个标记,看看未加载(Queued)完成的资源和已加载完成的资源的数量(Loaded)
  34. if(!manager.update()){
  35. System.out.println("QueuedAssets:"+manager.getQueuedAssets());
  36. System.out.println("LoadedAssets:"+manager.getLoadedAssets());
  37. System.out.println("Progress:"+manager.getProgress());
  38. }
  39. }
  40. @Override
  41. public void resize(int arg0, int arg1) {
  42. // TODO Auto-generated method stub

  43. }
  44. @Override
  45. public void resume() {
  46. // TODO Auto-generated method stub

  47. }
  48. @Override
  49. public void show() {
  50. // TODO Auto-generated method stub
  51. bar=new ProgressBar(0,0);
  52. //新建一个舞台
  53. stage=new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(), true);
  54. stage.addActor(bar);
  55. //记得初始化一下AssetManager实例
  56. manager=new AssetManager();
  57. //传入AssetManger的引用,便于animal的资源初始化,但是注意了,只有在调用iniResourse()后资源才被初始化
  58. animal=new AnimalActor(manager);
  59. //把资源加入加载列表,这里我放了一个29帧的动画,在asset文件夹下animal下有29张图片
  60. for(int i=1;i<30;i++){
  61. manager.load("animal/"+i+".png", Texture.class);
  62. }
  63. }



  64. @Override
  65. public void dispose() {
  66. // TODO Auto-generated method stub
  67. //同样的,在结束时释放资源
  68. bar.dispose();
  69. animal.dispose();
  70. manager.clear();
  71. manager.dispose();
  72. }

  73. @Override
  74. public void pause() {
  75. // TODO Auto-generated method stub

  76. }


  77. }
复制代码
变化非常小,无非在 render() 函数中添加了以下几条语句:
  1. if(Gdx.input.isKeyPressed(Input.Keys.BACK))
  2. System.out.println("Back Pressed");
复制代码
而且我们没做任何的工作,只是在返回键按下的时候打印一条消息,便于我们识别,我们先不要急着往下做,我们先来看看效果。
由于UI并没有任何的变化,这里就不贴图了,我们在进入游戏动画界面后,按下手机上的回退键,然后看看Logcat里的输出。


豌豆荚截图20120707224539.png

2012-7-8 12:04 上传
下载附件(35.7 KB)



按下返回键

log.jpg
2012-7-8 12:01 上传
下载附件(310.38 KB)
我们发现事件确实被捕捉到了。但是这里又存在一个问题,由于render()函数执行得非常快,而用户按下Back的持续时间可能相对于一次render()函数执行的时间要长得多,因此事件就被捕捉了很多次,这也是我们不希望看到的。那么我们可以新建一个boolean变量,标记Back是否按下,按下一次后改变它的值,不再让render()函数继续捕捉事件。



 

Progress.java中添加一个用于标记的boolean变量
  1. boolean BackHasTouched;
复制代码
初始值设为false。并在render()中检测Back事件的部分做出相应的修改。改动后的代码如下:
  1. if(!BackHasTouched&&Gdx.input.isKeyPressed(Input.Keys.BACK)){
  2. BackHasTouched=true;
  3. System.out.println("Back Pressed");
  4. }
复制代码
这样我们就只会得到一次Back键的触摸事件。我们来看看输出:
log2.jpg
2012-7-8 12:01 上传
下载附件(258.97 KB)
确实只有一个Back事件被捕捉到了。
当然有一点我们要注意一下,每次该Screen变成主页面的时候,即系统自动调用show()函数的时候我们要将该boolean重新置为false:
  1. BackHasTouched=false;
复制代码


 

既然事件都捕捉到了,我们就可以尝试用setScreen捕捉界面了。修改一下Progress.java的构造函数,将LibgdxActivity的实例传入,便于调用setScreen,这和上一讲我们修改MyGame.java是一样的。Progress.java中添加变量:




 

LibgdxActivity activity;




 

修改构造函数:
  1. public Progress(LibgdxActivity activity) {
  2. super();
  3. this.activity = activity;
  4. }
复制代码
然后我们捕捉到事件后尝试切换界面,继续修改render()内用于捕捉Back事件的部分:
  1. if(!BackHasTouched&&Gdx.input.isKeyPressed(Input.Keys.BACK)){
  2. BackHasTouched=true;
  3. System.out.println("Back Pressed");
  4. activity.ag.setScreen(activity.mg);
  5. }
复制代码
运行一下看看效果。


 

依然到达动画界面后我们按下Back键。诶??还是退出了?我们再来看一看输出。



 

log3.jpg
2012-7-8 12:01 上传
下载附件(324.62 KB)


 

输出依然是有的,Back事件确实是捕捉到了,但依然不能够成功切换页面,那又是什么原因呢?


 

其实原因还是一样的,Back事件被Android系统自身捕捉到使得LibgdxActivity整个退出了,那么我们在LibgdxActivity中设置的各个Screen。有什么解决方案呢?Android 2.1以上提供了一个方法。



 

public void onBackPressed ()
Since: API Level 5
Called when the activity has detectedthe user's press of the back key. The default implementation simply finishesthe current activity, but you can override this to do whatever you want.



 

我们可以看到,这个方法在用户按下Back键的时候自动触发,但是我们可以重写这个方法,达到屏蔽Back事件的效果()。下面是onBackPressed()方法未重写时的代码:
  1. @Override
  2. public void onBackPressed() {
  3. // TODO Auto-generated method stub
  4. super.onBackPressed();
  5. }
复制代码
其中的关键在于
  1. super.onBackPressed();
复制代码
所谓默认的处理函数,其实按下返回键退出当前Activity的代码就在这短短一行代码里:
  1. super.onBackPressed();
复制代码
我们先不做任何处理,这时候的LibgdxActivity源码:


 

  1. public class LibgdxActivity extends AndroidApplication {
  2. Progress progress;
  3. MyGame mg;
  4. ApplicationGame ag;
  5. @Override
  6. public void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. progress=new Progress(this);
  9. mg=new MyGame(this);
  10. ag=new ApplicationGame(mg);
  11. initialize(ag, false);
  12. }
  13. @Override
  14. public void onBackPressed() {
  15. // TODO Auto-generated method stub
  16. super.onBackPressed();
  17. }

  18. }
复制代码
这时候运行结果还是一样的,我们按返回键退出Progress(Screen)的时候,程序还是直接退出了,并没有切换界面,而是直接退出了,现在我们将
  1. super.onBackPressed();
复制代码
这一段语句注释掉,即在Android(非libgdx)系统捕获Back事件时,不调用默认的处理函数。另外我对代码做了一些小的修改,比如标记该Screen是否是第一次启动,因为我们只需要在第一次启动的时候加载资源,今后再启动是不需要加载资源的。这些大家都可以从下面附上的代码中看到,基本都有注释。做完这些工作以后我们运行。



 

是不是可以自由切换了呢?在主页面我们按下按钮进入动画界面,在动画界面按下返回按钮返回主界面,这一切都只是在一个Activity中实现的!是不是很流畅!



 

这里还有一个小问题,我们捕捉了Back事件后,若在主界面按下Back键系统就不会再默认退出程序,这点我们可以在主界面的Screen(MyGame.java)的render()函数中同样的捕捉Back事件,捕捉到时退出。这里就不再赘述。




 

另外预告一下, 七讲之后我决定暂时不继续进行主干内容的介绍转而写几篇外篇,介绍我曾经用一天时间写出的一个很简单的俄罗斯方块的游戏,我将把源代码完全放出来,并且一步步地将这个基于SDK API的程序移植到libgdx下,毕竟我们学了这么多,终于可以实战了 ,也增加大家的一些信心。两部分的源码都将公开,请大家继续支持Testin杯Mkey libgdx游戏引擎系列教程。



 

还是附上一张我正在编写的libgdx游戏的截图


Screenshot_2012-07-08-11-58-13.png

2012-7-8 12:01 上传
下载附件(290.07 KB)



附上接下来几讲我们要移植的俄罗斯方块的截图,目前是用SDK的API写的,我们要做的是将其一步步得移植到libgdx上。


Screenshot_2012-07-08-12-04-26.png

2012-7-8 12:07 上传
下载附件(322.12 KB)




源码下载:
edu.nju.wsj.libgdx.rar(3.57 MB, 下载次数: 389)

2012-7-8 12:08 上传
点击文件名下载附件
下载积分: 下载豆 -2




Testin ID:ilovemkey@gmail.com 总的而言这个平台真的非常好用!我正是用这个平台发现了现在写的这个应用的一些内存管理方面的问题。另外建议能够在测试报告中看到某一台机型在整个执行过程中的cpu和内存占用的变化情况,这样可以更快地锁定问题。感谢Itestin让测试变得轻松了太多太多!

logo.png(28.18 KB, 下载次数: 5)

2012-7-8 12:01 上传

点击文件名下载附件

logo.png

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值