Android之自定义五子棋View
16lz
2022-01-20
Android之自定义五子棋View
最近没什么事情,学习了一下有关五子棋的自定义View,该View完成了简易五子棋自定义 主要使用了Android的自定义View,自定义了一个有关五子棋的View ,其中包括棋盘网格线的绘制、棋子的绘制、规则的制定、输赢的提示 、还有最重要的当接到电话以及旋转屏幕等情况时 当前游戏状态的存储与恢复 以及"再来一局"。
放上View的完整代码,与各位共勉之。代码中添加了详细的注释,在此不做过多的解释,请自行查看注释。
完整Demo下载地址
public class WuZiQiPanel extends View { private int mPanelWidth;//View的宽度 private float mLineHeight;//View中的行高 private int MAX_LINE = 10;//最大的行数 private int MAX_COUNT_IN_LINE = 5;//一条线上最大的棋子数为5 private Paint mPaint = new Paint();//创建Pain对象 //创建黑白棋子对象 private Bitmap mBlackPiece; private Bitmap mWhitePiece; //引入棋子的比例 是行高的四分之三 private float ratioPieceOffLineHeight = 3 * 1.0f / 4; //判断是白棋先走 private boolean mIsWhite = true; //存放黑白棋子放置的位置 private ArrayList mWhiteArray = new ArrayList<>(); private ArrayList mBlackArray = new ArrayList<>(); //判断游戏是否结束 private boolean mIsGameOver; //判断是否白棋胜出 private boolean mIsWhiteWinner; public WuZiQiPanel(Context context, @Nullable AttributeSet attrs) { super(context, attrs); //setBackgroundColor(0x44ff0000); //初始化 init(); } /** * 初始化操作 */ private void init() { //初始化Pint mPaint.setColor(0x88000000); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setStyle(Paint.Style.STROKE); //初始化黑白棋子 /* mWhitePiece = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wihte_img); mBlackPiece = BitmapFactory.decodeResource(getResources(), R.drawable.ic_black_img);*/ mWhitePiece = BitmapFactory.decodeResource(getResources(), R.drawable.ic_white_img); mBlackPiece = BitmapFactory.decodeResource(getResources(), R.drawable.ic_black_img); } /** * 设置View的宽高 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = Math.min(widthSize, heightSize); if (widthSize == MeasureSpec.UNSPECIFIED) { width = heightSize; } else if (heightSize == MeasureSpec.UNSPECIFIED) { width = widthSize; } setMeasuredDimension(width, width); } /** * 处理尺寸相关的操作 * * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mPanelWidth = w; mLineHeight = mPanelWidth * 1.0f / MAX_LINE; //初始化棋子的大小 行高的四分之三 int pieceWidth = (int) (mLineHeight * ratioPieceOffLineHeight); mBlackPiece = Bitmap.createScaledBitmap(mBlackPiece, pieceWidth, pieceWidth, false); mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece, pieceWidth, pieceWidth, false); } /** * 手势处理 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { if (mIsGameOver) return false; int action = event.getAction(); if (action == MotionEvent.ACTION_UP) { //获取手指触摸时的横纵坐标 int x = (int) event.getX(); int y = (int) event.getY(); Point point = getValidPoint(x, y); //判断当前点击的位置是否有棋子 如果有 返回false if (mWhiteArray.contains(point) || mBlackArray.contains(point)) { return false; } if (mIsWhite) { mWhiteArray.add(point); } else { mBlackArray.add(point); } //重置 invalidate(); mIsWhite = !mIsWhite; } return true; } /** * 处理点击的坐标点 * * @param x * @param y * @return */ private Point getValidPoint(int x, int y) { return new Point((int) (x / mLineHeight), (int) (y / mLineHeight)); } /** * 绘制 * * @param canvas */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制棋盘 drawBoard(canvas); //绘制棋子 drawPiece(canvas); checkGameOver(canvas); } private void checkGameOver(Canvas canvas) { boolean whiteWin = checkFiveInLine(mWhiteArray); boolean blackWin = checkFiveInLine(mBlackArray); if (whiteWin||blackWin){ mIsGameOver = true; mIsWhiteWinner = whiteWin; String text = mIsWhiteWinner?"白棋胜利":"黑棋胜利"; Toast.makeText(getContext(),text,Toast.LENGTH_LONG).show(); } } private boolean checkFiveInLine(List points) { for (Point point : points) { int x = point.x; int y = point.y; //检测横向是否成线 boolean win = checkHorizontal(x,y,points); if (win) return true; win = checkVertical(x,y,points); if (win) return true; win = checkLeftDiagonal(x,y,points); if (win) return true; win = checkRightDiagonal(x,y,points); if (win) return true; } return false; } /** * 判断x,y位置的棋子 是否横向有五个棋子相邻 * @param x * @param y * @param points * @return */ private boolean checkHorizontal(int x, int y, List points) { int count = 1; //左 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x-i,y))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; //有 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x+i,y))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; return false; } /** * 判断x,y位置的棋子 是否纵向有五个棋子相邻 * @param x * @param y * @param points * @return */ private boolean checkVertical(int x, int y, List points) { int count = 1; //上 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x,y-i))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; //下 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x,y+i))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; return false; } /** * 判断x,y位置的棋子 是否左斜方向有五个棋子相邻 * @param x * @param y * @param points * @return */ private boolean checkLeftDiagonal(int x, int y, List points) { int count = 1; //上 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x-i,y+i))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; //下 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x+i,y-i))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; return false; } /** * 判断x,y位置的棋子 是否左斜方向有五个棋子相邻 * @param x * @param y * @param points * @return */ private boolean checkRightDiagonal(int x, int y, List points) { int count = 1; //上 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x-i,y-i))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; //下 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { if (points.contains(new Point(x+i,y+i))){ count++; }else { break; } } if (count==MAX_COUNT_IN_LINE) return true; return false; } /** * 绘制棋子 * * @param canvas */ private void drawPiece(Canvas canvas) { //绘制白棋 for (int i = 0; i < mWhiteArray.size(); i++) { Point whitePoint = mWhiteArray.get(i); canvas.drawBitmap(mWhitePiece, (whitePoint.x + (1 - ratioPieceOffLineHeight) / 2) * mLineHeight, (whitePoint.y + (1 - ratioPieceOffLineHeight) / 2) * mLineHeight, null); } //绘制黑棋 for (int i = 0; i < mBlackArray.size(); i++) { Point whitePoint = mBlackArray.get(i); canvas.drawBitmap(mBlackPiece, (whitePoint.x + (1 - ratioPieceOffLineHeight) / 2) * mLineHeight, (whitePoint.y + (1 - ratioPieceOffLineHeight) / 2) * mLineHeight, null); } } /** * 绘制棋盘 * * @param canvas */ private void drawBoard(Canvas canvas) { int w = mPanelWidth;//获取棋盘的宽 float lineHeight = mLineHeight;//获取每一行的高 //棋盘上下左右分别空出半个行高(0.5个lineHeight)的外边距 所以棋盘的左上角网格的起始坐标是mLineHeight/2 for (int i = 0; i < MAX_LINE; i++) { int startX = (int) (lineHeight / 2);//获取网格的起始坐标 int endX = (int) (w - lineHeight / 2);//获取网格的终止坐标 //获取纵坐标 注意上方有0.5个lineHeight的外边距 int y = (int) ((0.5 + i) * lineHeight); //棋盘为正方形网格 纵向和横向的变化是一致的 //画横线 canvas.drawLine(startX, y, endX, y, mPaint); //画纵线 canvas.drawLine(y, startX, y, endX, mPaint); } } /** * 再来一局 */ public void reStart(){ mWhiteArray.clear(); mBlackArray.clear(); mIsGameOver = false; mIsWhiteWinner = false; invalidate(); } //View的存储与恢复 // 注意:如果下面代码完成后 View的状态依然有问题 // 检查XML中View的使用是否添加ID(必须有ID 下面代码才能正常) private static final String INSTANCE = "instance"; private static final String INSTANCE_GAME_OVER = "instance_game_over"; private static final String INSTANCE_WHITE_ARRAY = "instance_white_array"; private static final String INSTANCE_BLACK_ARRAY = "instance_black_array"; @Nullable @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(INSTANCE,super.onSaveInstanceState()); bundle.putBoolean(INSTANCE_GAME_OVER,mIsGameOver); bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY,mWhiteArray); bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY,mBlackArray); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle){ Bundle bundle = (Bundle) state; mIsGameOver = bundle.getBoolean(INSTANCE_GAME_OVER); mWhiteArray = bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY); mBlackArray = bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return; } super.onRestoreInstanceState(state); }}
更多相关文章
- Android自定义视图
- Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)
- Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)
- android自定义adapter 滑动屏幕时 进度条显示混乱
- android自定义adapter 滑动屏幕时 进度条显示混乱
- android自定义adapter 滑动屏幕时 进度条显示混乱
- Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)
- Android绘图机制(三)——自定义View的实现方式以及半弧圆新控件
- Android图形显示系统——概述