专栏首页恩蓝脚本Android自定义View实现五子棋游戏

Android自定义View实现五子棋游戏

本文实例为大家分享了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 

以上就是本文的全部内容,希望对大家的学习有所帮助。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android页面中引导蒙层的使用方法详解

    蒙层是什么,蒙层是一层透明的呈灰色的视图,是在用户使用App时让用户快速学会使用的一些指导。类似于一些引导页面,只不过比引导页面更加生动形象而已。在GitHub...

    砸漏
  • GuideView的封装实现app功能引导页

    本文实例为大家分享了GuideView的封装实现app功能引导页的具体代码,供大家参考,具体内容如下

    砸漏
  • Android利用ViewDragHelper轻松实现拼图游戏的示例

    最近一段时间看了一些介绍ViewDragHelper的博客,感觉这是一个处理手势滑动的神奇,看完以后就想做点东西练练手,于是就做了这个Android拼图小游戏。

    砸漏
  • 算法复杂度O(logn)详解

    由于cnt每次在乘以2之后都会更加逼近n,也就是说,在有x次后,cnt将会大于n从而跳出循环,所以

    233333
  • 挑战程序竞赛系列(27):3.5二分图匹配(2)

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    用户1147447
  • 判断一个数是不是素数的几种方法,不断优化!!! 素数判定 HDU - 2012

    这种题目应该算是比较基础的了,但是,越是基础的东西,越是要记得清楚明白,初学C的时候,看过这种问题,后来慢慢就不在意了,再次看到这个题目,依然感触颇深。

    种花家的奋斗兔
  • P1120 小木棍 [数据加强版]

    题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍...

    attack
  • 蓝桥杯-2017年省赛C++B组题5-取数位

    本文采用CC BY-NC-SA 3.0 Unported协议进行许可,转载请保留此文章链接

    Debug客栈
  • 挑战程序竞赛系列(35):3.3Binary Indexed Tree

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.n...

    用户1147447
  • 带分数 第四届蓝桥杯省赛C++B组

    输入样例1: 100 输出样例1: 11 输入样例2: 105 输出样例2: 6

    dejavu1zz

扫码关注云+社区

领取腾讯云代金券