2048游戏实现——GridLayout应用

学习自http://blog.csdn.net/lmj623565791/article/details/40020137

原博主用继承自RelativeLayout的布局实现,我用专门实现网格布局的GridLayout重新实现。另外用二维数组来存储每一个子View,逻辑上更清晰。

先上效果图




其中的每一个格子是一个自定义的View


public class Game2048Item extends View {

    private Paint paint;
    private int number;
    private Rect textRect;
    private String numberValue;

    public Game2048Item(Context context) {
        this(context, null);
    }

    public Game2048Item(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Game2048Item(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
    }

    public void setNumber(int number) {
        this.number = number;
        numberValue = "" + number;
        paint.setTextSize(50);
        textRect = new Rect();
        paint.getTextBounds(numberValue, 0, numberValue.length(), textRect);
        invalidate();
    }

    public int getNumber() {
        return number;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        String backColor;
        switch (number) {
            case 0:
                backColor = "#CCC0B3";
                break;
            case 2:
                backColor = "#EEE4DA";
                break;
            case 4:
                backColor = "#EDE0C8";
                break;
            case 8:
                backColor = "#F2B179";
                break;
            case 16:
                backColor = "#F49563";
                break;
            case 32:
                backColor = "#F5794D";
                break;
            case 64:
                backColor = "#F55D37";
                break;
            case 128:
                backColor = "#EEE863";
                break;
            case 256:
                backColor = "#EDB04D";
                break;
            case 512:
                backColor = "#ECB04D";
                break;
            case 1024:
                backColor = "#EB9437";
                break;
            case 2048:
                backColor = "#EA7821";
                break;
            default:
                backColor = "#EA7821";
                break;
        }

        /*每个方块的背景*/
        paint.setColor(Color.parseColor(backColor));
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);

        /*每个方块上的数字*/
        if (number != 0) {
            paint.setColor(Color.BLACK);
            float x = (getMeasuredWidth()-textRect.width())/2;
            float y = getMeasuredHeight()/2 + textRect.width()/2;
            canvas.drawText(numberValue, x, y, paint);
        }
    }
}


把他们用一个继承自GridLayout的自定义ViewGroup放置


public class Game2048GridLayout extends GridLayout {

    private int childWidth;
    private int padding;
    private int childRow = 4;
    private int margin = 10;
    private boolean isLayout = false;
    private Game2048Item[][] game2048Items = null;
    private GestureDetector gestureDetector;
    private boolean isMoveHappen = false;
    private boolean isMergeHappen = false;
    private boolean isFirst = true;
    private int score;
    private OnGame2048Listener onGame2048Listener;

    /*枚举手势操作类型*/
    private enum Action {
        UP, DOWN, LEFT, RIGHT
    }

    public Game2048GridLayout(Context context) {
        this(context, null);
    }

