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

167
回复
896889
查看
  [复制链接]

30

主题

350

帖子

1089

安币

Android大神

Rank: 6Rank: 6

QQ达人

发表于 2012-7-8 12:12:06 | 显示全部楼层 |阅读模式
本帖最后由 耒佲請ぁ留埗 于 2012-8-3 11:24 编辑


新浪微博:http://weibo.com/u/1634658054  

以下系列教程按讲课顺序排列,也是推荐阅读的顺序

第一讲:libgdx游戏引擎教程(一)性能优良的游戏引擎—libgdx http://www.apkbus.com/android-57355-1-1.html

第二讲: libgdx游戏引擎教程(二) ApplicationListener,着手第一个程序http://www.apkbus.com/android-57372-1-1.html

第三讲:libgdx游戏引擎教程()libgdx粒子系统的使用 http://www.apkbus.com/android-57456-1-1.html

外篇一:libgdx游戏引擎教程外篇(一)优美的自定义进度条(附源码) http://www.apkbus.com/android-57523-1-1.html

第四讲:libgdx游戏引擎教程(四) 游戏正式开始,资源异步加载(附源码)http://www.apkbus.com/android-57716-1-1.html

第五讲 libgdx游戏引擎教程(五)多游戏界面的实现(一)附源码 http://www.apkbus.com/android-57936-1-1.html

第六讲 libgdx游戏引擎教程(六)多游戏界面的实现(二)附源码 http://www.apkbus.com/android-57991-1-1.html  

第七讲 libgdx游戏引擎教程(七) 在libgdx中拦截Android按键事件 http://www.apkbus.com/android-58030-1-1.html  

外篇二: libgdx游戏引擎教程外篇(二)libgdx移植俄罗斯方块(附源码) http://www.apkbus.com/android-58102-1-1.html   

实战一:libgdx游戏引擎教程实战篇(一)移植俄罗斯方块(附源码) http://www.apkbus.com/android-58404-1-1.html  

实战二:libgdx游戏引擎教程实战篇(二)移植俄罗斯方块(附源码) http://www.apkbus.com/android-58408-1-1.html  

实战三:libgdx游戏引擎教程实战篇(三)移植俄罗斯方块(附源码) http://www.apkbus.com/android-58414-1-1.html  

实战四:libgdx游戏引擎教程实战篇(四)移植俄罗斯方块(附源码) http://www.apkbus.com/android-58418-1-1.html  

实战五:libgdx游戏引擎教程实战篇(五)移植俄罗斯方块(附源码) http://www.apkbus.com/android-58419-1-1.html

实战六:libgdx游戏引擎教程实战篇(六)移植俄罗斯方块(附源码) http://www.apkbus.com/android-58760-1-1.html

第八讲 libgdx游戏引擎教程(八) libgdx中的用户手势识别(一)附源码 http://www.apkbus.com/android-58933-1-1.html

第九讲 libgdx游戏引擎教程(九) libgdx中的用户手势识别(二)附源码 http://www.apkbus.com/android-59152-1-1.html

外篇三: libgdx游戏引擎教程外篇(三) 优美的自定义对话框(附源码) http://www.apkbus.com/android-59654-1-1.html  

第十讲:  libgdx游戏引擎教程(十)演员类的复杂动画(一)(附源码) http://www.apkbus.com/android-59998-1-2.html

第十一讲:libgdx游戏引擎教程(十一)演员类的复杂动画(二)(附源码) http://www.apkbus.com/android-60053-1-1.html

第十二讲:libgdx游戏引擎教程(十二)演员类的复杂动画(三)(附源码) http://www.apkbus.com/android-60194-1-1.html   

第十三讲: libgdx游戏引擎教程(十三)演员类的用户交互功能详解(附源码)  http://www.apkbus.com/android-60457-1-1.html

第十四讲:libgdx游戏引擎教程(十四)libgdx中常用的控件(附源码) http://www.apkbus.com/android-60569-1-1.html

