安卓自定义View游戏篇-(五子棋)

1、效果图:

2、GobangPanel棋盘面板:

public class GobangPanel extends View {

    private int mPanelWidth;//棋盘的宽度
    private float mLineHeight;//行,高要为float
    private int MAX_LINE = 15;//棋盘行数
    private int MAX_COUNT_IN_LINE = 5;//设置赢棋子个数(6子棋设置为6)

    private Paint mPaint = new Paint();//画笔
    private Bitmap mWhitePiece;//白色棋子
    private Bitmap mBlackPiece;//黑色棋子

    private float ratioPieceOfLineHeight = 3 * 1.0f / 4;//2个棋子间3/4距离

    //白旗先手,当前轮到白棋了
    private boolean mIsWhite = true;
    private ArrayList mWhiteArray = new ArrayList<>();//白棋数组
    private List mBlackArray = new ArrayList<>();//黑骑数组

    private boolean mIsGameOver;//游戏是否结束
    private boolean mIsWhiteWinner;//白色棋子赢 true白子赢,false黑色赢

    public GobangPanel(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 初始化画笔参数
     */
    private void init() {
        mPaint.setColor(0x88000000);//Paint颜色 半透明灰色
        mPaint.setAntiAlias(true);//抗锯齿(边界明显变模糊)
        mPaint.setDither(true);//设置防抖动(图片柔和)
        mPaint.setStyle(Paint.Style.STROKE);//样式
        //黑、白棋资源图片
        mWhitePiece = BitmapFactory.decodeResource(getResources(), R.mipmap.white_bg);
        mBlackPiece = BitmapFactory.decodeResource(getResources(), R.mipmap.black_bg);
    }

    /**
     * 自定义View尺寸的规则
     */
    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.max(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 pieceWidth = (int) (mLineHeight * ratioPieceOfLineHeight);    //比例
        mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece, pieceWidth, pieceWidth, false);//棋子跟随控件变化
        mBlackPiece = Bitmap.createScaledBitmap(mBlackPiece, pieceWidth, pieceWidth, false);
    }

