我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)
16lz
2021-01-25
正如在《我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)》一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piece对象时,程序会直接调用ImageUtil的getPlayImages()方法去获取图片,该方法会随机从res/drawable目录中取得N张图片。
下面是res/drawable目录视图:
Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)_第1张图片" style="border:none; max-width:100%;border:1px solid black;" width="325" height="575">
为了让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对象后返回。
- packagecn.oyp.link.utils;
- importjava.lang.reflect.Field;
- importjava.util.ArrayList;
- importjava.util.Collections;
- importjava.util.List;
- importjava.util.Random;
- importandroid.content.Context;
- importandroid.graphics.Bitmap;
- importandroid.graphics.BitmapFactory;
- importcn.oyp.link.R;
- importcn.oyp.link.view.PieceImage;
- /**
- *图片资源工具类,主要用于读取游戏图片资源值<br/>
- *<br/>
- *关于本代码介绍可以参考一下博客:<ahref="http://blog.csdn.net/ouyang_peng">欧阳鹏的CSDN博客</a><br/>
- */
- publicclassImageUtil{
- /**
- *保存所有连连看图片资源值(int类型)
- */
- privatestaticList<Integer>imageValues=getImageValues();
- /**
- *获取连连看所有图片的ID(约定所有图片ID以p_开头)
- */
- publicstaticList<Integer>getImageValues(){
- try{
- //得到R.drawable所有的属性,即获取drawable目录下的所有图片
- Field[]drawableFields=R.drawable.class.getFields();
- List<Integer>resourceValues=newArrayList<Integer>();
- for(Fieldfield:drawableFields){
- //如果该Field的名称以p_开头
- if(field.getName().indexOf("p_")!=-1){
- resourceValues.add(field.getInt(R.drawable.class));
- }
- }
- returnresourceValues;
- }catch(Exceptione){
- returnnull;
- }
- }
- /**
- *随机从sourceValues的集合中获取size个图片ID,返回结果为图片ID的集合
- *
- *@paramsourceValues
- *从中获取的集合
- *@paramsize
- *需要获取的个数
- *@returnsize个图片ID的集合
- */
- publicstaticList<Integer>getRandomValues(List<Integer>sourceValues,
- intsize){
- //创建一个随机数生成器
- Randomrandom=newRandom();
- //创建结果集合
- List<Integer>result=newArrayList<Integer>();
- for(inti=0;i<size;i++){
- try{
- //随机获取一个数字,大于、小于sourceValues.size()的数值
- intindex=random.nextInt(sourceValues.size());
- //从图片ID集合中获取该图片对象
- Integerimage=sourceValues.get(index);
- //添加到结果集中
- result.add(image);
- }catch(IndexOutOfBoundsExceptione){
- returnresult;
- }
- }
- returnresult;
- }
- /**
- *从drawable目录中中获取size个图片资源ID(以p_为前缀的资源名称),其中size为游戏数量
- *
- *@paramsize
- *需要获取的图片ID的数量
- *@returnsize个图片ID的集合
- */
- publicstaticList<Integer>getPlayValues(intsize){
- if(size%2!=0){
- //如果该数除2有余数,将size加1
- size+=1;
- }
- //再从所有的图片值中随机获取size的一半数量,即N/2张图片
- List<Integer>playImageValues=getRandomValues(imageValues,size/2);
- //将playImageValues集合的元素增加一倍(保证所有图片都有与之配对的图片),即N张图片
- playImageValues.addAll(playImageValues);
- //将所有图片ID随机“洗牌”
- Collections.shuffle(playImageValues);
- returnplayImageValues;
- }
- /**
- *将图片ID集合转换PieceImage对象集合,PieceImage封装了图片ID与图片本身
- *
- *@paramcontext
- *@paramresourceValues
- *@returnsize个PieceImage对象的集合
- */
- publicstaticList<PieceImage>getPlayImages(Contextcontext,intsize){
- //获取图片ID组成的集合
- List<Integer>resourceValues=getPlayValues(size);
- List<PieceImage>result=newArrayList<PieceImage>();
- //遍历每个图片ID
- for(Integervalue:resourceValues){
- //加载图片
- Bitmapbm=BitmapFactory.decodeResource(context.getResources(),
- value);
- //封装图片ID与图片本身
- PieceImagepieceImage=newPieceImage(bm,value);
- result.add(pieceImage);
- }
- returnresult;
- }
- /**
- *获取选中标识的图片
- *@paramcontext
- *@return选中标识的图片
- */
- publicstaticBitmapgetSelectImage(Contextcontext){
- Bitmapbm=BitmapFactory.decodeResource(context.getResources(),
- R.drawable.selected);
- returnbm;
- }
- }
前面已经给出了游戏界面的布局文件,该布局文件需要一个Activity来负责显示,除此之外,Activity还需要为游戏界面的按钮、GameView组件的事件提供事件监听器。 尤其是对于GameView组件,程序需要监听用户的触摸动作,当用户触摸屏幕时,程序需要获取用户触摸的是哪个方块,并判断是否需要“消除”该方块。为了判断是否消除该方块,程序需要进行如下判断:
- 如果程序之前已经选择了某个方块,就判断当前触碰的方块是否能和之前的方块“相连”,如果可以相连,则消除两个方块;如果不能相连,则把当前方块设置为选中方块。
- 如果程序之前没有选中方块,直接将当前方块设置为选中方块。
下面是Activity的代码:cn\oyp\link\LinkActivity.java [java] view plain copy
- packagecn.oyp.link;
- importjava.util.Timer;
- importjava.util.TimerTask;
- importandroid.app.Activity;
- importandroid.app.AlertDialog;
- importandroid.content.DialogInterface;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.os.Vibrator;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.widget.Button;
- importandroid.widget.TextView;
- importcn.oyp.link.board.GameService;
- importcn.oyp.link.board.impl.GameServiceImpl;
- importcn.oyp.link.utils.GameConf;
- importcn.oyp.link.utils.LinkInfo;
- importcn.oyp.link.view.GameView;
- importcn.oyp.link.view.Piece;
- /**
- *游戏Activity<br/>
- *<br/>
- *关于本代码介绍可以参考一下博客:<ahref="http://blog.csdn.net/ouyang_peng">欧阳鹏的CSDN博客</a><br/>
- */
- publicclassLinkActivityextendsActivity{
- /**
- *游戏配置对象
- */
- privateGameConfconfig;
- /**
- *游戏业务逻辑接口
- */
- privateGameServicegameService;
- /**
- *游戏界面
- */
- privateGameViewgameView;
- /**
- *开始按钮
- */
- privateButtonstartButton;
- /**
- *记录剩余时间的TextView
- */
- privateTextViewtimeTextView;
- /**
- *失败后弹出的对话框
- */
- privateAlertDialog.BuilderlostDialog;
- /**
- *游戏胜利后的对话框
- */
- privateAlertDialog.BuildersuccessDialog;
- /**
- *定时器
- */
- privateTimertimer=newTimer();
- /**
- *记录游戏的剩余时间
- */
- privateintgameTime;
- /**
- *记录是否处于游戏状态
- */
- privatebooleanisPlaying;
- /**
- *振动处理类
- */
- privateVibratorvibrator;
- /**
- *记录已经选中的方块
- */
- privatePieceselectedPiece=null;
- /**
- *Handler类,异步处理
- */
- privateHandlerhandler=newHandler(){
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- case0x123:
- timeTextView.setText("剩余时间:"+gameTime);
- gameTime--;//游戏剩余时间减少
- //时间小于0,游戏失败
- if(gameTime<0){
- //停止计时
- stopTimer();
- //更改游戏的状态
- isPlaying=false;
- //失败后弹出对话框
- lostDialog.show();
- return;
- }
- break;
- }
- }
- };
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //初始化界面
- init();
- }
- /**
- *初始化游戏的方法
- */
- privatevoidinit(){
- config=newGameConf(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=newGameServiceImpl(this.config);
- //设置游戏逻辑的实现类
- gameView.setGameService(gameService);
- //为开始按钮的单击事件绑定事件监听器
- startButton.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewsource){
- startGame(GameConf.DEFAULT_TIME);
- }
- });
- //为游戏区域的触碰事件绑定监听器
- this.gameView.setOnTouchListener(newView.OnTouchListener(){
- publicbooleanonTouch(Viewview,MotionEvente){
- if(e.getAction()==MotionEvent.ACTION_DOWN){
- gameViewTouchDown(e);
- }
- if(e.getAction()==MotionEvent.ACTION_UP){
- gameViewTouchUp(e);
- }
- returntrue;
- }
- });
- //初始化游戏失败的对话框
- lostDialog=createDialog("Lost","游戏失败!重新开始",R.drawable.lost)
- .setPositiveButton("确定",newDialogInterface.OnClickListener(){
- publicvoidonClick(DialogInterfacedialog,intwhich){
- startGame(GameConf.DEFAULT_TIME);
- }
- });
- //初始化游戏胜利的对话框
- successDialog=createDialog("Success","游戏胜利!重新开始",
- R.drawable.success).setPositiveButton("确定",
- newDialogInterface.OnClickListener(){
- publicvoidonClick(DialogInterfacedialog,intwhich){
- startGame(GameConf.DEFAULT_TIME);
- }
- });
- }
- @Override
- protectedvoidonPause(){
- //暂停游戏
- stopTimer();
- super.onPause();
- }
- @Override
- protectedvoidonResume(){
- //如果处于游戏状态中
- if(isPlaying){
- //以剩余时间重新开始游戏
- startGame(gameTime);
- }
- super.onResume();
- }
- /**
- *触碰游戏区域的处理方法
- *
- *@paramevent
- */
- privatevoidgameViewTouchDown(MotionEventevent){
- //获取GameServiceImpl中的Piece[][]数组
- Piece[][]pieces=gameService.getPieces();
- //获取用户点击的x座标
- floattouchX=event.getX();
- //获取用户点击的y座标
- floattouchY=event.getY();
- //根据用户触碰的座标得到对应的Piece对象
- PiececurrentPiece=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进行判断并进行连接
- LinkInfolinkInfo=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);
- }
- }
- }
- /**
- *触碰游戏区域的处理方法
- *
- *@parame
- */
- privatevoidgameViewTouchUp(MotionEvente){
- this.gameView.postInvalidate();
- }
- /**
- *以gameTime作为剩余时间开始或恢复游戏
- *
- *@paramgameTime
- *剩余时间
- */
- privatevoidstartGame(intgameTime){
- //如果之前的timer还未取消,取消timer
- if(this.timer!=null){
- stopTimer();
- }
- //重新设置游戏时间
- this.gameTime=gameTime;
- //如果游戏剩余时间与总游戏时间相等,即为重新开始新游戏
- if(gameTime==GameConf.DEFAULT_TIME){
- //开始新的游戏游戏
- gameView.startGame();
- }
- isPlaying=true;
- this.timer=newTimer();
- //启动计时器,每隔1秒发送一次消息
- this.timer.schedule(newTimerTask(){
- publicvoidrun(){
- handler.sendEmptyMessage(0x123);
- }
- },0,1000);
- //将选中方块设为null。
- this.selectedPiece=null;
- }
- /**
- *成功连接后处理
- *
- *@paramlinkInfo
- *连接信息
- *@paramprePiece
- *前一个选中方块
- *@paramcurrentPiece
- *当前选择方块
- *@parampieces
- *系统中还剩的全部方块
- */
- privatevoidhandleSuccessLink(LinkInfolinkInfo,PieceprePiece,
- PiececurrentPiece,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;
- }
- }
- /**
- *创建对话框的工具方法
- *
- *@paramtitle
- *标题
- *@parammessage
- *内容
- *@paramimageResource
- *图片
- *@return
- */
- privateAlertDialog.BuildercreateDialog(Stringtitle,Stringmessage,
- intimageResource){
- returnnewAlertDialog.Builder(this).setTitle(title)
- .setMessage(message).setIcon(imageResource);
- }
- /**
- *停止计时
- */
- privatevoidstopTimer(){
- //停止定时器
- this.timer.cancel();
- this.timer=null;
- }
- }
该Activity用了两个类,这两个类在下一篇博客中再进行相关描述。
- GameConf:负责管理游戏的初始化设置信息。
- GameService:负责游戏的逻辑实现。
更多相关文章
- GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
- Android侧边导航栏+ListView基础实践
- Android实现贪吃蛇游戏一:游戏界面及控制
- Android(安卓)button自定义显示
- Android(安卓)图片控件ImageView
- android中获取手机相机和相册可以传多张图片
- 【开源】XPShadow, 用阴影让UWP更有层次感
- [Android实例教程] 教你如何拍照+相册选择图片+剪裁图片完整实现
- Android(安卓)调用系统相机,解决回调的resultCode一直都是0的问题