Android自定义View实现五子棋游戏
本文实例为大家分享了Android实现五子棋游戏的具体代码,供大家参考,具体内容如下
直接上效果图
原理
从棋盘到棋子,到开始下棋的各类点击事件,均在ChessView中实现,这个View没有提供自定义属性(因为我觉得没有必要~~~)。
项目GitHub地址:Wuziqi
实现步骤
1.新建一个棋子类,这个类非常简单,代码如下:
publicclassChess{ publicenumColor{BLACK,WHITE,NONE} privateColorcolor; publicChess(){ this.color=Color.NONE; } publicColorgetColor(){ returncolor; } publicvoidsetColor(Colorcolor){ this.color=color; } }
每个棋子类有三种状态,即WHITE,BLACK,NONE。这里我们使用枚举来表示这三种状态。
2.自定义ChessView类,这个类就是核心类了,我们这个五子棋的所有逻辑都是在这个类里面实现。构造方法初始化各个字段,代码如下:
publicChessView(Contextcontext,AttributeSetattrs,intdefStyleAttr){ super(context,attrs,defStyleAttr); //初始化字段mEveryPlay,悔棋会用到 initEveryPlay(); //初始化每个棋子,设置属性为NONE initChess(); //初始化棋盘画笔 initBoardPaint(); //初始化棋子画笔 initChessPaint(); //初始化背景画笔 initBgPaint(); }
各个方法的具体实现如下:
privatevoidinitEveryPlay(){ //初始化List大小,此方法不影响list.size()返回值 mEveryPlay=newArrayList<>(225); } privatevoidinitChess(){ mChessArray=newChess[15][15]; for(inti=0;i3.重写onMeasure()方法,强制将View大小变为正方形,代码如下:
@Override protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){ intwidthSize=MeasureSpec.getSize(widthMeasureSpec); intheightSize=MeasureSpec.getSize(heightMeasureSpec); intmin=widthSize之所以设置为16的整数倍而不是15,是因为如果设置成15,那么棋盘的背景就会跟棋盘最边界的线条重合,此时如果有棋子落在边界,棋子将不能显示完全。
4.重点来了,重写onDraw()方法,绘制出棋盘,代码如下:
@Override protectedvoidonDraw(Canvascanvas){ intheight=getMeasuredHeight(); intwidth=getMeasuredWidth(); intavg=height/16; canvas.drawRect(0,0,width,height,mBgPaint); for(inti=1;i<16;i++){ //画竖线 canvas.drawLine(avg*i,avg,avg*i,height-avg,mBoardPaint); //画横线 canvas.drawLine(avg,avg*i,width-avg,avg*i,mBoardPaint); } for(inti=1;i<16;i++){ for(intj=1;j<16;j++){ switch(mChessArray[i-1][j-1].getColor()){ caseBLACK: mChessPaint.setColor(android.graphics.Color.BLACK); break; caseWHITE: mChessPaint.setColor(android.graphics.Color.WHITE); break; caseNONE: continue; } canvas.drawCircle(avg*i,avg*j,avg/2-0.5f,mChessPaint); } } }这样我们就将整个棋盘画出来了,之后我们只需要改变数组mChessArray[][]里面对象的Color属性,再调用invalidate()方法便可以刷新棋盘了。
5.接下来,我们便要处理点击事件,实现对弈的逻辑了,重写onTouchEvent()方法,代码如下:
@Override publicbooleanonTouchEvent(MotionEventevent){ switch(event.getAction()){ caseMotionEvent.ACTION_DOWN: //如果棋盘被锁定(即胜负已分,返回查看棋局的时候) //此时只允许查看,不允许落子了 if(isLocked){ returntrue; } floatx=event.getX(); floaty=event.getY(); //以点击的位置为中心,新建一个小矩形 Rectrect=getLittleRect(x,y); //获得上述矩形包含的棋盘上的点 Pointpoint=getContainPoint(rect); if(point!=null){ //若点不为空,则刷新对应位置棋子的属性 setChessState(point); //记录下每步操作,方便悔棋操作 mEveryPlay.add(point); if(gameIsOver(point.x,point.y)){ //游戏结束弹窗提示 showDialog(); } //更改游戏玩家 isBlackPlay=!isBlackPlay; } break; caseMotionEvent.ACTION_MOVE: break; caseMotionEvent.ACTION_UP: break; } returnsuper.onTouchEvent(event); }下面分别来说说调用到的各个方法的实现思路:
getLittleRect()
/** *以传入点为中心,获得一个矩形 * *@paramx传入点x坐标 *@paramy传入点y坐标 *@return所得矩形 */ privateRectgetLittleRect(floatx,floaty){ intside=getMeasuredHeight()/16; intleft=(int)(x-side/2); inttop=(int)(y-side/2); intright=(int)(x+side/2); intbottom=(int)(y+side/2); returnnewRect(left,top,right,bottom); }getContainPoint()
/** *获取包含在rect中并且是能够下棋的位置的点 * *@paramrect矩形 *@return返回包含的点,若没有包含任何点或者包含点已有棋子返回null */ privatePointgetContainPoint(Rectrect){ intavg=getMeasuredHeight()/16; for(inti=1;i<16;i++){ for(intj=1;j<16;j++){ if(rect.contains(avg*i,avg*j)){ Pointpoint=newPoint(i-1,j-1); //包含点没有棋子才返回point if(mChessArray[point.x][point.y].getColor()==Chess.Color.NONE){ returnpoint; } break; } } } returnnull; }showDialog()
顺便一提,这个方法用的是v7包里面的对话框,因为这样可以在版本较低的安卓平台下也可以得到不错的显示效果,效果如下:
/** *游戏结束,显示对话框 */ privatevoidshowDialog(){ AlertDialog.Builderbuilder=newAlertDialog.Builder(getContext()); builder.setTitle("游戏结束"); if(isBlackPlay){ builder.setMessage("黑方获胜!!!"); }else{ builder.setMessage("白方获胜!!!"); } builder.setCancelable(false); builder.setPositiveButton("重新开始",newDialogInterface.OnClickListener(){ @Override publicvoidonClick(DialogInterfacedialog,intwhich){ resetChessBoard(); dialog.dismiss(); } }); builder.setNegativeButton("返回查看",newDialogInterface.OnClickListener(){ @Override publicvoidonClick(DialogInterfacedialog,intwhich){ isLocked=true; dialog.dismiss(); } }); builder.show(); }setChessState()
/** *重新设定用户所点位置的棋子状态 * *@parampoint棋子的位置 */ privatevoidsetChessState(Pointpoint){ if(isBlackPlay){ mChessArray[point.x][point.y].setColor(Chess.Color.BLACK); }else{ mChessArray[point.x][point.y].setColor(Chess.Color.WHITE); } invalidate(); }以上几个方法都较为简单不多说了,接下来重点讲一下判断游戏结束的逻辑。
-gameIsOver()
/** *判断游戏是否结束,游戏结束标志:当前落子位置与其他同色棋子连成5个 * *@paramx落子位置x坐标 *@paramy落子位置y坐标 *@return若连成5个,游戏结束,返回true,负责返回false */ privatebooleangameIsOver(intx,inty){ Chess.Colorcolor=mChessArray[x][y].getColor(); returnisOverA(x,y,color)||isOverB(x,y,color)||isOverC(x,y,color)||isOverD(x,y,color); }这个方法用来判断游戏是否结束,思路便是以当前落子位置为基准,去寻找竖直、水平、左上至右下、左下至右上四个方向是否连成5子,分别对应isOverA(),isOverB(),isOverC(),isOverD()四个方法,这四个方法的实现如下:
privatebooleanisOverA(intx,inty,Chess.Colorcolor){ intamount=0; for(inti=y;i>=0;i--){ if(mChessArray[x][i].getColor()==color){ amount++; }else{ break; } } for(inti=y;i5; } privatebooleanisOverB(intx,inty,Chess.Colorcolor){ intamount=0; for(inti=x;i>=0;i--){ if(mChessArray[i][y].getColor()==color){ amount++; }else{ break; } } for(inti=x;i 5; } privatebooleanisOverC(intx,inty,Chess.Colorcolor){ intamount=0; for(inti=x,j=y;i>=0&&j>=0;i--,j--){ if(mChessArray[i][j].getColor()==color){ amount++; }else{ break; } } for(inti=x,j=y;i 5; } privatebooleanisOverD(intx,inty,Chess.Colorcolor){ intamount=0; for(inti=x,j=y;i =0;i++,j--){ if(mChessArray[i][j].getColor()==color){ amount++; }else{ break; } } for(inti=x,j=y;i>=0&&j 5; } 6.最后定义两个公有方法,方便Activity调用,用来执行悔棋和重置棋盘操作。两个方法代码如下:
/** *悔棋,实现思路为:记录每一步走棋的坐标,若点击了悔棋, *则拿出最后记录的坐标,对mChessArray里面对应坐标的 *棋子进行处理(设置颜色为NONE),并移除集合里面最后 *一个元素 */ publicvoidretract(){ if(mEveryPlay.isEmpty()){ return; } Pointpoint=mEveryPlay.get(mEveryPlay.size()-1); mChessArray[point.x][point.y].setColor(Chess.Color.NONE); mEveryPlay.remove(mEveryPlay.size()-1); isLocked=false; isBlackPlay=!isBlackPlay; invalidate(); } /** *重置棋盘 */ publicvoidresetChessBoard(){ for(Chess[]chessRow:mChessArray){ for(Chesschess:chessRow){ chess.setColor(Chess.Color.NONE); } } mEveryPlay.clear(); isBlackPlay=true; isLocked=false; invalidate(); }到此,ChessView已经写完了,接下来只要在布局文件里面声明即可。
7.在activity_main布局文件如下,非常简单,我相信不用多说都能看懂:
8.最后一步了,只需要在MainActivity里面拿到ChessView对象和两个Button按钮,即可实现悔棋与重新开始:
packagecom.yangqi.wuziqi; importandroid.os.Bundle; importandroid.support.v7.app.AppCompatActivity; importandroid.view.View; importandroid.widget.Button; publicclassMainActivityextendsAppCompatActivity{ privateButtonbt_reset; privateButtonbt_retract; privateChessViewchessView; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); initListener(); } privatevoidinitListener(){ bt_reset.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ chessView.resetChessBoard(); } }); bt_retract.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ chessView.retract(); } }); } privatevoidinitUI(){ bt_reset=(Button)findViewById(R.id.bt_reset); bt_retract=(Button)findViewById(R.id.bt_retract); chessView=(ChessView)findViewById(R.id.chessView); } }以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。