首页 > 代码库 > 五子棋--自定义控件

五子棋--自定义控件

package com.example.administrator.viewapp;

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

import java.util.ArrayList;

/**
 * Created by Zyh on 2016/11/18.
 */
public class WuZiPanel extends View {


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //系统传递的参数widthMeasureSpec和heightMeasureSpec,我们可以根据这两个参数获取当前控件的宽高
        //首先获取当前的大小
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //我们画正方形需要去宽和高中的最小值
        int width = Math.min(widthSize, heightSize);
        //然后将测量的结果设置给系统,这个方法是最终设置自定义view大小的方法
        setMeasuredDimension(width, width);
//        Log.d("系统给我们的宽度-------------->", "" + width);
//        Log.d("系统给我们的宽度(转换后)---->", widthSize+"----" + heightSize);
        //初始化棋盘宽度和线的间距,需要在setMeasureDimsion之后进行
    }

    //在onMeasure中调用setMeasuredDimension方法之后,接下来系统会执行onSizeChanged方法
    //所以初始化棋盘宽度和线的间距要在onSizeChanged中进行
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d("new-----------",w+"----"+h);
        Log.d("old-----------",oldw+"---"+oldh);
        mPanelWidth = w;
        mLineHeight = w * 1.0f / MAX_LIME;//首先将宽度转换成float类型
        //规定旗子的宽高
        int width = (int) (mLineHeight * 3 / 4);
        //按比例重新设置宽高
        mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece, width, width, false);
        mBlackPiece = Bitmap.createScaledBitmap(mBlackPiece, width, width, false);
    }
    //初始化画笔,
    private Paint mPaint = new Paint();
    //横线竖线的数量,
    private static final int MAX_LIME = 12;
    //棋盘的宽度,和高度
    private int mPanelWidth;
    //线和线之间的间距
    private float mLineHeight;
    //记录白棋和黑棋的落子位置
    private ArrayList<Point> mWhiteArry = new ArrayList<>();
    private ArrayList<Point> mBlackArray = new ArrayList<>();
    private boolean mIsWhite = true;//判断目前谁走

    //系统会默认调用两个参数的构造方法
    public WuZiPanel(Context context, AttributeSet attrs) {
        super(context, attrs);
//        0X--代表16进制的数值的写法前缀
        setBackgroundColor(0x66ff0000);
        mPaint.setStrokeWidth(2);//设置画笔的宽度
        mPaint.setTextSize(18);
        inte();
    }
    private void inte() {
        mPaint.setColor(0xaa000000);
        mPaint.setStyle(Paint.Style.STROKE); //设置划一条线
        mWhitePiece = BitmapFactory.decodeResource(getResources(), R.mipmap.baizi);
        mBlackPiece = BitmapFactory.decodeResource(getResources(), R.mipmap.heizi);

    }

    /**
     * onTouchEvent内有四个返回值:true,false,和super.onTouchEvent(event)
     * true:表示拦截此次事件,不再交给其他方法使用
     * false:表示对此次事件不感兴趣,让系统分发给其他的方法
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //游戏已结束那么点击事件就不关心了
        if (misGameOver){
            return false;
        }
        //MotionEvent.ACTION_DOWN--指鼠标点下的事件
        //MotionEvent.ACTION_UP---指鼠标抬起的事件
        if (event.getAction() == MotionEvent.ACTION_UP) {
//            Toast.makeText(getContext(), "鼠标抬起事件", Toast.LENGTH_SHORT).show();
            int x = (int) event.getX();
            int y = (int) event.getY();
            Point p = new Point((int) (x / mLineHeight), (int) (y / mLineHeight));
            //表示不能重复落子,那么此次的点击事件我们交给系统,我们不做任何的处理
            if (mWhiteArry.contains(p) || mBlackArray.contains(p)) {
                return false;
            }
            //如果正确落子,记录当前的落子的位置,并将它加入黑白集合当中
            mCurPoint=p;
//            Toast.makeText(getContext(), (int) (y / mLineHeight) + "当前交叉点的坐标" + (int) (x / mLineHeight), Toast.LENGTH_SHORT).show();
           //判断该谁走
            if (mIsWhite) {
                mWhiteArry.add(new Point((int) (x / mLineHeight), (int) (y / mLineHeight)));
                mIsWhite = false;
            } else {
                mBlackArray.add(new Point((int) (x / mLineHeight), (int) (y / mLineHeight)));
                mIsWhite = true;
            }
//            Toast.makeText(getContext(), x + "---" + y, Toast.LENGTH_SHORT).show();
            //记录下点击的位置,请求重新绘制界面,重新调用onDraw方法
            invalidate();//作用重新调用onDraw
            postInvalidate();//请求在非UI线程中重绘

            return true;//这里代表我们已经处理过点击事件不需要去返回给系统
        }
        //我们必须首先将点击下的动作拦截下来才能接下来拦截抬起的动作
        else if (event.getAction() == MotionEvent.ACTION_DOWN) {
//            Toast.makeText(getContext(), "鼠标抬起事件", Toast.LENGTH_SHORT).show();
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**canvas---画布,系统提供
         canvas.drawLine(0,0,300,300,mPaint);//画一条线:参数起始的坐标和终止的坐标以及画笔
         canvas.drawCircle(150,150,40,mPaint);//画圆,起始坐标指定的是圆心的坐标
         canvas.drawText("我的世界",150,150,mPaint);//写字,参数说明:字体,起始坐标,画笔
         canvas.drawRect(10,10,200,200,mPaint);//画矩形
         画Bitmap,
         canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher),0,0,mPaint);
         */
        drawBoard(canvas);
        drawPiece(canvas);
        //每一次落子之后都要进行胜负判断
        checkGameIsOver();
    }
    private boolean misGameOver=false;
    private boolean misWhiteWinner=false;
    private void checkGameIsOver() {
        boolean isBlackWin=false;
        boolean isWhiteWin=false;
        if (mIsWhite){
            isBlackWin=checkFiveInLine(mBlackArray);
        }else {
            isWhiteWin=checkFiveInLine(mWhiteArry);
        }
        if (isBlackWin||isWhiteWin){
            //只要有一个胜利,那么游戏就结束
            misGameOver=true;
            misWhiteWinner=isWhiteWin;
            //写一个监听的接口,将胜负发送到对应的activity中处理
            //问号在这里表示判断的
            String str=misWhiteWinner?"白棋胜利":"黑棋胜利";
            Toast.makeText(getContext(),str, Toast.LENGTH_SHORT).show();
        }
    }
    private Point mCurPoint;

    private boolean checkFiveInLine(ArrayList<Point> mArry) {
        //如果落下的是白子,mIsWhite是false,如果落下的是黑子,那么mIsWhite是true
//        if ((!mIsWhite&&isWhite)||(mIsWhite&&!isWhite)){
//            //mCurPoint白棋+白棋mArray或者mCurPoint黑棋+黑棋mArray
//            //符合这两种情况接下来往下判断,否则跳出
//        }else {
//            return false;
//        }
        //如果当前位置旗子的位置不为空,判断他的四个方向是否满足胜利条件
        if (mCurPoint!=null){
//            Toast.makeText(getContext(),x+""+y, Toast.LENGTH_SHORT).show();
           if (checkHorIsFive(mArry,mCurPoint))
               return true;
            if (checkVerIsFive(mArry,mCurPoint)){
                return true;
            }
            if (checkRightAngileIsFive(mArry,mCurPoint)){
                return true;
            }
            if (checkLelfeAngileIsFive(mArry,mCurPoint)){
                return true;
            }
        }

        return false;
    }

    private boolean checkHorIsFive(ArrayList<Point> mArry,Point mPoint) {
        //首先判断横向是否凑齐五个子 第二次遍历向右判断
        int x=mCurPoint.x;
        int y=mCurPoint.y;
        int count=1;
        boolean isFirstBreak=true;
        boolean isSecondBreak=true;
        for (int i=1;i<5;i++){
            if (isFirstBreak){
                if (mArry.contains(new Point(x-i,y))){
                    count++;
                }else {
                    //向右判断
                    isFirstBreak=false;
                }
            }
            //如果是true的话,就代表当前位置旗子右边有相同的子,count加1
            if (isSecondBreak){
                if (mArry.contains(new Point(x+i,y))){
                    count++;
                }else {
                    //向右判断
                    isSecondBreak=false;
                }
            }
            //如果已经达到5颗子,跳出循环
        }
        if(count==5){
            return true;
        }else {
            return false;
        }
    }
    private boolean checkVerIsFive(ArrayList<Point> mArry,Point mPoint) {
        //首先判断横向是否凑齐五个子 第二次遍历向右判断
        int x=mCurPoint.x;
        int y=mCurPoint.y;
        int count=1;
        boolean isFirstBreak=true;
        boolean isSecondBreak=true;
        for (int i=1;i<5;i++){
            if (isFirstBreak){
                if (mArry.contains(new Point(x,y-i))){
                    count++;
                }else {
                    //向右判断
                    isFirstBreak=false;
                }
            }
            //如果是true的话,就代表当前位置旗子右边有相同的子,count加1
            if (isSecondBreak){
                if (mArry.contains(new Point(x,y+i))){
                    count++;
                }else {
                    //向右判断
                    isSecondBreak=false;
                }
            }
            //如果已经达到5颗子,跳出循环
        }
        if(count==5){
            return true;
        }else {
            return false;
        }
    }
    private boolean checkRightAngileIsFive(ArrayList<Point> mArry,Point mPoint) {
        //首先判断横向是否凑齐五个子 第二次遍历向右判断
        int x=mCurPoint.x;
        int y=mCurPoint.y;
        int count=1;
        boolean isFirstBreak=true;
        boolean isSecondBreak=true;
        for (int i=1;i<5;i++){
            if (isFirstBreak){
                if (mArry.contains(new Point(x-i,y-i))){
                    count++;
                }else {
                    //向右判断
                    isFirstBreak=false;
                }
            }
            //如果是true的话,就代表当前位置旗子右边有相同的子,count加1
            if (isSecondBreak){
                if (mArry.contains(new Point(x+i,y+i))){
                    count++;
                }else {
                    //向右判断
                    isSecondBreak=false;
                }
            }
            //如果已经达到5颗子,跳出循环
        }
        if(count==5){
            return true;
        }else {
            return false;
        }
    }
    private boolean checkLelfeAngileIsFive(ArrayList<Point> mArry,Point mPoint) {
        //首先判断横向是否凑齐五个子 第二次遍历向右判断
        int x=mCurPoint.x;
        int y=mCurPoint.y;
        int count=1;
        boolean isFirstBreak=true;
        boolean isSecondBreak=true;
        for (int i=1;i<5;i++){
            if (isFirstBreak){
                if (mArry.contains(new Point(x-i,y+i))){
                    count++;
                }else {
                    //向右判断
                    isFirstBreak=false;
                }
            }
            //如果是true的话,就代表当前位置旗子右边有相同的子,count加1
            if (isSecondBreak){
                if (mArry.contains(new Point(x+i,y-i))){
                    count++;
                }else {
                    //向右判断
                    isSecondBreak=false;
                }
            }
            //如果已经达到5颗子,跳出循环
        }
        if(count==5){
            return true;
        }else {
            return false;
        }
    }

    private void drawPiece(Canvas canvas) {
        //画旗子的方法
        for (int i = 0; i < mWhiteArry.size(); i++) {
            Point point = mWhiteArry.get(i);
            float x = (float) ((point.x + 1.0 / 8) * mLineHeight);
            float y = (float) ((point.y + 1.0 / 8) * mLineHeight);
            canvas.drawBitmap(mWhitePiece, x, y, null);
        }
        for (int i = 0; i < mBlackArray.size(); i++) {
            Point point = mBlackArray.get(i);
            float x = (float) ((point.x + 1.0 / 8) * mLineHeight);
            float y = (float) ((point.y + 1.0 / 8) * mLineHeight);
            canvas.drawBitmap(mBlackPiece, x, y, null);
        }
    }

    private Bitmap mWhitePiece;
    private Bitmap mBlackPiece;

    private void drawBoard(Canvas canvas) {
        int w = mPanelWidth;
        float lineHeight = mLineHeight;
        for (int i = 0; i < MAX_LIME; i++) {
            int startX = (int) (lineHeight / 2);
            int endX = w - startX;
            int Y = (int) ((0.5 + i) * lineHeight);
            canvas.drawLine(startX, Y, endX, Y, mPaint);
        }
        for (int i = 0; i < MAX_LIME; i++) {
            int starY = (int) (lineHeight / 2);
            int X = (int) ((int) (lineHeight / 2) + i * lineHeight);
            int endY = (int) (w - lineHeight / 2);
            canvas.drawLine(X, starY, X, endY, mPaint);
        }
    }

    /**
     * 状态的保存和恢复,
     * @return
     */