最新更新 第十五讲 :libgdx游戏引擎教程(十五)在Libgdx中仿微信帮助滑动效果(附源码) http://www.apkbus.com/android-60822-1-1.html

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

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

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



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



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



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






    就像上图中我们看到的那样,所谓的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.     }

  12.     @Override
  13.     public void render(float arg0) {
  14.        // TODO Auto-generated method stub
  15.        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  16.        Gdx.gl.glClearColor(1f,1f,1f,0f);
  17.        stage.act(Gdx.graphics.getDeltaTime());
  18.        stage.draw();
  19.       
  20.        if(Gdx.input.isKeyPressed(Input.Keys.BACK))
  21.            System.out.println("Back Pressed");
  22.       
  23.        if(!manager.update()){
  24.            bar.setProgress(manager.getProgress()*100);
  25.        }
  26.        //加载完成且之前没有初始化过AnimalActor,且在手触摸屏幕时初始化AnimalActor,并将进度条从舞台中移除,并加入AnimalActor对象
  27.        if(!hasini&&manager.update()){
  28.            bar.setProgress(100);
  29.            if(Gdx.input.isTouched()){
  30.            stage.removeActor(bar);
  31.            animal.iniResource();
  32.            stage.addActor(animal);
  33.            hasini=true;
  34.            }
  35.        }
  36.        //我们做一个标记,看看未加载(Queued)完成的资源和已加载完成的资源的数量(Loaded)
  37.        if(!manager.update()){
  38.     System.out.println("QueuedAssets:"+manager.getQueuedAssets());
  39.        System.out.println("LoadedAssets:"+manager.getLoadedAssets());
  40.        System.out.println("Progress:"+manager.getProgress());
  41.        }
  42.     }
  43.     @Override
  44.     public void resize(int arg0, int arg1) {
  45.        // TODO Auto-generated method stub
  46.       
  47.     }
  48.     @Override
  49.     public void resume() {
  50.        // TODO Auto-generated method stub
  51.       
  52.     }
  53.     @Override
  54.     public void show() {
  55.        // TODO Auto-generated method stub
  56.        bar=new ProgressBar(0,0);
  57.        //新建一个舞台
  58.        stage=new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(), true);
  59.        stage.addActor(bar);
  60.        //记得初始化一下AssetManager实例
  61.        manager=new AssetManager();
  62.        //传入AssetManger的引用,便于animal的资源初始化,但是注意了,只有在调用iniResourse()后资源才被初始化
  63.        animal=new AnimalActor(manager);
  64.        //把资源加入加载列表,这里我放了一个29帧的动画,在asset文件夹下animal下有29张图片
  65.        for(int i=1;i<30;i++){
  66.            manager.load("animal/"+i+".png", Texture.class);
  67.        }
  68.     }



  69.     @Override
  70.     public void dispose() {
  71.        // TODO Auto-generated method stub
  72.        //同样的,在结束时释放资源
  73.        bar.dispose();
  74.        animal.dispose();
  75.        manager.clear();
  76.        manager.dispose();
  77.     }

  78.     @Override
  79.     public void pause() {
  80.        // TODO Auto-generated method stub

  81.     }


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



按下返回键

     我们发现事件确实被捕捉到了。但是这里又存在一个问题,由于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键的触摸事件。我们来看看输出:
确实只有一个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键。诶??还是退出了?我们再来看一看输出。



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

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


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


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


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



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


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



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




源码下载:



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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
君见一叶舟,出没风波里

2826

主题

7684

帖子

9622

安币

管理员

Rank: 9Rank: 9Rank: 9

QQ达人最佳新人活跃会员热心会员推广达人灌水之王突出贡献

QQ
发表于 2012-7-8 12:42:16 | 显示全部楼层
{:soso_e179:} 越来越精彩了啊!!!!

30

主题

350

帖子

1089

安币

Android大神

Rank: 6Rank: 6

QQ达人

 楼主| 发表于 2012-7-8 14:34:44 | 显示全部楼层
chino 发表于 2012-7-8 12:42
越来越精彩了啊!!!!

压力巨大,要继续努力!
君见一叶舟,出没风波里

0

主题

18

帖子

19

安币

初级码农

Rank: 1

发表于 2012-7-8 19:02:52 | 显示全部楼层
我决定要跟着楼主做游戏了

主题

帖子

安币

游客

发表于 2012-7-8 19:10:42 | 显示全部楼层
正在学习

0

主题

7

帖子

2

安币

初级码农

Rank: 1

发表于 2012-7-9 11:56:01 | 显示全部楼层
{:4_136:}学习

2

主题

104

帖子

1029

安币

Android大神

Rank: 6Rank: 6

发表于 2012-7-9 23:41:19 | 显示全部楼层
再接再厉

0

主题

12

帖子

66

安币

程序猿

Rank: 2

QQ达人

发表于 2012-7-10 14:24:50 | 显示全部楼层
牛逼牛逼牛逼牛逼牛逼牛逼牛逼牛逼牛逼
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )