Android實現(xiàn)瘋狂連連看游戲之加載界面圖片和實現(xiàn)游戲Activity(四)
正如在《我的Android進(jìn)階之旅------>Android瘋狂連連看游戲的實現(xiàn)之狀態(tài)數(shù)據(jù)模型(三)》一文中看到的,在AbstractBoard的代碼中,當(dāng)程序需要創(chuàng)建N個Piece對象時,程序會直接調(diào)用ImageUtil的getPlayImages()方法去獲取圖片,該方法會隨機從res/drawable目錄中取得N張圖片。
下面是res/drawable目錄視圖:

為了讓getPlayImages()方法能隨機從res/drawable目錄中取得N張圖片,具體實現(xiàn)分為以下幾步:
- 通過反射來獲取R.drawable的所有Field(Android的每張圖片資源都會自動轉(zhuǎn)換為R.drawable的靜態(tài)Field),并將這些Field值添加到一個List集合中。
- 從第一步得到的List集合中隨機“抽取”N/2個圖片ID。
- 將第二步得到的N/2個圖片ID全部復(fù)制一份,這樣就得到了N個圖片ID,而且每個圖片ID都可以找到與之配對的。
- 將第三步得到的N個圖片ID再次“隨機打亂”,并根據(jù)圖片ID加載相應(yīng)的Bitmap對象,最后把圖片ID及對應(yīng)的Bitmap封裝成PieceImage對象后返回。
下面是ImageUtil類的代碼:cn\oyp\link\utils\ImageUtil.java
package cn.oyp.link.utils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import cn.oyp.link.R;
import cn.oyp.link.view.PieceImage;
/**
* 圖片資源工具類, 主要用于讀取游戲圖片資源值<br/>
* <br/>
* 關(guān)于本代碼介紹可以參考一下博客: <a rel="external nofollow" rel="external nofollow" >歐陽鵬的CSDN博客</a> <br/>
*/
public class ImageUtil {
/**
* 保存所有連連看圖片資源值(int類型)
*/
private static List<Integer> imageValues = getImageValues();
/**
* 獲取連連看所有圖片的ID(約定所有圖片ID以p_開頭)
*/
public static List<Integer> getImageValues() {
try {
// 得到R.drawable所有的屬性, 即獲取drawable目錄下的所有圖片
Field[] drawableFields = R.drawable.class.getFields();
List<Integer> resourceValues = new ArrayList<Integer>();
for (Field field : drawableFields) {
// 如果該Field的名稱以p_開頭
if (field.getName().indexOf("p_") != -1) {
resourceValues.add(field.getInt(R.drawable.class));
}
}
return resourceValues;
} catch (Exception e) {
return null;
}
}
/**
* 隨機從sourceValues的集合中獲取size個圖片ID, 返回結(jié)果為圖片ID的集合
*
* @param sourceValues
* 從中獲取的集合
* @param size
* 需要獲取的個數(shù)
* @return size個圖片ID的集合
*/
public static List<Integer> getRandomValues(List<Integer> sourceValues,
int size) {
// 創(chuàng)建一個隨機數(shù)生成器
Random random = new Random();
// 創(chuàng)建結(jié)果集合
List<Integer> result = new ArrayList<Integer>();
for (int i = 0; i < size; i++) {
try {
// 隨機獲取一個數(shù)字,大于、小于sourceValues.size()的數(shù)值
int index = random.nextInt(sourceValues.size());
// 從圖片ID集合中獲取該圖片對象
Integer image = sourceValues.get(index);
// 添加到結(jié)果集中
result.add(image);
} catch (IndexOutOfBoundsException e) {
return result;
}
}
return result;
}
/**
* 從drawable目錄中中獲取size個圖片資源ID(以p_為前綴的資源名稱), 其中size為游戲數(shù)量
*
* @param size
* 需要獲取的圖片ID的數(shù)量
* @return size個圖片ID的集合
*/
public static List<Integer> getPlayValues(int size) {
if (size % 2 != 0) {
// 如果該數(shù)除2有余數(shù),將size加1
size += 1;
}
// 再從所有的圖片值中隨機獲取size的一半數(shù)量,即N/2張圖片
List<Integer> playImageValues = getRandomValues(imageValues, size / 2);
// 將playImageValues集合的元素增加一倍(保證所有圖片都有與之配對的圖片),即N張圖片
playImageValues.addAll(playImageValues);
// 將所有圖片ID隨機“洗牌”
Collections.shuffle(playImageValues);
return playImageValues;
}
/**
* 將圖片ID集合轉(zhuǎn)換PieceImage對象集合,PieceImage封裝了圖片ID與圖片本身
*
* @param context
* @param resourceValues
* @return size個PieceImage對象的集合
*/
public static List<PieceImage> getPlayImages(Context context, int size) {
// 獲取圖片ID組成的集合
List<Integer> resourceValues = getPlayValues(size);
List<PieceImage> result = new ArrayList<PieceImage>();
// 遍歷每個圖片ID
for (Integer value : resourceValues) {
// 加載圖片
Bitmap bm = BitmapFactory.decodeResource(context.getResources(),
value);
// 封裝圖片ID與圖片本身
PieceImage pieceImage = new PieceImage(bm, value);
result.add(pieceImage);
}
return result;
}
/**
* 獲取選中標(biāo)識的圖片
* @param context
* @return 選中標(biāo)識的圖片
*/
public static Bitmap getSelectImage(Context context) {
Bitmap bm = BitmapFactory.decodeResource(context.getResources(),
R.drawable.selected);
return bm;
}
}
前面已經(jīng)給出了游戲界面的布局文件,該布局文件需要一個Activity來負(fù)責(zé)顯示,除此之外,Activity還需要為游戲界面的按鈕、GameView組件的事件提供事件監(jiān)聽器。
尤其是對于GameView組件,程序需要監(jiān)聽用戶的觸摸動作,當(dāng)用戶觸摸屏幕時,程序需要獲取用戶觸摸的是哪個方塊,并判斷是否需要“消除”該方塊。為了判斷是否消除該方塊,程序需要進(jìn)行如下判斷:
如果程序之前已經(jīng)選擇了某個方塊,就判斷當(dāng)前觸碰的方塊是否能和之前的方塊“相連”,如果可以相連,則消除兩個方塊;如果不能相連,則把當(dāng)前方塊設(shè)置為選中方塊。
如果程序之前沒有選中方塊,直接將當(dāng)前方塊設(shè)置為選中方塊。
下面是Activity的代碼:cn\oyp\link\LinkActivity.java
package cn.oyp.link;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import cn.oyp.link.board.GameService;
import cn.oyp.link.board.impl.GameServiceImpl;
import cn.oyp.link.utils.GameConf;
import cn.oyp.link.utils.LinkInfo;
import cn.oyp.link.view.GameView;
import cn.oyp.link.view.Piece;
/**
* 游戲Activity <br/>
* <br/>
* 關(guān)于本代碼介紹可以參考一下博客: <a rel="external nofollow" rel="external nofollow" >歐陽鵬的CSDN博客</a> <br/>
*/
public class LinkActivity extends Activity {
/**
* 游戲配置對象
*/
private GameConf config;
/**
* 游戲業(yè)務(wù)邏輯接口
*/
private GameService gameService;
/**
* 游戲界面
*/
private GameView gameView;
/**
* 開始按鈕
*/
private Button startButton;
/**
* 記錄剩余時間的TextView
*/
private TextView timeTextView;
/**
* 失敗后彈出的對話框
*/
private AlertDialog.Builder lostDialog;
/**
* 游戲勝利后的對話框
*/
private AlertDialog.Builder successDialog;
/**
* 定時器
*/
private Timer timer = new Timer();
/**
* 記錄游戲的剩余時間
*/
private int gameTime;
/**
* 記錄是否處于游戲狀態(tài)
*/
private boolean isPlaying;
/**
* 振動處理類
*/
private Vibrator vibrator;
/**
* 記錄已經(jīng)選中的方塊
*/
private Piece selectedPiece = null;
/**
* Handler類,異步處理
*/
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 0x123:
timeTextView.setText("剩余時間: " + gameTime);
gameTime--; // 游戲剩余時間減少
// 時間小于0, 游戲失敗
if (gameTime < 0) {
// 停止計時
stopTimer();
// 更改游戲的狀態(tài)
isPlaying = false;
// 失敗后彈出對話框
lostDialog.show();
return;
}
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 初始化界面
init();
}
/**
* 初始化游戲的方法
*/
private void init() {
config = new GameConf(8, 9, 2, 10, GameConf.DEFAULT_TIME, this);
// 得到游戲區(qū)域?qū)ο?
gameView = (GameView) findViewById(R.id.gameView);
// 獲取顯示剩余時間的文本框
timeTextView = (TextView) findViewById(R.id.timeText);
// 獲取開始按鈕
startButton = (Button) this.findViewById(R.id.startButton);
// 獲取振動器
vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
// 初始化游戲業(yè)務(wù)邏輯接口
gameService = new GameServiceImpl(this.config);
// 設(shè)置游戲邏輯的實現(xiàn)類
gameView.setGameService(gameService);
// 為開始按鈕的單擊事件綁定事件監(jiān)聽器
startButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View source) {
startGame(GameConf.DEFAULT_TIME);
}
});
// 為游戲區(qū)域的觸碰事件綁定監(jiān)聽器
this.gameView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
gameViewTouchDown(e);
}
if (e.getAction() == MotionEvent.ACTION_UP) {
gameViewTouchUp(e);
}
return true;
}
});
// 初始化游戲失敗的對話框
lostDialog = createDialog("Lost", "游戲失敗! 重新開始", R.drawable.lost)
.setPositiveButton("確定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
startGame(GameConf.DEFAULT_TIME);
}
});
// 初始化游戲勝利的對話框
successDialog = createDialog("Success", "游戲勝利! 重新開始",
R.drawable.success).setPositiveButton("確定",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
startGame(GameConf.DEFAULT_TIME);
}
});
}
@Override
protected void onPause() {
// 暫停游戲
stopTimer();
super.onPause();
}
@Override
protected void onResume() {
// 如果處于游戲狀態(tài)中
if (isPlaying) {
// 以剩余時間重新開始游戲
startGame(gameTime);
}
super.onResume();
}
/**
* 觸碰游戲區(qū)域的處理方法
*
* @param event
*/
private void gameViewTouchDown(MotionEvent event) {
// 獲取GameServiceImpl中的Piece[][]數(shù)組
Piece[][] pieces = gameService.getPieces();
// 獲取用戶點擊的x座標(biāo)
float touchX = event.getX();
// 獲取用戶點擊的y座標(biāo)
float touchY = event.getY();
// 根據(jù)用戶觸碰的座標(biāo)得到對應(yīng)的Piece對象
Piece currentPiece = gameService.findPiece(touchX, touchY);
// 如果沒有選中任何Piece對象(即鼠標(biāo)點擊的地方?jīng)]有圖片), 不再往下執(zhí)行
if (currentPiece == null)
return;
// 將gameView中的選中方塊設(shè)為當(dāng)前方塊
this.gameView.setSelectedPiece(currentPiece);
// 表示之前沒有選中任何一個Piece
if (this.selectedPiece == null) {
// 將當(dāng)前方塊設(shè)為已選中的方塊, 重新將GamePanel繪制, 并不再往下執(zhí)行
this.selectedPiece = currentPiece;
this.gameView.postInvalidate();
return;
}
// 表示之前已經(jīng)選擇了一個
if (this.selectedPiece != null) {
// 在這里就要對currentPiece和prePiece進(jìn)行判斷并進(jìn)行連接
LinkInfo linkInfo = this.gameService.link(this.selectedPiece,
currentPiece);
// 兩個Piece不可連, linkInfo為null
if (linkInfo == null) {
// 如果連接不成功, 將當(dāng)前方塊設(shè)為選中方塊
this.selectedPiece = currentPiece;
this.gameView.postInvalidate();
} else {
// 處理成功連接
handleSuccessLink(linkInfo, this.selectedPiece, currentPiece,
pieces);
}
}
}
/**
* 觸碰游戲區(qū)域的處理方法
*
* @param e
*/
private void gameViewTouchUp(MotionEvent e) {
this.gameView.postInvalidate();
}
/**
* 以gameTime作為剩余時間開始或恢復(fù)游戲
*
* @param gameTime
* 剩余時間
*/
private void startGame(int gameTime) {
// 如果之前的timer還未取消,取消timer
if (this.timer != null) {
stopTimer();
}
// 重新設(shè)置游戲時間
this.gameTime = gameTime;
// 如果游戲剩余時間與總游戲時間相等,即為重新開始新游戲
if (gameTime == GameConf.DEFAULT_TIME) {
// 開始新的游戲游戲
gameView.startGame();
}
isPlaying = true;
this.timer = new Timer();
// 啟動計時器 , 每隔1秒發(fā)送一次消息
this.timer.schedule(new TimerTask() {
public void run() {
handler.sendEmptyMessage(0x123);
}
}, 0, 1000);
// 將選中方塊設(shè)為null。
this.selectedPiece = null;
}
/**
* 成功連接后處理
*
* @param linkInfo
* 連接信息
* @param prePiece
* 前一個選中方塊
* @param currentPiece
* 當(dāng)前選擇方塊
* @param pieces
* 系統(tǒng)中還剩的全部方塊
*/
private void handleSuccessLink(LinkInfo linkInfo, Piece prePiece,
Piece currentPiece, Piece[][] pieces) {
// 它們可以相連, 讓GamePanel處理LinkInfo
this.gameView.setLinkInfo(linkInfo);
// 將gameView中的選中方塊設(shè)為null
this.gameView.setSelectedPiece(null);
this.gameView.postInvalidate();
// 將兩個Piece對象從數(shù)組中刪除
pieces[prePiece.getIndexX()][prePiece.getIndexY()] = null;
pieces[currentPiece.getIndexX()][currentPiece.getIndexY()] = null;
// 將選中的方塊設(shè)置null。
this.selectedPiece = null;
// 手機振動(100毫秒)
this.vibrator.vibrate(100);
// 判斷是否還有剩下的方塊, 如果沒有, 游戲勝利
if (!this.gameService.hasPieces()) {
// 游戲勝利
this.successDialog.show();
// 停止定時器
stopTimer();
// 更改游戲狀態(tài)
isPlaying = false;
}
}
/**
* 創(chuàng)建對話框的工具方法
*
* @param title
* 標(biāo)題
* @param message
* 內(nèi)容
* @param imageResource
* 圖片
* @return
*/
private AlertDialog.Builder createDialog(String title, String message,
int imageResource) {
return new AlertDialog.Builder(this).setTitle(title)
.setMessage(message).setIcon(imageResource);
}
/**
* 停止計時
*/
private void stopTimer() {
// 停止定時器
this.timer.cancel();
this.timer = null;
}
}
該Activity用了兩個類,這兩個類在下一篇博客中再進(jìn)行相關(guān)描述。
GameConf:負(fù)責(zé)管理游戲的初始化設(shè)置信息。
GameService:負(fù)責(zé)游戲的邏輯實現(xiàn)。
關(guān)于具體的實現(xiàn)步驟,請參考下面的鏈接:
我的Android進(jìn)階之旅------>Android瘋狂連連看游戲的實現(xiàn)之游戲效果預(yù)覽(一)
我的Android進(jìn)階之旅------>Android瘋狂連連看游戲的實現(xiàn)之開發(fā)游戲界面(二)
我的Android進(jìn)階之旅------>Android瘋狂連連看游戲的實現(xiàn)之狀態(tài)數(shù)據(jù)模型(三)
我的Android進(jìn)階之旅------>Android瘋狂連連看游戲的實現(xiàn)之實現(xiàn)游戲邏輯(五)
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實現(xiàn)背景可滑動登錄界面 (不壓縮背景彈出鍵盤)
這篇文章主要介紹了Android實現(xiàn)背景可滑動登錄界面 (不壓縮背景彈出鍵盤),需要的朋友可以參考下2017-04-04
android自定義開關(guān)控件-SlideSwitch的實例
本篇文章主要介紹了android自定義開關(guān)控件-SlideSwitch的實例,實現(xiàn)了手機控件開關(guān)的功能,感興趣的小伙伴們可以參考一下。2016-11-11
android studio 使用Mocklocation虛擬定位
這篇文章主要介紹了android studio 使用Mocklocation虛擬定位總結(jié),本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12
android activity設(shè)置無標(biāo)題實現(xiàn)全屏
本文將詳細(xì)介紹Android如何設(shè)置Activity全屏和無標(biāo)題的實現(xiàn)方法,需要的朋友可以參考下2012-12-12
解決android 顯示內(nèi)容被底部導(dǎo)航欄遮擋的問題
今天小編就為大家分享一篇解決android 顯示內(nèi)容被底部導(dǎo)航欄遮擋的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Android Studio去除界面默認(rèn)標(biāo)題欄的方法
這篇文章主要介紹了Android Studio去除界面默認(rèn)標(biāo)題欄的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2007-09-09

