安卓五子棋小游戏

一款五子棋小游戏,可以拿来联系一下自定义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();//构造方法中需要初始化的变量
    }

2.在构造方法中调用初始化方法,初始化一些变量

   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);
    }

3.在onMeasure()方法中对View的宽高测量模式进行判断

  @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;//设置最大几子相连获胜

4.在onSizeChange()方法中去初始化棋盘的网格高度等一些数据(此方法是在onMeasure()调用之后执行的)

    //当测量工作完成时回调此方法
    @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);
    }

6.重写onDraw()方法

  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);
        }
    }

7.每次绘制的时候都要检查一下是否有五连的棋子

检测得方法也给抽取出来,分为横向五连的检测, 纵向五连的检测, 以及斜对角五连的检测

//检测棋子是否获胜,要检测每个棋子横向||纵向||斜方向上是否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;
    }

三种情况只要有一种符合就return true;然后就是每种检测的具体实现了

 //斜向检测
    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;
    }

一旦返回true就在onDraw()方法中获取到,并且停止绘制,把isGameOVer变量=true;在下次onTouch的时候也不触发

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不能完成重建回复
最后完成了,你也可以把棋盘行数搞成一个自定义的属性,这样就可以在布局中给他设置行数了



你可能感兴趣的:(android,自定义View,五子棋,游戏,android)