热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android实现疯狂连连看游戏之加载界面图片和实现游戏Activity(四)

这篇文章主要为大家详细介绍了Android实现疯狂连连看游戏之加载界面图片和实现游戏Activity,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

下面是res/drawable目录视图:

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

  • 通过反射来获取R.drawable的所有Field(Android的每张图片资源都会自动转换为R.drawable的静态Field),并将这些Field值添加到一个List集合中。
  • 从第一步得到的List集合中随机“抽取”N/2个图片ID。
  • 将第二步得到的N/2个图片ID全部复制一份,这样就得到了N个图片ID,而且每个图片ID都可以找到与之配对的。
  • 将第三步得到的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; 
 
/** 
 * 图片资源工具类, 主要用于读取游戏图片资源值
*
* 关于本代码介绍可以参考一下博客: 欧阳鹏的CSDN博客
*/ public class ImageUtil { /** * 保存所有连连看图片资源值(int类型) */ private static List imageValues = getImageValues(); /** * 获取连连看所有图片的ID(约定所有图片ID以p_开头) */ public static List getImageValues() { try { // 得到R.drawable所有的属性, 即获取drawable目录下的所有图片 Field[] drawableFields = R.drawable.class.getFields(); List resourceValues = new ArrayList(); 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 getRandomValues(List sourceValues, int size) { // 创建一个随机数生成器 Random random = new Random(); // 创建结果集合 List result = new ArrayList(); for (int i = 0; i getPlayValues(int size) { if (size % 2 != 0) { // 如果该数除2有余数,将size加1 size += 1; } // 再从所有的图片值中随机获取size的一半数量,即N/2张图片 List 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 getPlayImages(Context context, int size) { // 获取图片ID组成的集合 List resourceValues = getPlayValues(size); List result = new ArrayList(); // 遍历每个图片ID for (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 
*
* 关于本代码介绍可以参考一下博客: 欧阳鹏的CSDN博客
*/ 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; } } }; @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); // 得到游戏区域对象 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() { @Override public 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); } }); } @Override protected void onPause() { // 暂停游戏 stopTimer(); super.onPause(); } @Override protected 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); // 表示之前没有选中任何一个Piece if (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为null if (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还未取消,取消timer if (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处理LinkInfo this.gameView.setLinkInfo(linkInfo); // 将gameView中的选中方块设为null this.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用了两个类,这两个类在下一篇博客中再进行相关描述。
GameConf:负责管理游戏的初始化设置信息。
GameService:负责游戏的逻辑实现。

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

我的Android进阶之旅------>Android疯狂连连看游戏的实现之游戏效果预览(一)

我的Android进阶之旅------>Android疯狂连连看游戏的实现之开发游戏界面(二)

我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)

