Android自定义View实现五子棋游戏
作者:HelloWord- 发布时间:2021-10-16 23:39:27
标签:Android,五子棋
本文实例为大家分享了Android五子棋游戏的具体代码,供大家参考,具体内容如下
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<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>
来源:https://blog.csdn.net/qq_34536167/article/details/86773161


猜你喜欢
- java 深拷贝与浅拷贝机制详解概要:在Java中,拷贝分为深拷贝和浅拷贝两种。java在公共超类Object中实现了一种叫做c
- 目录1. 支付宝支付接口(沙箱实现)1.1 支付宝沙箱账号获取1.2 下载客户端(目前好像只支持Android)1.3 代码配置1. 支付宝
- C++ 11中的互斥量,声明在 <mutex> 头文件中,互斥量的使用可以在各种方面,比较常用在对共享数据的
- 跨域跨域请求是指浏览器脚本文件在发送请求时,脚本所在的服务器和请求的服务器地址不一样。跨域是有浏览器的同源策略造成的,是浏览器对JavaSc
- java 中编写 GUI 有两中工具包,分别为 AWT、Swing。Swing 是 AWT 的拓展,Swing 具有比 AWT 丰富的组件和
- 在前面介绍的代码中有使用特性,这些特性都是Microsoft定义好的,作为.NET Framework类库的一部分,许多特性都得到了C#编译
- @RequestBody出现400 Bad Request的问题今天与同事调试一个接口,发现后台使用@RequestBody老是获取不到数据
- 在还没给大家介绍单选按钮(RadioGroup)的使用,先给大家展示下效果图吧:xml文件 <LinearLayoutxmlns:an
- 目录前言基本概念实践1. 首先准备一个控制台小程序2. 编写使用 AppDomain 的程序一些思考1. 为什么不使用 AppDomain
- 前言:回顾之前的微信公众号配置和消息处理的内容,我们已经掌握了如何配置服务器与微信公众号建立连接,也掌握了通过消息管理的方式,对用户的信息进
- 一、WebSocket简介WebSocket协议通过在客户端和服务端之间提供全双工通信来进行Web和服务器的交互功能。在WebSocket应
- 微信公众号开发一般是针对企业和组织的,个人一般只能申请订阅号,并且调用的接口有限,下面我们就来简单的描述下接入公众号的步骤:1、首先你需要一
- 一、编译步骤解压下载的GDAL源程序,并在命令行中切换到解压目录。tar -xzvf gdal-2.1.3.tar.gzcd gdal-2.
- 一、前言知识补充:Arrays.copyOf函数:public static int[] copyOf(int[] original, in
- 引言综合应用Java的GUI编程和网络编程,实现一个能够支持多组用户同时使用的聊天室软件。该聊天室具有比较友好的GUI界面,并使用C/S模式
- 本文介绍restTemplate基础用法。Java中get和post的用法请参考:一文带你搞懂Java中Get和Post的使用1 提供get
- FeignClient脱离eureka自定义URL需求Spring Cloud环境中的FeignClient有时候需要调用特定主机的接口,但
- 第1部分 HashSet介绍HashSet 简介HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序,而且
- 锁是个非常有用的工具,运用场景非常多,因为它使用起来非常简单,而且易于理解。但同时它也会带来一些困扰,那就是可能会引起死锁,一旦产生死锁,就
- 作为Android的四大组件之一,ContentProvider作为进程之间静态数据传递的重要手段,其在系统级别的应用中起了重大的作用。毫无