private  static final String INSTANCE="INSTANCE";
    private  static final String INSTANCE_mWhiteArry="mWhiteArry";
    private  static final String INSTANCE_mBlackArray="mBlackArray";
    private  static final String INSTANCE_misGameOver="misGameOver";
    private  static final String INSTANCE_mIsWhite="mIsWhite";
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle=new Bundle();
        bundle.putParcelableArrayList(INSTANCE_mWhiteArry,mWhiteArry);
        bundle.putParcelableArrayList(INSTANCE_mBlackArray,mBlackArray);
        bundle.putBoolean(INSTANCE_misGameOver,misGameOver);
        bundle.putBoolean(INSTANCE_mIsWhite,mIsWhite);
        bundle.putParcelable(INSTANCE,super.onSaveInstanceState());
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        //如果是bundle类型就表明我们传递的有值,屏幕发生旋转或者activity强制被杀死,保存过信息
        if (state instanceof Bundle){
            Bundle bundle= (Bundle) state;
            mIsWhite=bundle.getBoolean(INSTANCE_mIsWhite,false);
            misGameOver=bundle.getBoolean(INSTANCE_misGameOver,false);
            mBlackArray=bundle.getParcelableArrayList(INSTANCE_mBlackArray);
            mWhiteArry=bundle.getParcelableArrayList(INSTANCE_mWhiteArry);
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE));
            return;
        }
        super.onRestoreInstanceState(state);
    }


}

 

五子棋--自定义控件