Android简易打地鼠

作者: 雾里看花最是迷人 | 来源:发表于2016-07-23 18:35 被阅读1762次

    先看看基本的效果(背景图片及老鼠图片来源于网络)

    未开始时
    开始后
    停止播放背景音乐
    时间到时

    上面贴出的这几张就是该APP的游戏界面,下面谈谈一开始设计的基本思路

    在布局方面,整体是一个线性布局,最下方是由两个按钮和一个文本组成

    关于上方的游戏画面,有两种设计思想

    1.最外层采用帧布局,设置背景为有9个空洞的那张图片,在帧布局中使用相对布局放置9个ImageView,分别放置在洞的位置。在画面左上角,设置一个CheckBox用于开关背景音乐

    2.最外层采用帧布局,设置背景为有9个空洞的那张图片,在帧布局中使用相对布局放置9个按钮,分别放置在洞的位置。在画面左上角,设置一个CheckBox用于开关背景音乐

    关于代码,主要的实现在于背景音乐的控制、老鼠的弹出、下方游戏时间的控制

    背景音乐控制:采用了Service的基本用法,根据用户在游戏界面CheckBox的选中与否,分别开启和关闭服务,在服务中使用MediaPlayer播放背景音乐

    老鼠的弹出:这个与游戏画面的不同设计有关。  对于第1种,是采用一个数组存放9个ImageView的对象,同时在线程中每隔1s生成一次随机数(0~8),刚好与ImageView的对象在数组中的序号对应,然后将对应的ImageView背景设置为那张老鼠。   对于第2种,是制作9张老鼠与背景的合成图分别作为背景,即每个洞分别与老鼠进行合成,然后将这9张图用一个数组存放起来,同时在线程中每隔1s生成一次随机数(0~8),刚好与9张合成图在数组中的序号对应,然后将包裹9个按钮的相对布局的背景设置为这张图片,由于图片的覆盖,所以看上去老鼠像是弹出的

    下方游戏时间控制:当用户点击开始后,开启线程控制总时间的线程、弹出老鼠的线程和计时线程,总时间设定为60s,控制总时间的线程即sleep共60s,时间到后关闭点弹老鼠的线程和计时线程,在其sleep期间,计时线程每隔1s改变一次文本显示的内容(从60到0)

    相比上次的Android简易老虎机  ,这次也是使用线程的相关知识,不同的就是添加了服务用于后台控制音乐的播放,使用了帧布局,而且解决了上次的一个小bug,就是在点击开始游戏后(开启各种线程),再次重复点击开始会造成线程重复启动,使老虎机中的转动絮乱,解决方法是使用一个标识flag,初始化为true,而且只有在flag为true时点击按钮才有效果,所以当点击开始后,会设置flag为false,同时启动一个线程,让该线程sleep一定时间,在sleep结束后设置flag为true,所以在该线程sleep期间,flag一直为flase,那么点击开始按钮是无效的

    更为详细的解释都放在代码的注释中,这里贴出实现该游戏的主要代码,完整代码已上传到GitHub 

    public class GameActivity extends Activity{

    private Button playGame,overGame;

    private TextView times;

    private Button btn1,btn2,btn3,btn4,btn5,btn6,btn7,btn8,btn9;

    private LinearLayout ll_bg_show;

    private CheckBox cb_sound;

    private intbgAtrr[] =new int[9];//保存九张带有老鼠的背景图

    private intbtnAtrr[] =new int[9];//保存九个洞对应的按钮

    private MyHandler myHandler=newMyHandler();

    private SumTime sumTime;

    private GameTime gameTime;

    private GoTime goTime;

    private ClickTime clickTime;

    private intt=59;

    private intsumMouse=0;//弹出老鼠总个数

    private intclickMouse=0;//用户点中的老鼠个数

    private intmouseCheckedId=0;//当前弹出图片在数组中的序号

    private booleanflag=true;//用于限制开始按钮的有效点击次数

    @Override

    protected void onCreate(BundlesavedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.game_layout);

    initView();//初始化控件

    initAtrr();//添加图片id到数组bgAtrr中

    initListener();//初始化监听器,将监听器与开始和结束按钮进行绑定

    judgeMusic();//播放背景音乐

    }

    private void judgeMusic() {

    Intent intent Service=newIntent(GameActivity.this,MyMusicService.class);

    if(cb_sound.isChecked()) {

    stopService(intentService);

    }else{

    startService(intentService);

    }

    }

    /**

    * 计时器,游戏开始,该线程sleep一分钟,时间到后,终止弹老鼠进程和计时进程并弹出提示框,传入MyHander的参数为0

    */

    class SumTime extendsThread{

    @Override

    public voidrun() {

    super.run();

    try{

    sleep(60000);

    }catch(InterruptedExceptione) {

    e.printStackTrace();

    }

    gameTime.isStop();//调用gameTime的isStop方法,使其停止弹出老鼠

    goTime.isStop();//调用goTime的isStop方法,结束该线程

    Message message=Message.obtain();

    message.what=0;

    myHandler.sendMessage(message);

    }

    }

    /**

    * 随机弹出老鼠图片,传入到MyHander的参数为1

    */

    class GameTime extends Thread{

    private boolean isStoped=false;

    private void isStop() {

    isStoped=true;

    gameTime.interrupt();

    }

    @Override

    public voidrun() {

    super.run();

    while(!isStoped) {

    try{

    sleep(1000);

    }catch(InterruptedExceptione) {

    e.printStackTrace();

    }

    Message message=Message.obtain();//获取Message对象

    message.what=1;

    myHandler.sendMessage(message);

    }

    }

    }

    /**

    * 时间计数,传入到MyHander的参数为2

    */

    class GoTime extends Thread{

    private boolean isStoped=false;

    private void isStop() {

    isStoped=true;

    goTime.interrupt();

    }

    @Override

    public void run() {

    super.run();

    while(!isStoped) {

    try{

    sleep(1000);

    }catch(InterruptedExceptione) {

    e.printStackTrace();

    }

    Message message=Message.obtain();

    message.what=2;

    myHandler.sendMessage(message);

    }

    }

    }

    class ClickTime implements Runnable{

    @Override

    public voidrun() {

    try{

    Thread.sleep(60000);

    }catch(InterruptedExceptione) {

    e.printStackTrace();

    }

    flag=true;

    }

    }

    /**

    * 根据传入的参数进行处理,传入为0则表示游戏结束,传入为1表示开始游戏,传入2是倒时器开始

    */

    class MyHandler extends Handler{

    @Override

    public void handleMessage(Messagemsg) {

    super.handleMessage(msg);

    switch(msg.what) {

    case0:

    sumTime.interrupt();

    AlertDialog.Builderdialog=newAlertDialog.Builder(GameActivity.this);

    dialog.setTitle("游戏结束");

    dialog.setMessage("本轮地鼠总数为: "+sumMouse+" 只\n"+

    "您逮住的地鼠共:"+clickMouse+" 只\n"+

    "捕获率:"+clickMouse*100/sumMouse+"%"

    );

    dialog.setCancelable(false);

    dialog.setPositiveButton("再试一次",newDialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterfacedialog,intwhich) {

    times.setText(60+"");

    t=60;

    sumMouse=0;

    clickMouse=0;

    onRestart();

    }

    });

    dialog.show();

    break;

    case1:

    Random random=newRandom();

    int index=random.nextInt(9);

    ll_bg_show.setBackgroundResource(bgAtrr[index]);

    sumMouse++;

    if(btnAtrr[index] ==mouseCheckedId) {

    clickMouse++;//判断当前老鼠所在的RadioButton的id与用户点击的是否一致,若一致则为打中地鼠

    }

    break;

    case2:

    times.setText(t+"");

    t--;

    break;

    }

    }

    }

    class ButtonListener implementsView.OnClickListener{

    @Override

    public voidonClick(Viewv) {

    mouseCheckedId=v.getId();

    switch(v.getId()) {

    caseR.id.bt_play:

    if(flag) {

    sumTime=newSumTime();

    sumTime.start();

    gameTime=newGameTime();

    gameTime.start();

    goTime=newGoTime();

    goTime.start();

    }

    flag=false;

    clickTime=newClickTime();

    newThread(clickTime).start();

    break;

    caseR.id.bt_over:

    finish();

    Intent intent=newIntent(GameActivity.this,MyMusicService.class);

    stopService(intent);

    break;

    caseR.id.cb_sound:

    judgeMusic();

    break;

    }

    }

    }

    下面列出这次开发中的一些小知识点:

    设置手机强制横屏

    在配置文件中

    在LInearLayout中设置 orientation属性为vertical时,layout_gravity只能够使用横向的如:left, right, center_horizontal   设置 orientation属性为horizontal时,layout_gravity只能够使用纵向的,如:top, bottom, center_vertical

    在src中放置的是原图,是不会被拉伸的,如果在水平或者垂直方向需要拉伸,分别使用scaleX和scaleY,参数为缩放比例

    在Activity中调用onRestart方法可以使当前Activity回到原始状态

    使用Service,需要写一个类继承自Service,在需要使用服务的Activity中使用 Intent intent = new Intent(this, 定义的类.class); 使用startService(intent)开启服务  ,记得使用stopService来关闭服务,整体操作类似Activity的跳转

    音频文件放在raw文件夹中(可能需要自己创建),然后使用 MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.music) 创建播放器对象并指定音频位置,然后使用mediaPlayer.setLooping(true)设置循环播放,最后使用mediaPlayer.start()开始播放,在退出时使用mediaPlayer.release()释放资源


    相关文章

      网友评论

        本文标题:Android简易打地鼠

        本文链接:https://www.haomeiwen.com/subject/durwjttx.html