我的Android进阶之旅------>Android疯狂连连看游戏的实现之实现游戏逻辑(五)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 如何在Android项目中正确导入和配置MySQL数据库驱动 ... [详细]
  • 本文深入解析了线程事件机制的原理及其在实际应用中的案例。通过具体示例,展示了多个线程在不同状态下的交互过程,如线程1、2、3处于等待连接状态,而线程4则负责检测服务的运行状况,并在检测完成后通知其他线程开始连接。该机制有效提高了多线程环境下的资源利用效率和系统响应速度。 ... [详细]
  • 在Mac平台上通过终端操作完成MySQL的启动与彻底关闭——八步指南
    在Mac平台上,通过终端操作实现MySQL的启动与完全关闭,本文提供了一套详细的八步指南。首先,在Finder中使用快捷键进入 `/usr/local` 目录,找到并进入 `mysql` 文件夹。接着,右键选择该文件夹并从上下文菜单中打开终端。在终端中,输入并执行 `./scripts/mysql_install` 命令以开始安装或初始化过程。后续步骤将指导用户如何顺利启动和安全关闭MySQL服务,确保系统资源的有效管理。 ... [详细]
  • C++ STL 常见函数应用详解与实例解析
    本文详细解析了 C++ STL 中常见函数的应用,并通过具体实例进行说明。特别地,文章对迭代器(iterator)的概念进行了深入探讨,将其视为一种将迭代操作抽象化的工具,便于在不同容器间进行元素访问和操作。此外,还介绍了迭代器的基本类型、使用方法及其在算法中的应用,为读者提供了丰富的实践指导。 ... [详细]
  • C#中实现高效UDP数据传输技术
    C#中实现高效UDP数据传输技术 ... [详细]
  • 本文深入探讨了ASP.NET中ViewState、Cookie和Session三种状态管理技术的区别与应用场景。ViewState主要用于保存页面控件的状态信息,确保在多次往返服务器过程中数据的一致性;Cookie则存储在客户端,适用于保存少量用户偏好设置等非敏感信息;而Session则在服务器端存储数据,适合处理需要跨页面保持的数据。文章详细分析了这三种技术的工作原理及其优缺点,并提供了实际应用中的最佳实践建议。 ... [详细]
  • 高效批量文件重命名软件
    开发了一款基于Python的高效批量文件重命名软件,并集成了wxWidgets图形用户界面,使用cxfreeze将其打包为独立的可执行文件(exe)。该工具适用于需要频繁处理大量文件的用户,能够显著提高文件管理效率。详细使用说明包含在软件压缩包内。开发环境为Python 2.7和wxWidgets 3.0,运行环境要求兼容Windows系统。 ... [详细]
  • 前端技术实现调用摄像头进行拍照功能
    在公司项目中,为了实现调用摄像头进行拍照的功能,我们深入研究了HTML5的相关技术。尽管Java在许多方面表现出色,但在这一场景下,HTML5的灵活性和易用性更胜一筹。本文将分享具体的代码设计和实现细节,帮助开发者快速掌握这一功能。 ... [详细]
  • 大数据应用实例:电视收视率分析企业项目实操第二篇
    本文继续探讨大数据在电视收视率分析中的应用,详细介绍了如何在CentOS系统中进行防火墙管理。针对CentOS 6.5及更早版本,提供了具体的命令操作步骤,包括停止防火墙服务和禁用防火墙启动。此外,还深入讨论了这些操作对数据传输和系统安全的影响,为实际项目实施提供了宝贵的技术参考。 ... [详细]
  • 使用CardView实现圆角和圆形效果:边角与半径的精准控制 ... [详细]
  • 通过整合JavaFX与Swing,我们成功地将现有的Swing应用程序组件进行了现代化改造。此次升级不仅提升了用户界面的美观性和交互性,还确保了与原有Swing应用程序的无缝集成,为开发高质量的Java桌面应用提供了坚实的基础。 ... [详细]
  • Spring Security 认证模块的项目构建与初始化
    本文详细介绍了如何构建和初始化Spring Security认证模块的项目。首先,通过创建一个分布式Maven聚合工程,该工程包含四个模块,分别为core、browser(用于演示)、app等,以构成完整的SeehopeSecurity项目。在项目构建过程中,还涉及日志生成机制,确保能够输出关键信息,便于调试和监控。 ... [详细]
  • 本文深入探讨了 Android DrawingView 的优化技巧与实现方法,重点介绍了如何实现平滑绘制效果。通过支持常见的绘图工具和形状,以及图层变换功能,提升了用户体验。文章详细解析了绘制过程中的性能优化策略,包括减少重绘次数、使用硬件加速和优化内存管理等技术,为开发者提供了实用的参考。 ... [详细]
  • 如何在IDEA中安装和配置反编译插件以提高代码审查效率
    在 IntelliJ IDEA 中提升代码审查效率的一种方法是安装和配置反编译插件。首先,进入 IDEA 的设置界面,然后导航到插件管理部分。接下来,搜索 "ideaJad" 插件并进行安装。安装完成后,重启 IDEA 以确保插件生效。这将帮助你在审查二进制文件时更加高效地查看源代码。 ... [详细]
  • Git基础操作指南:掌握必备技能
    掌握 Git 基础操作是每个开发者必备的技能。本文详细介绍了 Git 的基本命令和使用方法,包括初始化仓库、配置用户信息、添加文件、提交更改以及查看版本历史等关键步骤。通过这些操作,读者可以快速上手并高效管理代码版本。例如,使用 `git config --global user.name` 和 `git config --global user.email` 来设置全局用户名和邮箱,确保每次提交时都能正确标识提交者信息。 ... [详细]
author-avatar
曾玉珊志君
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有