1、效果图:
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 mWhiteArray = new ArrayList<>();//白棋数组
private List 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 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 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 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 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 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: