前段时间在慕课网上看了hyman大神的五子棋视频,发现下面没有提供源代码,然后自己跟着编写了一份,梳理了一下思路,用作总结
画棋盘
首先自定义view的四部曲
1,在attrs中申明自定义的属性
2,在View的构造方法中获得我们自定义的属性
3,重写onMeasure方法
4,重写ondraw方法
五子棋的自定义属性我都写死了,也可以不写死,写在attrs中动态的改变,这样都可以
生成构造方法
public WuZiQiView(Context context, AttributeSet attrs) {
super(context, attrs);
//设置一个背景颜色用来区分我们自定义的view的大小最后的时候去掉
setBackgroundColor(0x44ff0000);
init();
}
//棋盘的线条数
private int maxline = 10;
//棋盘的宽度
private int mpanelWidth;
//每一行的高度
private float mLineHeight;
//新建一只画棋盘的画笔
private Paint mPaint = new Paint();
重写onmeasure方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
//取view的宽度
int width = Math.min(widthSize, heightSize);
if (heightMode == MeasureSpec.UNSPECIFIED) {
//测量模式为不确定的时候也取view的宽度
width = widthSize;
} else if (widthMode == MeasureSpec.UNSPECIFIED) {
width = heightSize;
}
//设置自定义view的宽度和高度
setMeasuredDimension(width, width);
}
获取棋盘的高度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//设置view的宽度为棋盘的宽度
mpanelWidth = w;
//获取每一行的宽度=棋盘的宽度/行数
mLineHeight = mpanelWidth * 1.0f / maxline;
}
重写ondraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画棋盘
doawBoard(canvas);
}
//初始化画笔
private void init() {
//设置颜色
mPaint.setColor(0x88000000);
//抗锯齿
mPaint.setAntiAlias(true);
//防抖动
mPaint.setDither(true);
//Paint.Style.STROKE 仅描边
mPaint.setStyle(Paint.Style.STROKE);
}
private void doawBoard(Canvas canvas) {
int w = mpanelWidth;
int LineHeight = (int) mLineHeight;
for (int i = 0; i < maxline; i++) {
//不能从起点开始画,因为我们还需要放置旗子,所以这里取行宽的2分之1
int startX = (int) (mLineHeight / 2);
int endX = (int) (w - mLineHeight / 2);
//Y坐标根据行数进行变化
int y = (int) ((0.5 + i) * LineHeight);
canvas.drawLine(startX, y, endX, y, mPaint);
canvas.drawLine(y, startX, y, endX, mPaint);
}
}
到这里棋盘就绘制完了,效果图如下
画棋子
首先在网上找俩张资源图,棋子的,放在drawble中
首先初始化它
private void init() {
mPaint.setColor(0x88000000);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
//根据资源文件获取白棋黑棋对应的bitmap
mWhitePiece = BitmapFactory.decodeResource(getResources(), R.drawable.stone_w2);
mBlancPiece = BitmapFactory.decodeResource(getResources(), R.drawable.stone_b1);
}
画棋子大概的思路如下
俩个list用来存取2种颜色的棋子的坐标,用一个常量区分当前是下白子还是黑子,重写ontouchevent方法,在手势抬起的时候绘制棋子,之所以不是手势按下就绘制就考虑到玩家按下后平移,但是发现棋子已经绘制好了,所以把棋子的绘制放在手势抬起的时候
//白棋
private Bitmap mWhitePiece;
//黑棋
private Bitmap mBlancPiece;
//百分比用来设置棋子的宽度
private float ratioPieceOfLineHeight = 1.0f * 3 / 4;
//常量用来区分当前的状态
private boolean meisWhite = false;
//存放白棋坐标的list
private ArrayList<Point> mWhiteList = new ArrayList<>();
//存放黑棋坐标的list
private ArrayList<Point> mBlackList = new ArrayList<>();
//获取棋子的宽度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mpanelWidth = w;
mLineHeight = mpanelWidth * 1.0f / maxline;
//棋子的宽度去行宽的3/4
int pipcewidth = (int) (mLineHeight * ratioPieceOfLineHeight);
//根据棋子的宽度重新定制棋子的大小和尺寸
mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece, pipcewidth, pipcewidth, false);
mBlancPiece = Bitmap.createScaledBitmap(mBlancPiece, pipcewidth, pipcewidth, false);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mIsGameOver) {
return false;
}
int action = event.getAction();
if (action == MotionEvent.ACTION_UP) {
//记录手势的坐标并且将坐标存在list中
int x = (int) event.getX();
int y = (int) event.getY();
//使用系统自带的point类,用来存放x,y
Point p = getVaildPoint(x, y);
//避免重复绘制
if (mWhiteList.contains(p) || mBlackList.contains(p)) {
return false;
}
//用一个常量来区分当前下的棋子是白色还是黑色
if (meisWhite) {
mWhiteList.add(p);
} else {
mBlackList.add(p);
}
//刷新界面
invalidate();
//每次都取反,这样白子和黑子一人一轮
meisWhite = !meisWhite;
}
return true;
}
//获取棋子的坐标
private Point getVaildPoint(int x, int y) {
return new Point((int) (x / mLineHeight), (int) (y / mLineHeight));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
doawBoard(canvas);
//画棋子
drawPiece(canvas);
}
private void drawPiece(Canvas canvas) {
//根据存放的list的大小绘制棋子
for (int i = 0; i < mWhiteList.size(); i++) {
Point whitePoint = mWhiteList.get(i);
//画棋子,棋子的坐标加上行宽的1/8,保持绘制的棋子处于线条交叉正中心的位置
canvas.drawBitmap(mWhitePiece, (whitePoint.x + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, (whitePoint.y + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, null);
}
for (int i = 0; i < mBlackList.size(); i++) {
Point mBlackPoint = mBlackList.get(i);
canvas.drawBitmap(mBlancPiece, (mBlackPoint.x + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, (mBlackPoint.y + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, null);
}
}
接下来看下效果
检查游戏是否结束
最后检查游戏的结束状态
//是否是白棋胜利
private boolean misWhiteWinner;
//游戏是否结束
private boolean mIsGameOver;
//同色的相邻棋子最大数
private int MaxCountLine = 5;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
doawBoard(canvas);
drawPiece(canvas);
/检查游戏是否结束
checkGameOver();
}
private void checkGameOver() {
//检查的逻辑算法
boolean whiteWin = checkFiveInLine(mWhiteList);
boolean blackWin = checkFiveInLine(mBlackList);
if (whiteWin || blackWin) {
mIsGameOver = true;
misWhiteWinner = whiteWin;
//弹出toast
String text = misWhiteWinner ? "白棋胜利" : "黑期胜利";
Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
//游戏结束后弹出对话框,
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
alertDialog.setTitle("游戏结束").setMessage("在来一局吗").setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//重置游戏
reStart();
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).show();
}
}
检查的逻辑算法:再用户当前放置棋子的坐标开始判断,横,竖,左斜,右斜,进行5次循环判断,判断list中是否存在有相邻的棋子,如果有则用index++,当index达到5的时候代表游戏结束
private boolean checkFiveInLine(List<Point> points) {
for (Point point : points) {
int x = point.x;
int y = point.y;
boolean horizontalwin = checkHorizontal(x, y, points);
boolean verticalwin = checkvertical(x, y, points);
boolean leftwin = checkleftDiagonal(x, y, points);
boolean rightwin = checkRightDigonal(x, y, points);
if (horizontalwin || verticalwin || leftwin || rightwin) {
return true;
}
}
return false;
}
private boolean checkvertical(int x, int y, List<Point> points) {
int count = 1;
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x, y + i))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x, y - i))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
return false;
}
private boolean checkleftDiagonal(int x, int y, List<Point> points) {
int count = 1;
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x - i, y + i))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x + i, y - i))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
return false;
}
private boolean checkRightDigonal(int x, int y, List<Point> points) {
int count = 1;
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x + i, y + i))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x - i, y - i))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
return false;
}
/**
* 检查横向的是否胜利
*
* @param x
* @param y
* @param points
*/
private boolean checkHorizontal(int x, int y, List<Point> points) {
int count = 1;
//左边
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x - i, y))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
for (int i = 1; i < MaxCountLine; i++) {
if (points.contains(new Point(x + i, y))) {
count++;
} else {
break;
}
}
if (count == MaxCountLine) {
return true;
}
return false;
}
重置的代码
public void reStart() {
//清楚所有的list
mWhiteList.clear();
mBlackList.clear();
//标识位恢复
mIsGameOver = false;
misWhiteWinner = false;
invalidate();
}
屏幕状态改变后,保存当前的棋盘信息
private static final String INSTANCE = "instance";//主标识,用来存放super
//存放游戏是否结束
private static final String INSTANCE_GAME_OVER = "instance_game_over";
//存放当前轮到什么颜色的棋子下
private static final String INSTANCE_ME_ISWHITE = "instance_me_iswhite";
//存放白棋的list
private static final String INSTANCE_WHITE_ARRAY = "instance_white_array";
private static final String INSTANCE_BLACK_ARRAY = "instance_black_array";
//重写,存储
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE, super.onSaveInstanceState());
bundle.putBoolean(INSTANCE_GAME_OVER, mIsGameOver);
bundle.putBoolean(INSTANCE_ME_ISWHITE, meisWhite);
bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY, mWhiteList);
bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY, mBlackList);
return bundle;
}
//恢复
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mIsGameOver = bundle.getBoolean(INSTANCE_GAME_OVER);
mBlackList = bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY);
mWhiteList = bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY);
meisWhite = bundle.getBoolean(INSTANCE_ME_ISWHITE);
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE));
return;
}
super.onRestoreInstanceState(state);
}
最后的效果