Android自定义View实现五子棋游戏
人气:01、效果图:
2、GobangPanel棋盘面板:
public class GobangPanel extends View { private int mPanelWidth;//棋盘的宽度 private float mLineHeight;//行,高要为float private int MAX_LINE = 15;//棋盘行数 private int MAX_COUNT_IN_LINE = 5;//设置赢棋子个数(6子棋设置为6) private Paint mPaint = new Paint();//画笔 private Bitmap mWhitePiece;//白色棋子 private Bitmap mBlackPiece;//黑色棋子 private float ratioPieceOfLineHeight = 3 * 1.0f / 4;//2个棋子间3/4距离 //白旗先手,当前轮到白棋了 private boolean mIsWhite = true; private ArrayList<Point> mWhiteArray = new ArrayList<>();//白棋数组 private List<Point> mBlackArray = new ArrayList<>();//黑骑数组 private boolean mIsGameOver;//游戏是否结束 private boolean mIsWhiteWinner;//白色棋子赢 true白子赢,false黑色赢 public GobangPanel(Context context, AttributeSet attrs) { super(context, attrs); init(); } /** * 初始化画笔参数 */ private void init() { mPaint.setColor(0x88000000);//Paint颜色 半透明灰色 mPaint.setAntiAlias(true);//抗锯齿(边界明显变模糊) mPaint.setDither(true);//设置防抖动(图片柔和) mPaint.setStyle(Paint.Style.STROKE);//样式 //黑、白棋资源图片 mWhitePiece = BitmapFactory.decodeResource(getResources(), R.mipmap.white_bg); mBlackPiece = BitmapFactory.decodeResource(getResources(), R.mipmap.black_bg); } /** * 自定义View尺寸的规则 */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //宽和高 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width = Math.max(widthSize, heightSize); //最大值 if (widthMode == MeasureSpec.UNSPECIFIED) { width = heightSize; } else if (heightMode == MeasureSpec.UNSPECIFIED) { width = widthSize; } setMeasuredDimension(width, width); } /** * 控件大小发生改变时调用 */ 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 * ratioPieceOfLineHeight); //比例 mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece, pieceWidth, pieceWidth, false);//棋子跟随控件变化 mBlackPiece = Bitmap.createScaledBitmap(mBlackPiece, pieceWidth, pieceWidth, false); } //触摸焦点 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 p = getValidPoint(x, y); if (mWhiteArray.contains(p) || mBlackArray.contains(p)) { return false; } if (mIsWhite) { mWhiteArray.add(p); } else { mBlackArray.add(p); } invalidate();//重绘棋子 mIsWhite = !mIsWhite; } return true;//感兴趣交给其处理 } private Point getValidPoint(int x, int y) { return new Point((int) (x / mLineHeight), (int) (y / mLineHeight)); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawBoard(canvas); drawPieces(canvas); checkGameOver(); } private static final String TAG = "GobangPanel"; /** * 游戏结束方法 */ public void checkGameOver() { boolean whiteWin = checkFiveInLine(mWhiteArray); boolean blackWin = checkFiveInLine(mBlackArray); //黑棋或白棋赢游戏结束 if (whiteWin || blackWin) { mIsGameOver = true; mIsWhiteWinner = whiteWin; if (null != listener) { listener.onFinish(mIsWhiteWinner); Log.e(TAG, "checkGameOver: 111111" ); } Log.e(TAG, "checkGameOver: 222222" ); } } /** * 判断棋子是否5个相邻【5个相连只有4中情况分别是:水平、垂直、左斜和右斜】 */ private boolean checkFiveInLine(List<Point> points) { for (Point p : points) { int x = p.x; int y = p.y; boolean win = checkLevel(x, y, points); if (win) return true; win = checkVetical(x, y, points); if (win) return true; win = checkLeftWin(x, y, points); if (win) return true; win = checkRightWin(x, y, points); if (win) return true; } return false; } /** * 判断x,y位置的棋子是否【水平】有相邻的五个一致 */ private boolean checkLevel(int x, int y, List<Point> points) { int count = 1; //横向左边棋子个数 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x - i, y))) { count++; } else { break; } } //有5个时则赢 if (count == MAX_COUNT_IN_LINE) return true; //横向右边棋子个数 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x + i, y))) { count++; } else { break; } } //有5个时则赢 if (count == MAX_COUNT_IN_LINE) return true; return false; } /** * 判断x,y位置的棋子是否[垂直]有相邻的五个一致 */ private boolean checkVetical(int x, int y, List<Point> points) { int count = 1; //上下棋子个数 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x, y - i))) { count++; } else { break; } } //有5个时则赢,return true; if (count == MAX_COUNT_IN_LINE) return true; for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x, y + i))) { count++; } else { break; } } //有5个时则赢 if (count == MAX_COUNT_IN_LINE) return true; return false; } /** * 判断x,y位置的棋子是否【左斜和右斜】有相邻的五个一致 */ private boolean checkLeftWin(int x, int y, List<Point> points) { int count = 1; //横向上下棋子个数 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x - i, y + i))) { count++; } else { break; } } //有5个时则赢,return true; if (count == MAX_COUNT_IN_LINE) return true; for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x + i, y - i))) { count++; } else { break; } } //有5个时则赢 if (count == MAX_COUNT_IN_LINE) return true; return false; } /** * 判断x,y位置的棋子是否【右斜】有相邻的五个一致 */ private boolean checkRightWin(int x, int y, List<Point> points) { int count = 1; //横向上下棋子个数 for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x - i, y - i))) { count++; } else { break; } } //有5个时则赢,return true; if (count == MAX_COUNT_IN_LINE) return true; for (int i = 1; i < MAX_COUNT_IN_LINE; i++) { //如果有加1,没有重新计算是否有五个,否者中断 if (points.contains(new Point(x + i, y + i))) { count++; } else { break; } } //有5个时则赢 if (count == MAX_COUNT_IN_LINE) return true; return false; } private void drawPieces(Canvas canvas) { //白色棋子 for (int i = 0, n = mWhiteArray.size(); i < n; i++) { Point whitePoint = mWhiteArray.get(i); canvas.drawBitmap(mWhitePiece, (whitePoint.x + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, (whitePoint.y + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, null); } //黑色棋子 for (int i = 0, n = mBlackArray.size(); i < n; i++) { Point blackPoint = mBlackArray.get(i); canvas.drawBitmap(mBlackPiece, (blackPoint.x + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, (blackPoint.y + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, null); } } /** * 画格子棋盘 */ private void drawBoard(Canvas canvas) { int w = mPanelWidth; float lineHeight = mLineHeight; for (int i = 0; i < MAX_LINE; i++) { int startx = (int) (lineHeight / 2);//横坐标起点,终点 int endX = (int) (w - lineHeight / 2); int y = (int) ((0.5 + i) * lineHeight); canvas.drawLine(startx, y, endX, y, mPaint); canvas.drawLine(y, startx, y, endX, mPaint); } } /** * 再来一局 */ public void start() { if (null != mWhiteArray && null != mBlackArray) { mWhiteArray.clear();//清除数据 mBlackArray.clear(); } mIsGameOver = false; mIsWhiteWinner = false; invalidate(); //再次调用 } /** * 后台运行时,调用此方法,防止数据丢失 */ 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"; /** * 保存数据 */ 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, mWhiteArray); return bundle; } /** * 恢复时调用 */ 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); } /** * 游戏结束回调 */ public OnFinishListener listener; public void setListener(OnFinishListener listener) { this.listener = listener; } public interface OnFinishListener { void onFinish(boolean mIsWhiteWinner); } }
3、使用MainActivity
public class MainActivity extends AppCompatActivity { private GobangPanel panel; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); panel = this.findViewById(R.id.gobang_panel); panel.setListener(new GobangPanel.OnFinishListener() { @Override public void onFinish(boolean mIsWhiteWinner) { initDialog(mIsWhiteWinner); } }); } /** * 初始化弹框 */ private void initDialog(boolean mIsWhiteWinner) { AlertDialog dialog = new AlertDialog.Builder(this) //.setTitle("这是标题") .setMessage(mIsWhiteWinner ? "白棋胜利,是否重新开始?" : "黑棋胜利,是否重新开始?") //.setIcon(R.mipmap.ic_launcher) .setPositiveButton("确定", new DialogInterface.OnClickListener() {//添加"Yes"按钮 @Override public void onClick(DialogInterface dialogInterface, int i) { panel.start(); } }) // .setNegativeButton("取消", new DialogInterface.OnClickListener() {//添加取消 // @Override // public void onClick(DialogInterface dialogInterface, int i) { // Toast.makeText(MainActivity.this, "这是取消按钮", Toast.LENGTH_SHORT).show(); // } // }) //方法一:setCanceledOnTouchOutside(false);按对话框以外的地方不起作用。按返回键起作用 //方法二:setCanceleable(false);按对话框以外的地方不起作用。按返回键也不起作用 .setCancelable(false) .create(); dialog.show(); } }
对应布局文件:activity_main:
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fff" tools:context=".MainActivity"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <com.helloworld.game.GobangPanel android:id="@+id/gobang_panel" android:layout_width="1000dp" android:layout_height="1000dp" android:layout_centerInParent="true" /> </ScrollView> </HorizontalScrollView>
您可能感兴趣的文章:
加载全部内容