学习心得之 Android五子棋大战

来源:互联网 发布:三体电影跳票知乎 编辑:程序博客网 时间:2024/05/22 02:11

            第一次用csdn写文章感觉好高大上,最近跟Hyman老师学习了 Android中五子棋的实现,记录学习,共同成长。。。

话不多说,进入主题吧!既然是五子棋,那必定是有一个棋盘。那我们当然是首选是使用自定义View 来自定义我们的棋盘。

当然所有的核心代码也几乎全部包含于这个自定义View当中。

如图所示 红色区域即是我们的自定义view的区域。



import java.util.ArrayList;
import java.util.List;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;


//五子棋自定义 面板类(面板内里有绘制的棋盘,和棋子)
public class WuzhiqiPanel extends View {
// 棋盘 网格 的 每一个的高度(网格为正方形)
private float mLineHeight;
// 垂直方向有多少行网格的宽度
private int MAX_LINE = 10;
// 自定义面板的宽度
private int mPanelWidth;
// 绘制棋盘网格的画笔
private Paint mPaint = new Paint();
// 白棋本地 bitmap 资源
private Bitmap WhitePiece;
// 黑棋本地 bitmap 资源
private Bitmap BlackPiece;
// 棋子的宽度与单个网格的宽度比例
private static final float radios_Bmap_Panel_Size = 3 * 1.0f / 4;
// 用于保存白棋的落点 (point)的集合
private ArrayList<Point> WPointList = new ArrayList<Point>();
// 用于保存黑棋的落点 (point)的集合
private ArrayList<Point> BPointList = new ArrayList<Point>();
// 标记 当前是否为白棋落子
private Boolean IsWhitePiece = true;

//是否是游戏结束
private Boolean IsGameOver = false;

//是否是白棋赢
private Boolean IsWhiteWin;


private static final int MaxCheakCount = 5;


public WuzhiqiPanel(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0x44ff0000);
initPaint();


}


// 1.初始化画笔的设置,用于绘制棋盘网格。 2.初始化黑白棋的 本地bitmap资源
private void initPaint() {
mPaint.setColor(Color.parseColor("#000000"));
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStrokeWidth(2.5f);
mPaint.setStyle(Paint.Style.STROKE);
WhitePiece = BitmapFactory.decodeResource(getResources(),
R.drawable.stone_w2);
BlackPiece = BitmapFactory.decodeResource(getResources(),
R.drawable.stone_b1);


}


// 测量自定义面板
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.min(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 dstWidth = (int) (mLineHeight * radios_Bmap_Panel_Size);


WhitePiece = Bitmap.createScaledBitmap(WhitePiece, dstWidth, dstWidth,
false);
BlackPiece = Bitmap.createScaledBitmap(BlackPiece, dstWidth, dstWidth,
false);
}


@Override
protected void onDraw(Canvas canvas) {


super.onDraw(canvas);


/*
* int w = mPanelWidth; float lineheight = mLineHeight;
*/
DrawPanel(canvas);
Drawpiece(canvas);
CheakGameOver();
}


// 检查是否游戏结束
private void CheakGameOver() {
Boolean WhitepieceWin = CheakFiveLine(WPointList);
Boolean BlackpieceWin = CheakFiveLine(BPointList);
if (WhitepieceWin || BlackpieceWin) {
String Text = WhitepieceWin ? "白棋胜利" : "黑棋胜利";
IsGameOver = true;
Toast.makeText(getContext(), Text, 0).show();
}


}


private Boolean CheakFiveLine(List<Point> PointList) {
Boolean IsFiveLine = false;
for (Point mPoint : PointList) {
int x = mPoint.x;
int y = mPoint.y;
Boolean fiveline1 = CheakHorizonal(x, y, PointList);
Boolean fiveline2 = CheakVertical(x, y, PointList);
Boolean fiveline3 = CheakLeftSlant(x, y, PointList);
Boolean fiveline4 = CheakRightSlant(x, y, PointList);
IsFiveLine = (fiveline1 || fiveline2 || fiveline3 || fiveline4);


if (IsFiveLine) {
break;
}
}


return IsFiveLine;
}


private Boolean CheakRightSlant(int x, int y, List<Point> pointList) {
// 右上
int Count = 1;
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x - i, y - i))) {
Count++;
} else {
break;
}
}
// 右下
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x + i, y + i))) {
Count++;
} else {
break;
}
}


if (Count == 5) {
return true;
} else {
return false;
}


}


private Boolean CheakLeftSlant(int x, int y, List<Point> pointList) {
// 左上
int Count = 1;
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x + i, y - i))) {
Count++;
} else {
break;
}
}
// 左下
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x - i, y + i))) {
Count++;
} else {
break;
}
}


if (Count == 5) {
return true;
} else {
return false;
}


}


private Boolean CheakVertical(int x, int y, List<Point> pointList) {
// 上
int Count = 1;
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x, y - i))) {
Count++;
} else {
break;
}
}
// 下
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x, y + i))) {
Count++;
} else {
break;
}
}


if (Count == 5) {
return true;
} else {
return false;
}


}


private Boolean CheakHorizonal(int x, int y, List<Point> pointList) {
// 左
int Count = 1;
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x - i, y))) {
Count++;
} else {
break;
}
}
// 右
for (int i = 1; i <= MaxCheakCount; i++) {
if (pointList.contains(new Point(x + i, y))) {
Count++;
} else {
break;
}
}


if (Count == 5) {
return true;
} else {
return false;
}


}


