正如在《我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)》一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piece对象时,程序会直接调用ImageUtil的getPlayImages()方法去获取图片,该方法会随机从res/drawable目录中取得N张图片。

下面是res/drawable目录视图:

Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)_第1张图片" width="325" height="575" style="border:1px solid black;">

为了让getPlayImages()方法能随机从res/drawable目录中取得N张图片,具体实现分为以下几步:

  1. 通过反射来获取R.drawable的所有Field(Android的每张图片资源都会自动转换为R.drawable的静态Field),并将这些Field值添加到一个List集合中。
  2. 从第一步得到的List集合中随机“抽取”N/2个图片ID。
  3. 将第二步得到的N/2个图片ID全部复制一份,这样就得到了N个图片ID,而且每个图片ID都可以找到与之配对的。
  4. 将第三步得到的N个图片ID再次“随机打乱”,并根据图片ID加载相应的Bitmap对象,最后把图片ID及对应的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/> * 关于本代码介绍可以参考一下博客: <a href="http://blog.csdn.net/ouyang_peng">欧阳鹏的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, 返回结果为图片ID的集合 *  * @param sourceValues *            从中获取的集合 * @param size *            需要获取的个数 * @return size个图片ID的集合 */public static List<Integer> getRandomValues(List<Integer> sourceValues,int size) {// 创建一个随机数生成器Random random = new Random();// 创建结果集合List<Integer> result = new ArrayList<Integer>();for (int i = 0; i < size; i++) {try {// 随机获取一个数字,大于、小于sourceValues.size()的数值int index = random.nextInt(sourceValues.size());// 从图片ID集合中获取该图片对象Integer image = sourceValues.get(index);// 添加到结果集中result.add(image);} catch (IndexOutOfBoundsException e) {return result;}}return result;}/** * 从drawable目录中中获取size个图片资源ID(以p_为前缀的资源名称), 其中size为游戏数量 *  * @param size *            需要获取的图片ID的数量 * @return size个图片ID的集合 */public static List<Integer> getPlayValues(int size) {if (size % 2 != 0) {// 如果该数除2有余数,将size加1size += 1;}// 再从所有的图片值中随机获取size的一半数量,即N/2张图片List<Integer> playImageValues = getRandomValues(imageValues, size / 2);// 将playImageValues集合的元素增加一倍(保证所有图片都有与之配对的图片),即N张图片playImageValues.addAll(playImageValues);// 将所有图片ID随机“洗牌”Collections.shuffle(playImageValues);return playImageValues;}/** * 将图片ID集合转换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>();// 遍历每个图片IDfor (Integer value : resourceValues) {// 加载图片Bitmap bm = BitmapFactory.decodeResource(context.getResources(),value);// 封装图片ID与图片本身PieceImage pieceImage = new PieceImage(bm, value);result.add(pieceImage);}return result;}/** *  获取选中标识的图片 * @param context * @return 选中标识的图片 */public static Bitmap getSelectImage(Context context) {Bitmap bm = BitmapFactory.decodeResource(context.getResources(),R.drawable.selected);return bm;}}


前面已经给出了游戏界面的布局文件,该布局文件需要一个Activity来负责显示,除此之外,Activity还需要为游戏界面的按钮、GameView组件的事件提供事件监听器。 尤其是对于GameView组件,程序需要监听用户的触摸动作,当用户触摸屏幕时,程序需要获取用户触摸的是哪个方块,并判断是否需要“消除”该方块。为了判断是否消除该方块,程序需要进行如下判断:
  • 如果程序之前已经选择了某个方块,就判断当前触碰的方块是否能和之前的方块“相连”,如果可以相连,则消除两个方块;如果不能相连,则把当前方块设置为选中方块。
  • 如果程序之前没有选中方块,直接将当前方块设置为选中方块。

下面是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/> * 关于本代码介绍可以参考一下博客: <a href="http://blog.csdn.net/ouyang_peng">欧阳鹏的CSDN博客</a> <br/> */public class LinkActivity extends Activity {/** * 游戏配置对象 */private GameConf config;/** * 游戏业务逻辑接口 */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;/** * 记录是否处于游戏状态 */private boolean isPlaying;/** * 振动处理类 */private Vibrator vibrator;/** * 记录已经选中的方块 */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();// 更改游戏的状态isPlaying = false;// 失败后弹出对话框lostDialog.show();return;}break;}}};@Overridepublic 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);// 得到游戏区域对象gameView = (GameView) findViewById(R.id.gameView);// 获取显示剩余时间的文本框timeTextView = (TextView) findViewById(R.id.timeText);// 获取开始按钮startButton = (Button) this.findViewById(R.id.startButton);// 获取振动器vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);// 初始化游戏业务逻辑接口gameService = new GameServiceImpl(this.config);// 设置游戏逻辑的实现类gameView.setGameService(gameService);// 为开始按钮的单击事件绑定事件监听器startButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View source) {startGame(GameConf.DEFAULT_TIME);}});// 为游戏区域的触碰事件绑定监听器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);}});}@Overrideprotected void onPause() {// 暂停游戏stopTimer();super.onPause();}@Overrideprotected void onResume() {// 如果处于游戏状态中if (isPlaying) {// 以剩余时间重新开始游戏startGame(gameTime);}super.onResume();}/** * 触碰游戏区域的处理方法 *  * @param event */private void gameViewTouchDown(MotionEvent event) {// 获取GameServiceImpl中的Piece[][]数组Piece[][] pieces = gameService.getPieces();// 获取用户点击的x座标float touchX = event.getX();// 获取用户点击的y座标float touchY = event.getY();// 根据用户触碰的座标得到对应的Piece对象Piece currentPiece = gameService.findPiece(touchX, touchY);// 如果没有选中任何Piece对象(即鼠标点击的地方没有图片), 不再往下执行if (currentPiece == null)return;// 将gameView中的选中方块设为当前方块this.gameView.setSelectedPiece(currentPiece);// 表示之前没有选中任何一个Pieceif (this.selectedPiece == null) {// 将当前方块设为已选中的方块, 重新将GamePanel绘制, 并不再往下执行this.selectedPiece = currentPiece;this.gameView.postInvalidate();return;}// 表示之前已经选择了一个if (this.selectedPiece != null) {// 在这里就要对currentPiece和prePiece进行判断并进行连接LinkInfo linkInfo = this.gameService.link(this.selectedPiece,currentPiece);// 两个Piece不可连, linkInfo为nullif (linkInfo == null) {// 如果连接不成功, 将当前方块设为选中方块this.selectedPiece = currentPiece;this.gameView.postInvalidate();} else {// 处理成功连接handleSuccessLink(linkInfo, this.selectedPiece, currentPiece,pieces);}}}/** * 触碰游戏区域的处理方法 *  * @param e */private void gameViewTouchUp(MotionEvent e) {this.gameView.postInvalidate();}/** * 以gameTime作为剩余时间开始或恢复游戏 *  * @param gameTime *            剩余时间 */private void startGame(int gameTime) {// 如果之前的timer还未取消,取消timerif (this.timer != null) {stopTimer();}// 重新设置游戏时间this.gameTime = gameTime;// 如果游戏剩余时间与总游戏时间相等,即为重新开始新游戏if (gameTime == GameConf.DEFAULT_TIME) {// 开始新的游戏游戏gameView.startGame();}isPlaying = true;this.timer = new Timer();// 启动计时器 , 每隔1秒发送一次消息this.timer.schedule(new TimerTask() {public void run() {handler.sendEmptyMessage(0x123);}}, 0, 1000);// 将选中方块设为null。this.selectedPiece = null;}/** * 成功连接后处理 *  * @param linkInfo *            连接信息 * @param prePiece *            前一个选中方块 * @param currentPiece *            当前选择方块 * @param pieces *            系统中还剩的全部方块 */private void handleSuccessLink(LinkInfo linkInfo, Piece prePiece,Piece currentPiece, Piece[][] pieces) {// 它们可以相连, 让GamePanel处理LinkInfothis.gameView.setLinkInfo(linkInfo);// 将gameView中的选中方块设为nullthis.gameView.setSelectedPiece(null);this.gameView.postInvalidate();// 将两个Piece对象从数组中删除pieces[prePiece.getIndexX()][prePiece.getIndexY()] = null;pieces[currentPiece.getIndexX()][currentPiece.getIndexY()] = null;// 将选中的方块设置null。this.selectedPiece = null;// 手机振动(100毫秒)this.vibrator.vibrate(100);// 判断是否还有剩下的方块, 如果没有, 游戏胜利if (!this.gameService.hasPieces()) {// 游戏胜利this.successDialog.show();// 停止定时器stopTimer();// 更改游戏状态isPlaying = false;}}/** * 创建对话框的工具方法 *  * @param title *            标题 * @param message *            内容 * @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用了两个类,这两个类在下一篇博客中再进行相关描述。
  1. GameConf:负责管理游戏的初始化设置信息。
  2. GameService:负责游戏的逻辑实现。

关于具体的实现步骤,请参考下面的链接:

  • 我的Android进阶之旅------>Android疯狂连连看游戏的实现之游戏效果预览(一)
  • 我的Android进阶之旅------>Android疯狂连连看游戏的实现之开发游戏界面(二)
  • 我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)
  • 我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)
  • 我的Android进阶之旅------>Android疯狂连连看游戏的实现之实现游戏逻辑(五)

==================================================================================================

作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!

转载请保留原文地址:http://blog.csdn.net/ouyang_peng

==================================================================================================


更多相关文章

  1. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  2. ffmpeg 移植到 android 并使用
  3. Android(安卓)图片Bitmap保存到内存卡
  4. Android图文混排ImageSpan居中,以及设置间距问题
  5. Android高手进阶教程(二十四)之---Android(安卓)WebView的缓存!!
  6. Android实现imageView在屏幕中缩放
  7. App自适应draw9patch不失真背景
  8. Android图片压缩的实例详解
  9. Android内存优化之图片优化

随机推荐

  1. 非阻塞 connect 的实现
  2. Linux随笔-鸟哥Linux服务器篇学习总结(全)
  3. Linux中的文件权限和用户组
  4. arm linux嵌入式开发入门与进阶---开发板
  5. linux--输入子系统
  6. linux usb枚举过程分析之守护进程及其唤
  7. 进程间通讯概述
  8. 字符串处理函数strcat和strtok
  9. (转)linux字体安装
  10. eclipse maven 插件安装 for mac,linux,win