Android简易打地鼠

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

Android简易打地鼠_第1张图片
未开始时


Android简易打地鼠_第2张图片
开始后
Android简易打地鼠_第3张图片
停止播放背景音乐
Android简易打地鼠_第4张图片
时间到时

上面贴出的这几张就是该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简易打地鼠)