    //触摸焦点
    public boolean onTouchEvent(MotionEvent event) {

        if (mIsGameOver) 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 (mWhiteArray.contains(p) || mBlackArray.contains(p)) {
                return false;
            }
            if (mIsWhite) {
                mWhiteArray.add(p);
            } else {
                mBlackArray.add(p);
            }
            invalidate();//重绘棋子
            mIsWhite = !mIsWhite;
        }
        return true;//感兴趣交给其处理
    }

    private Point getValidPoint(int x, int y) {
        return new Point((int) (x / mLineHeight), (int) (y / mLineHeight));
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBoard(canvas);
        drawPieces(canvas);
        checkGameOver();
    }

    private static final String TAG = "GobangPanel";
    /**
     * 游戏结束方法
     */
    public void checkGameOver() {
        boolean whiteWin = checkFiveInLine(mWhiteArray);
        boolean blackWin = checkFiveInLine(mBlackArray);
        //黑棋或白棋赢游戏结束
        if (whiteWin || blackWin) {
            mIsGameOver = true;
            mIsWhiteWinner = whiteWin;
            if (null != listener) {
                listener.onFinish(mIsWhiteWinner);
                Log.e(TAG, "checkGameOver: 111111" );
            }
            Log.e(TAG, "checkGameOver: 222222" );
        }
    }

    /**
     * 判断棋子是否5个相邻【5个相连只有4中情况分别是:水平、垂直、左斜和右斜】
     */
    private boolean checkFiveInLine(List points) {

        for (Point p : points) {
            int x = p.x;
            int y = p.y;
            boolean win = checkLevel(x, y, points);
            if (win) return true;
            win = checkVetical(x, y, points);
            if (win) return true;
            win = checkLeftWin(x, y, points);
            if (win) return true;
            win = checkRightWin(x, y, points);
            if (win) return true;
        }
        return false;
    }

    /**
     * 判断x,y位置的棋子是否【水平】有相邻的五个一致
     */
    private boolean checkLevel(int x, int y, List points) {
        int count = 1;
        //横向左边棋子个数
        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x - i, y))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢
        if (count == MAX_COUNT_IN_LINE) return true;

        //横向右边棋子个数
        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x + i, y))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢
        if (count == MAX_COUNT_IN_LINE) return true;
        return false;
    }

    /**
     * 判断x,y位置的棋子是否[垂直]有相邻的五个一致
     */
    private boolean checkVetical(int x, int y, List points) {
        int count = 1;
        //上下棋子个数
        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x, y - i))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢,return true;
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x, y + i))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢
        if (count == MAX_COUNT_IN_LINE) return true;

        return false;
    }

    /**
     * 判断x,y位置的棋子是否【左斜和右斜】有相邻的五个一致
     */
    private boolean checkLeftWin(int x, int y, List points) {
        int count = 1;
        //横向上下棋子个数
        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x - i, y + i))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢,return true;
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x + i, y - i))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢
        if (count == MAX_COUNT_IN_LINE) return true;

        return false;
    }

    /**
     * 判断x,y位置的棋子是否【右斜】有相邻的五个一致
     */
    private boolean checkRightWin(int x, int y, List points) {

        int count = 1;
        //横向上下棋子个数
        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x - i, y - i))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢,return true;
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            //如果有加1,没有重新计算是否有五个,否者中断
            if (points.contains(new Point(x + i, y + i))) {
                count++;
            } else {
                break;
            }
        }
        //有5个时则赢
        if (count == MAX_COUNT_IN_LINE) return true;

        return false;
    }

    private void drawPieces(Canvas canvas) {
        //白色棋子
        for (int i = 0, n = mWhiteArray.size(); i < n; i++) {
            Point whitePoint = mWhiteArray.get(i);
            canvas.drawBitmap(mWhitePiece,
                    (whitePoint.x + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight,
                    (whitePoint.y + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, null);
        }
        //黑色棋子
        for (int i = 0, n = mBlackArray.size(); i < n; i++) {
            Point blackPoint = mBlackArray.get(i);
            canvas.drawBitmap(mBlackPiece,
                    (blackPoint.x + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight,
                    (blackPoint.y + (1 - ratioPieceOfLineHeight) / 2) * mLineHeight, null);
        }
    }

    /**
     * 画格子棋盘
     */
    private void drawBoard(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);
        }
    }

    /**
     * 再来一局
     */
    public void start() {
        if (null != mWhiteArray && null != mBlackArray) {
            mWhiteArray.clear();//清除数据
            mBlackArray.clear();
        }
        mIsGameOver = false;
        mIsWhiteWinner = false;
        invalidate();    //再次调用
    }

    /**
     * 后台运行时,调用此方法,防止数据丢失
     */
    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";

    /**
     * 保存数据
     */
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable(INSTANCE, super.onSaveInstanceState());
        bundle.putBoolean(INSTANCE_GAME_OVER, mIsGameOver);
        bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY, mWhiteArray);
        bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY, mWhiteArray);
        return bundle;
    }

    /**
     * 恢复时调用
     */
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            mIsGameOver = bundle.getBoolean(INSTANCE_GAME_OVER);
            mWhiteArray = bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY);
            mBlackArray = bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY);
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE));
            return;
        }
        super.onRestoreInstanceState(state);
    }

    /**
     * 游戏结束回调
     */
    public OnFinishListener listener;

    public void setListener(OnFinishListener listener) {
        this.listener = listener;
    }

    public interface OnFinishListener {

        void onFinish(boolean mIsWhiteWinner);
    }
}

3、使用MainActivity

public class MainActivity extends AppCompatActivity {

    private GobangPanel panel;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        panel = this.findViewById(R.id.gobang_panel);
        panel.setListener(new GobangPanel.OnFinishListener() {
            @Override
            public void onFinish(boolean mIsWhiteWinner) {
                initDialog(mIsWhiteWinner);
            }
        });
    }

    /**
     * 初始化弹框
     */
    private void initDialog(boolean mIsWhiteWinner) {
        AlertDialog dialog = new AlertDialog.Builder(this)
                //.setTitle("这是标题")
                .setMessage(mIsWhiteWinner ? "白棋胜利,是否重新开始?" : "黑棋胜利,是否重新开始?")
                //.setIcon(R.mipmap.ic_launcher)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {//添加"Yes"按钮
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        panel.start();
                    }
                })
//                .setNegativeButton("取消", new DialogInterface.OnClickListener() {//添加取消
//                    @Override
//                    public void onClick(DialogInterface dialogInterface, int i) {
//                        Toast.makeText(MainActivity.this, "这是取消按钮", Toast.LENGTH_SHORT).show();
//                    }
//                })
                //方法一:setCanceledOnTouchOutside(false);按对话框以外的地方不起作用。按返回键起作用
                //方法二:setCanceleable(false);按对话框以外的地方不起作用。按返回键也不起作用
                .setCancelable(false)
                .create();
        dialog.show();
    }
}

对应布局文件:activity_main:



    
        
    


你可能感兴趣的:(View)