2048游戏实现——GridLayout应用

来源:互联网 发布:蚂蚁宝卡 知乎 编辑:程序博客网 时间:2024/06/07 15:47

学习自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();    }}






0 0
原创粉丝点击