一款五子棋小游戏,可以拿来联系一下自定义View Demo源码下载: 源码 (studio上面构建的项目,导入时要配置一下gradle文件)
1.写一个类继承View
/**
* Created by fanday on 2016/4/23.
*/
public class PieceView extends View {
private int lineNum=10;//棋盘的行列数
private float lineHeight;//每一行的行高
private int width;//棋盘的边长(棋盘是正方形的)
private Paint paint;
public PieceView(Context context) {
this(context,null);//一个参数的构造调用两个的,最终都走三个参数的构造
}
public PieceView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public PieceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();//构造方法中需要初始化的变量
}
private Bitmap whitePiece;//白棋
private Bitmap blackPiece;//黑棋
private double intScale=0.75;//棋子显示到棋盘上面是以棋盘的网格大小为基准的,棋子的缩放比例
private Paint piecePaint;//画棋子的画笔对象
private void init() {
piecePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
paint=new Paint();//画网格的画笔对象
paint.setAntiAlias(true);
paint.setColor(0xffff0000);
paint.setStyle(Paint.Style.STROKE);//设置画笔只描边
this.setBackgroundColor(0x44aaaaaa);
whitePiece= BitmapFactory.decodeResource(getResources(), R.drawable.white);
blackPiece=BitmapFactory.decodeResource(getResources(),R.drawable.black);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize=MeasureSpec.getSize(widthMeasureSpec);//获取宽度的sixe
int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获取宽度的Mode
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
if(widthMode==MeasureSpec.UNSPECIFIED){//如果宽度的测量模式不确定,就让宽度=高度
widthSize=heightSize;
}
else if(heightMode==MeasureSpec.UNSPECIFIED){//若果高度的测量模式不确定的,就让宽度=宽度
widthSize=widthSize;
}
setMeasuredDimension(widthSize,widthSize);//设置view的宽高
}
private boolean isWhiteWin;
private boolean isGameOver;
private int MAX_CONUNT=5;//设置最大几子相连获胜
//当测量工作完成时回调此方法
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
width=w;
lineHeight=w*1.0f/lineNum;
whitePiece=Bitmap.createScaledBitmap(whitePiece,(int)(intScale*lineHeight),(int)(intScale*lineHeight),false);//创建适应棋盘大小的的棋子Bitmap对象
blackPiece=Bitmap.createScaledBitmap(blackPiece,(int)(intScale*lineHeight),(int)(intScale*lineHeight),false);
}
Bitmap.createScaledBitmap这个api可以创建一个和原图一样的缩放的Bitmap对象
5.重写onTouchEvent()事件,把触摸的点的位置Point记录到集合中去
private ArrayList whitePoints=new ArrayList<>();//点击网格时记录点击坐标的集合
private ArrayList blackPoints=new ArrayList<>();
private boolean isWhite=true;
@Override
public boolean onTouchEvent(MotionEvent event) {
if(isGameOver){
if(isWhiteWin){
Toast.makeText(getContext(),"白子胜利了!",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getContext(),"黑子胜利了!",Toast.LENGTH_SHORT).show();
}
return false;
}
switch (event.getAction()){
case MotionEvent.ACTION_DOWN://在按下的时候return turn;表明想获取到时间
return true;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP://在Up时处理点击放置棋子的逻辑
float x = event.getX();
float y = event.getY();
int pointX= (int) (x/lineHeight);
int pointY= (int) (y/lineHeight);
Point point=new Point(pointX,pointY);
if(whitePoints.contains(point)||blackPoints.contains(point))
return false;
if(isWhite){
whitePoints.add(point);
}
else{
blackPoints.add(point);
}
isWhite=!isWhite;
invalidate();
return true;
}
return super.onTouchEvent(event);
}
private boolean isWhiteWin;
private boolean isGameOver;
private int MAX_CONUNT=5;//设置最大几子相连获胜
@Override
protected void onDraw(Canvas canvas) {
for(int i=0;i
private void drawPiece(List list,Bitmap bitmap,Canvas canvas){
for (int i = 0; i < list.size(); i++) {
Point point=list.get(i);
canvas.drawBitmap(bitmap,(float)(lineHeight/2+lineHeight*point.x-intScale*lineHeight/2),(float)(lineHeight/2+lineHeight*point.y-intScale*lineHeight/2),piecePaint);
}
}
检测得方法也给抽取出来,分为横向五连的检测, 纵向五连的检测, 以及斜对角五连的检测
//检测棋子是否获胜,要检测每个棋子横向||纵向||斜方向上是否5连
private boolean checkPieceIsWin(List list) {
//check横向是否5连
for (int i = 0; i < list.size(); i++) {
boolean isHorizontalFive=checkHorizontal(list,list.get(i));
if (isHorizontalFive)
return true;
continue;
}
//检查纵向是否5连
for (int i = 0; i < list.size(); i++) {
boolean isVerticalFive=checkVertical(list,list.get(i));
if(isVerticalFive)
return true;
continue;
}
//检查斜向是否5连
for (int i = 0; i < list.size(); i++) {
boolean isSlantFive=checkSlant(list,list.get(i));
if (isSlantFive)
return true;
continue;
}
return false;
}
//斜向检测
private boolean checkSlant(List list, Point point) {
int countLURD =1;
int countLDRP =1;
int x=point.x;
int y=point.y;
//检查左上5个
for (int i = 1; i < MAX_CONUNT; i++) {
if (list.contains(new Point(x-i,y-i))){
countLURD++;
}else{
break;
}
}
//检查右下5个
for (int i = 1; i < MAX_CONUNT; i++) {
if (list.contains(new Point(x+i,y+i))){
countLURD++;
}else{
break;
}
}
//检查右上5个
for (int i = 1; i < MAX_CONUNT; i++) {
if (list.contains(new Point(x+i,y-i))){
countLDRP++;
}else{
break;
}
}
//检查左下5个
for (int i = 1; i < MAX_CONUNT; i++) {
if(list.contains(new Point(x-i,y+i)))
countLDRP++;
break;
}
if(countLDRP>=5||countLURD>=5)
return true;
return false;
}
//纵向
private boolean checkVertical(List list, Point point) {
int count =1;
int x=point.x;
int y=point.y;
//检查上边5个
for (int i = 1; i < MAX_CONUNT; i++) {
if (list.contains(new Point(x,y-i))){
count++;
}else{
break;
}
}
//检查下边5个
for (int i = 1; i < MAX_CONUNT; i++) {
if(list.contains(new Point(x,y+i)))
count++;
break;
}
if(count>=5)
return true;
return false;
}
//横向
private boolean checkHorizontal(List list,Point point) {
int count =1;
int x=point.x;
int y=point.y;
//检查左边5个
for (int i = 1; i < MAX_CONUNT; i++) {
if (list.contains(new Point(x-i,y))){
count++;
}else{
break;
}
}
//检查右边5个
for (int i = 1; i < MAX_CONUNT; i++) {
if(list.contains(new Point(x+i,y)))
count++;
break;
}
if(count>=5)
return true;
return false;
}
8.最后就是考虑到数据的存储问题了,假设别人打电话过来,然后你接了电话,回来下好的棋子就没了,是不是很懊恼,其实View还有Activity都提供了
onSaveInstanceState()和onRestoreInstanceState(Parcelable state)一个是在View销毁的时候调用存储数据,一个是在View重置的时候把数据
取出来
private final String INSTANCE="instance";
private final String INSTANCE_GAME_OVER="instance_game_over";
private final String INSTANCE_WHITE_LIST="instance_white_list";
private final String INSTANCE_BLACK_LIST="instance_black_list";
//Activity重建的时候把数据取出来,但是要想能够回到之前的状态,需要在布局中给他一个id
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle=new Bundle();
bundle.putParcelable(INSTANCE,super.onSaveInstanceState());
bundle.putParcelableArrayList(INSTANCE_BLACK_LIST,blackPoints);
bundle.putParcelableArrayList(INSTANCE_WHITE_LIST,whitePoints);
bundle.putBoolean(INSTANCE_GAME_OVER,isGameOver);
return bundle;
}
//时在Activity走生命周期的时候记录下要保存的信息
@Override
protected void onRestoreInstanceState(Parcelable state) {
if(state instanceof Bundle){
Bundle bundle=(Bundle)state;
isGameOver=bundle.getBoolean(INSTANCE_GAME_OVER);
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE));
whitePoints=bundle.getParcelableArrayList(INSTANCE_WHITE_LIST);
blackPoints=bundle.getParcelableArrayList(INSTANCE_BLACK_LIST);
return;
}
super.onRestoreInstanceState(state);
}
View重建的时候把数据取出来,但是要想能够回到之前的状态,但是需要在布局中给他一个id,否则View不能完成重建回复