private void Drawpiece(Canvas canvas) {
int n1 = WPointList.size();
int n2 = BPointList.size();
for (int i = 0; i < n1; i++) {
Point point = WPointList.get(i);
canvas.drawBitmap(WhitePiece,
(point.x + (1 - radios_Bmap_Panel_Size) / 2) * mLineHeight,
(point.y + (1 - radios_Bmap_Panel_Size) / 2) * mLineHeight,
null);
}
for (int i = 0; i < n2; i++) {
Point point = BPointList.get(i);
canvas.drawBitmap(BlackPiece,
(point.x + (1 - radios_Bmap_Panel_Size) / 2) * mLineHeight,
(point.y + (1 - radios_Bmap_Panel_Size) / 2) * mLineHeight,
null);
}


}


private void DrawPanel(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);
}


}


@Override
public boolean onTouchEvent(MotionEvent event) {
if (IsGameOver) {
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 (WPointList.contains(p) || BPointList.contains(p)) {
return false;
}
if (IsWhitePiece) {
WPointList.add(p);
} else {
BPointList.add(p);
}


IsWhitePiece = !IsWhitePiece;


}
for (Point mPoint : WPointList) {
System.out.println(mPoint.toString());
}
for (Point mPoint : BPointList) {
System.out.println(mPoint.toString());
}


invalidate();


return true;


}

//将屏幕的真实坐标转化成网格的坐标
private Point getValidPoint(int x, int y) {
Point mpoint = new Point((int) (x / mLineHeight),
(int) (y / mLineHeight));
return mpoint;
}


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";


@Override
protected Parcelable onSaveInstanceState() {
Bundle mBundle = new Bundle();
mBundle.putParcelable(INSTANCE, super.onSaveInstanceState());
mBundle.putBoolean(INSTANCE_GAME_OVER, IsGameOver);
mBundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY, WPointList);
mBundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY, BPointList);
return mBundle;
}



@Override
protected void onRestoreInstanceState(Parcelable state) {
if(state instanceof Bundle){
Bundle mbundle = (Bundle)state;
IsGameOver = mbundle.getBoolean(INSTANCE_GAME_OVER);
WPointList = mbundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY);
BPointList = mbundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY);
super.onRestoreInstanceState(mbundle.getParcelable(INSTANCE));
return ;
}
super.onRestoreInstanceState(state);
}
}




以上将代码拷贝了一通。当我们将自定义view在布局文件应用时,首先进入构造方法

1.
         public WuzhiqiPanel(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0x44ff0000);
initPaint();


}

可以看到构造方法中除了设置了一下view的背景之外,我们还 初始化了一下画笔对象



// 1.初始化画笔的设置,用于绘制棋盘网格。 2.初始化黑白棋的 本地bitmap资源
private void initPaint() {
mPaint.setColor(Color.parseColor("#000000"));
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStrokeWidth(2.5f);
mPaint.setStyle(Paint.Style.STROKE);
WhitePiece = BitmapFactory.decodeResource(getResources(),
R.drawable.stone_w2);
BlackPiece = BitmapFactory.decodeResource(getResources(),
R.drawable.stone_b1);


}

进入 initPaint 正如注释所说 初始化了画笔和本地的黑白棋bitmap资源,而对于画笔的这些set方法你们可以自己查下一下,抗锯齿,线粗,还有Style


然后系统进入一个view的测量,绘制的过程。

运行进入

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法。

在这个方法中 我们从  widthMeasureSpec 与 heightMeasureSpec参数中获取到测量的View的值,然后取之中较小的那个作为宽高

重新setMeasuredDimension(width, width);  让去呈现一个正方形。


之后运行就会进入  protected void onSizeChanged(int w, int h, int oldw, int oldh)也就是控件的Size发生了变化系统 回调该方法。

这里面我们除了初始化了一些成员变量以外,我们还设定了 黑白棋图片的宽高,设为了正方形网格的3/4,这样就让整个界面更协调些,当然也方便了下面绘制


当然运行到这里我们的Activity还是处于空白的状态,接下来运行的是重写的绘制方法.

protected void onDraw(Canvas canvas) {


super.onDraw(canvas);


/*
 * int w = mPanelWidth; float lineheight = mLineHeight;
 */
DrawPanel(canvas);
Drawpiece(canvas);
CheakGameOver();
}

里面执行了3个我们自己的方法 绘制棋盘,绘制棋子,检查是否游戏结束。

最后执行的

public boolean onTouchEvent(MotionEvent event) 在ACTION_UP事件里处理逻辑。

落子前检查是否游戏结束,是的话不能继续响应事件。点击up后将对应的棋子存入自己的集合,下完一次改变IsWhitepiece让下一为不同的棋子,最后重绘一遍。

   最后两个方法是关于View的复用的,比如说下棋下到一半来了电话,等你接完电话系统可能因为运行内存不足将你的五子棋Activity给kill了,



protected Parcelable onSaveInstanceState()

protected void onRestoreInstanceState(Parcelable state)

通过重写这两个方法  一个在销毁前 保存一些变量,当重现后 onRestoreInstanceState 这些变量,就保证了棋子还在原来的位子。


关于判断是否5子连珠的思想是 分为是个 情况 1 水平 2 垂直 3 左斜 4右斜   遍历每个棋子  判断相邻的是否有5个  也就是Count的值


绘制里面的坐标的细节,需要动手画一画,我这比较难讲清。

好了,大概的梳理了整个代码的结构。。。就到这里吧  谢谢。。。
























0 0
原创粉丝点击