    public Game2048GridLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Game2048GridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        padding = Math.min(getPaddingBottom(), getPaddingTop());
        gestureDetector = new GestureDetector(context, new MyGestureListener());
        score = 0;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        /*布局的实际长宽一致,为两者中的最小值*/
        int length = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
        childWidth = (length - 2 * padding - (childRow - 1) * margin) / childRow;
        setMeasuredDimension(length, length);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        /*避免多次加载*/
        if (!isLayout) {
            if (game2048Items == null) {
                game2048Items = new Game2048Item[childRow][childRow];
            }

            for (int i = 0; i < childRow; i++) {
                for (int j = 0; j < childRow; j++) {
                    Game2048Item child = new Game2048Item(getContext());
                    game2048Items[i][j] = child;

                    Spec row = GridLayout.spec(i);
                    Spec column = GridLayout.spec(j);
                    GridLayout.LayoutParams lp = new LayoutParams(row, column);
                    /*每个子View的宽高*/
                    lp.width = childWidth;
                    lp.height = childWidth;
                    if ((j + 1) != childRow) {
                        lp.rightMargin = margin;
                    }
                    if (i > 0) {
                        lp.topMargin = margin;
                    }
                    lp.setGravity(Gravity.FILL);
                    addView(child, lp);
                }
            }

            /*生成、显示数字*/
            generateNum();
        }
        isLayout = true;
    }

    private void generateNum() {
        if (isGameOver()) {
            onGame2048Listener.onGameOver();
            return;
        }

        /*初始化,随机产生四个非零子View*/
        if (isFirst) {
            for (int i = 0; i < 4; i++) {
                int randomRow = new Random().nextInt(childRow);
                int randomCol = new Random().nextInt(childRow);
                Game2048Item item = game2048Items[randomRow][randomCol];
                while (item.getNumber() != 0) {
                    randomRow = new Random().nextInt(childRow);
                    randomCol = new Random().nextInt(childRow);
                    item = game2048Items[randomRow][randomCol];
                }
                item.setNumber(Math.random() > 0.75 ? 4 : 2);
                Animation scaleAnimation = new ScaleAnimation(0, 1, 0, 1,
                        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                scaleAnimation.setDuration(200);
                item.startAnimation(scaleAnimation);
                isMoveHappen = isMergeHappen = false;
            }
            isFirst = false;
        }

        if (!isFull()) {
            if (isMoveHappen || isMergeHappen) {
                int randomRow = new Random().nextInt(childRow);
                int randomCol = new Random().nextInt(childRow);
                Game2048Item item = game2048Items[randomRow][randomCol];
                while (item.getNumber() != 0) {
                    randomRow = new Random().nextInt(childRow);
                    randomCol = new Random().nextInt(childRow);
                    item = game2048Items[randomRow][randomCol];
                }
                item.setNumber(Math.random() > 0.75 ? 4 : 2);
                Animation scaleAnimation = new ScaleAnimation(0, 1, 0, 1,
                        Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                scaleAnimation.setDuration(200);
                item.startAnimation(scaleAnimation);
                isMoveHappen = isMergeHappen = false;
            }
        }
    }

    /*触摸事件交由手势监听器处理*/
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gestureDetector.onTouchEvent(event);
        return true;
    }

    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        final int MIN_DISTANCE = 50;

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float x = e2.getX() - e1.getX();
            float y = e2.getY() - e1.getY();
            float absX = Math.abs(x);
            float absY = Math.abs(y);

            if (x > MIN_DISTANCE && absX > absY) {
                action(Action.RIGHT);
            }
            if (x < -MIN_DISTANCE && absX > absY) {
                action(Action.LEFT);
            }
            if (y > MIN_DISTANCE && absY > absX) {
                action(Action.DOWN);
            }
            if (y < -MIN_DISTANCE && absY > absX) {
                action(Action.UP);
            }
            return true;
        }
    }

    private void action(Action action) {
        for (int i = 0; i < childRow; i++) {
            /*用来存储行或列的临时列表*/
            List<Game2048Item> rowList = new ArrayList<>();
            for (int j = 0; j < childRow; j++) {
                int rowIndex = getRowIndexByAction(action, i, j);
                int colIndex = getColIndexByAction(action, i, j);
                Game2048Item item = game2048Items[rowIndex][colIndex];
                /*将非零的子项存入临时列表*/
                if (item.getNumber() != 0) {
                    rowList.add(item);
                }
            }
            for (int j = 0; j < rowList.size(); j++) {
                int rowIndex = getRowIndexByAction(action, i, j);
                int colIndex = getColIndexByAction(action, i, j);
                Game2048Item item = game2048Items[rowIndex][colIndex];
                /*如果临时列表(除去数值为0的项)里的子项与移动前的此列或者行出现不同,说明移动发生*/
                if (item.getNumber() != rowList.get(j).getNumber()) {
                    isMoveHappen = true;
                }
            }

            /*在临时列表里合并相同数字*/
            mergeItem(rowList);

            for (int j = 0; j < childRow; j++) {
                /*依次将临时列表里的数字填入本列/行,剩余直接填0*/
                if (rowList.size() > j) {
                    switch (action) {
                        case LEFT :
                            game2048Items[i][j].setNumber(rowList.get(j).getNumber());
                            break;
                        case RIGHT:
                            game2048Items[i][childRow - 1 - j].setNumber(rowList.get(j).getNumber());
                            break;
                        case UP:
                            game2048Items[j][i].setNumber(rowList.get(j).getNumber());
                            break;
                        case DOWN:
                            game2048Items[childRow - 1 - j][i].setNumber(rowList.get(j).getNumber());
                            break;
                    }
                } else {
                    switch (action) {
                        case LEFT :
                            game2048Items[i][j].setNumber(0);
                            break;
                        case RIGHT:
                            game2048Items[i][childRow - 1 - j].setNumber(0);
                            break;
                        case UP:
                            game2048Items[j][i].setNumber(0);
                            break;
                        case DOWN:
                            game2048Items[childRow - 1 - j][i].setNumber(0);
                            break;
                    }
                }
            }
        }
        generateNum();
    }

    /*根据手势方向确定如何取得子项。例如向上滑动,则从列方向从上到下取得每一个子项在game2048Item里的对应坐标*/
    private int getRowIndexByAction(Action action, int i, int j) {
        int rowIndex = -1;

        switch (action) {
            case LEFT:
            case RIGHT:
                rowIndex = i;
                break;
            case UP:
                rowIndex = j;
                break;
            case DOWN:
                rowIndex = childRow - 1 - j;
                break;
        }
        return rowIndex;

    }

    private int getColIndexByAction(Action action, int i, int j) {
        int colIndex = -1;
        switch (action) {
            case LEFT:
                colIndex = j;
                break;
            case RIGHT:
                colIndex = childRow - 1 - j;
                break;
            case UP:
            case DOWN:
                colIndex = i;
                break;
        }
        return colIndex;
    }

    private void mergeItem(List<Game2048Item> rowList) {
        if (rowList.size() < 2) {
            return;
        }
        for (int i = 0; i < rowList.size() - 1; i++) {
            Game2048Item item1 = rowList.get(i);
            Game2048Item item2 = rowList.get(i + 1);
            if (item1.getNumber() == item2.getNumber()) {
                isMergeHappen = true;
                score += item1.getNumber() * 2;
                item1.setNumber(item1.getNumber() * 2);
                onGame2048Listener.onScoreChange(score);
                for (int j = i + 1; j < rowList.size() - 1; j++) {
                    /*将后一项的数字依次向前移*/
                    rowList.get(j).setNumber(rowList.get(j + 1).getNumber());
                }
                /*最后一项一定设置为0*/
                rowList.get(rowList.size() - 1).setNumber(0);
                return;
            }
        }
    }


    private boolean isGameOver() {
        //*如果格子没有被非零数字填满*//*
        if (!isFull()) {
            return false;
        }
        //*如果填满了,检验是否有相邻子项具有相同数字*//*
        for (int i = 0; i < childRow; i++) {
            for (int j = 0; j < childRow; j++) {
                Game2048Item item = game2048Items[i][j];
                // 如果不是最后一列,则与右边项相比
                if ((j + 1) != childRow) {
                    Game2048Item itemRight = game2048Items[i][j + 1];
                    if (item.getNumber() == itemRight.getNumber())
                        return false;
                }
                // 如果不是最后一行,则与下边项相比
                if ((i + 1)  != childRow) {
                    Log.e("TAG", "DOWN");
                    Game2048Item itemBottom = game2048Items[i + 1][j];
                    if (item.getNumber() == itemBottom.getNumber())
                        return false;
                }
                // 如果不是第一列,则与左边项相比
                if (j != 0) {
                    Log.e("TAG", "LEFT");
                    Game2048Item itemLeft = game2048Items[i][j - 1];
                    if (itemLeft.getNumber() == item.getNumber())
                        return false;
                }
                // 如果不是第一行,则与上边项相比
                if (i != 0) {
                    Log.e("TAG", "UP");
                    Game2048Item itemTop = game2048Items[i - 1][j];
                    if (item.getNumber() == itemTop.getNumber())
                        return false;
                }
            }
        }
        return true;
    }

    public boolean isFull() {
        for (int i = 0; i < childRow; i++) {
            for (int j = 0; j < childRow; j++) {
                Game2048Item game2048Item = game2048Items[i][j];
                if (game2048Item.getNumber() == 0)
                    return false;
            }
        }
        return true;
    }

    /*对外接口,由调用方决定具体逻辑*/
    public interface OnGame2048Listener {
        void onScoreChange(int score);

        void onGameOver();
    }

    public void setOnGame2048Listener(OnGame2048Listener onGame2048Listener) {
        this.onGame2048Listener = onGame2048Listener;
    }

    public void reStart() {
        for (int i = 0; i < childRow; i++) {
            for (int j = 0; j < childRow; j++) {
                Game2048Item item = game2048Items[i][j];
                item.setNumber(0);
            }
        }
        score = 0;
        onGame2048Listener.onScoreChange(0);
        isFirst = true;
        generateNum();
    }
}


其中提供了一个借口用于在activity中调用,让主程序决定要实现的操作


public class MainActivity extends AppCompatActivity implements Game2048Layout.OnGame2048Listener{

    private TextView scoreView;
    Game2048Layout game2048Layout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        game2048Layout = (Game2048Layout) findViewById(R.id.game);
        game2048Layout.setOnGame2048Listener(this);
        scoreView = (TextView) findViewById(R.id.score);
    }

    @Override
    public void onScoreChange(int score) {
        scoreView.setText("Score : "+score);
    }

    @Override
    public void onGameOver() {
        new AlertDialog.Builder(this).setTitle("game Over")
                .setMessage("you have got "+ scoreView.getText()).setCancelable(false)
                .setPositiveButton("Restart", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        game2048Layout.reStart();
                    }
                }).setNegativeButton("Exit", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                finish();
            }
        }).show();
    }
}






评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值