先看看基本的效果(背景圖片及老鼠圖片來(lái)源于網(wǎng)絡(luò))
上面貼出的這幾張就是該APP的游戲界面,下面談?wù)勔婚_(kāi)始設(shè)計(jì)的基本思路
在布局方面,整體是一個(gè)線性布局,最下方是由兩個(gè)按鈕和一個(gè)文本組成
關(guān)于上方的游戲畫(huà)面,有兩種設(shè)計(jì)思想
1.最外層采用幀布局,設(shè)置背景為有9個(gè)空洞的那張圖片,在幀布局中使用相對(duì)布局放置9個(gè)ImageView,分別放置在洞的位置。在畫(huà)面左上角,設(shè)置一個(gè)CheckBox用于開(kāi)關(guān)背景音樂(lè)
2.最外層采用幀布局,設(shè)置背景為有9個(gè)空洞的那張圖片,在幀布局中使用相對(duì)布局放置9個(gè)按鈕,分別放置在洞的位置。在畫(huà)面左上角,設(shè)置一個(gè)CheckBox用于開(kāi)關(guān)背景音樂(lè)
關(guān)于代碼,主要的實(shí)現(xiàn)在于背景音樂(lè)的控制、老鼠的彈出、下方游戲時(shí)間的控制
背景音樂(lè)控制:采用了Service的基本用法,根據(jù)用戶在游戲界面CheckBox的選中與否,分別開(kāi)啟和關(guān)閉服務(wù),在服務(wù)中使用MediaPlayer播放背景音樂(lè)
老鼠的彈出:這個(gè)與游戲畫(huà)面的不同設(shè)計(jì)有關(guān)。? 對(duì)于第1種,是采用一個(gè)數(shù)組存放9個(gè)ImageView的對(duì)象,同時(shí)在線程中每隔1s生成一次隨機(jī)數(shù)(0~8),剛好與ImageView的對(duì)象在數(shù)組中的序號(hào)對(duì)應(yīng),然后將對(duì)應(yīng)的ImageView背景設(shè)置為那張老鼠。?? 對(duì)于第2種,是制作9張老鼠與背景的合成圖分別作為背景,即每個(gè)洞分別與老鼠進(jìn)行合成,然后將這9張圖用一個(gè)數(shù)組存放起來(lái),同時(shí)在線程中每隔1s生成一次隨機(jī)數(shù)(0~8),剛好與9張合成圖在數(shù)組中的序號(hào)對(duì)應(yīng),然后將包裹9個(gè)按鈕的相對(duì)布局的背景設(shè)置為這張圖片,由于圖片的覆蓋,所以看上去老鼠像是彈出的
下方游戲時(shí)間控制:當(dāng)用戶點(diǎn)擊開(kāi)始后,開(kāi)啟線程控制總時(shí)間的線程、彈出老鼠的線程和計(jì)時(shí)線程,總時(shí)間設(shè)定為60s,控制總時(shí)間的線程即sleep共60s,時(shí)間到后關(guān)閉點(diǎn)彈老鼠的線程和計(jì)時(shí)線程,在其sleep期間,計(jì)時(shí)線程每隔1s改變一次文本顯示的內(nèi)容(從60到0)
相比上次的Android簡(jiǎn)易老虎機(jī)? ,這次也是使用線程的相關(guān)知識(shí),不同的就是添加了服務(wù)用于后臺(tái)控制音樂(lè)的播放,使用了幀布局,而且解決了上次的一個(gè)小bug,就是在點(diǎn)擊開(kāi)始游戲后(開(kāi)啟各種線程),再次重復(fù)點(diǎn)擊開(kāi)始會(huì)造成線程重復(fù)啟動(dòng),使老虎機(jī)中的轉(zhuǎn)動(dòng)絮亂,解決方法是使用一個(gè)標(biāo)識(shí)flag,初始化為true,而且只有在flag為true時(shí)點(diǎn)擊按鈕才有效果,所以當(dāng)點(diǎn)擊開(kāi)始后,會(huì)設(shè)置flag為false,同時(shí)啟動(dòng)一個(gè)線程,讓該線程sleep一定時(shí)間,在sleep結(jié)束后設(shè)置flag為true,所以在該線程sleep期間,flag一直為flase,那么點(diǎn)擊開(kāi)始按鈕是無(wú)效的
更為詳細(xì)的解釋都放在代碼的注釋中,這里貼出實(shí)現(xiàn)該游戲的主要代碼,完整代碼已上傳到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];//保存九個(gè)洞對(duì)應(yīng)的按鈕
private MyHandler myHandler=newMyHandler();
private SumTime sumTime;
private GameTime gameTime;
private GoTime goTime;
private ClickTime clickTime;
private intt=59;
private intsumMouse=0;//彈出老鼠總個(gè)數(shù)
private intclickMouse=0;//用戶點(diǎn)中的老鼠個(gè)數(shù)
private intmouseCheckedId=0;//當(dāng)前彈出圖片在數(shù)組中的序號(hào)
private booleanflag=true;//用于限制開(kāi)始按鈕的有效點(diǎn)擊次數(shù)
@Override
protected void onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game_layout);
initView();//初始化控件
initAtrr();//添加圖片id到數(shù)組bgAtrr中
initListener();//初始化監(jiān)聽(tīng)器,將監(jiān)聽(tīng)器與開(kāi)始和結(jié)束按鈕進(jìn)行綁定
judgeMusic();//播放背景音樂(lè)
}
private void judgeMusic() {
Intent intent Service=newIntent(GameActivity.this,MyMusicService.class);
if(cb_sound.isChecked()) {
stopService(intentService);
}else{
startService(intentService);
}
}
/**
* 計(jì)時(shí)器,游戲開(kāi)始,該線程sleep一分鐘,時(shí)間到后,終止彈老鼠進(jìn)程和計(jì)時(shí)進(jìn)程并彈出提示框,傳入MyHander的參數(shù)為0
*/
class SumTime extendsThread{
@Override
public voidrun() {
super.run();
try{
sleep(60000);
}catch(InterruptedExceptione) {
e.printStackTrace();
}
gameTime.isStop();//調(diào)用gameTime的isStop方法,使其停止彈出老鼠
goTime.isStop();//調(diào)用goTime的isStop方法,結(jié)束該線程
Message message=Message.obtain();
message.what=0;
myHandler.sendMessage(message);
}
}
/**
* 隨機(jī)彈出老鼠圖片,傳入到MyHander的參數(shù)為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對(duì)象
message.what=1;
myHandler.sendMessage(message);
}
}
}
/**
* 時(shí)間計(jì)數(shù),傳入到MyHander的參數(shù)為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;
}
}
/**
* 根據(jù)傳入的參數(shù)進(jìn)行處理,傳入為0則表示游戲結(jié)束,傳入為1表示開(kāi)始游戲,傳入2是倒時(shí)器開(kāi)始
*/
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("游戲結(jié)束");
dialog.setMessage("本輪地鼠總數(shù)為: "+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++;//判斷當(dāng)前老鼠所在的RadioButton的id與用戶點(diǎn)擊的是否一致,若一致則為打中地鼠
}
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;
}
}
}
下面列出這次開(kāi)發(fā)中的一些小知識(shí)點(diǎn):
設(shè)置手機(jī)強(qiáng)制橫屏

在LInearLayout中設(shè)置 orientation屬性為vertical時(shí),layout_gravity只能夠使用橫向的如:left, right, center_horizontal?? 設(shè)置 orientation屬性為horizontal時(shí),layout_gravity只能夠使用縱向的,如:top, bottom, center_vertical
在src中放置的是原圖,是不會(huì)被拉伸的,如果在水平或者垂直方向需要拉伸,分別使用scaleX和scaleY,參數(shù)為縮放比例
在Activity中調(diào)用onRestart方法可以使當(dāng)前Activity回到原始狀態(tài)
使用Service,需要寫(xiě)一個(gè)類繼承自Service,在需要使用服務(wù)的Activity中使用 Intent intent = new Intent(this, 定義的類.class); 使用startService(intent)開(kāi)啟服務(wù) ?,記得使用stopService來(lái)關(guān)閉服務(wù),整體操作類似Activity的跳轉(zhuǎn)
音頻文件放在raw文件夾中(可能需要自己創(chuàng)建),然后使用 MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.music) 創(chuàng)建播放器對(duì)象并指定音頻位置,然后使用mediaPlayer.setLooping(true)設(shè)置循環(huán)播放,最后使用mediaPlayer.start()開(kāi)始播放,在退出時(shí)使用mediaPlayer.release()釋放資源