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); } }}
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
- 2048游戏实现——GridLayout应用
- GridLayout—网格布局
- GridLayout——android style
- UI组件——GridLayout
- UI技巧 —— GridLayout & baseline
- Android笔记——GridLayout布局
- Android开发——GridLayout网格布局
- Android布局——GridLayout(网格布局)
- Android 自定义ViewGroup实现GridLayout
- GridLayout
- GridLayout
- gridlayout
- GridLayout
- GridLayout
- GridLayout
- gridlayout
- GridLayout
- GridLayout
- firefox安装和用于调试的工具firebug与模拟ie6,7,8,9,10环境的Ietester
- linux下IPTABLES配置详解
- ARM 关键几个寄存器
- ViewPager 和Fragment的使用
- hdu2548 两军交锋(简单题)
- 2048游戏实现——GridLayout应用
- 朴素贝叶斯
- iOS UIViewAinmation 坑
- HDU 1102.Constructing Roads【最小生成树kruskal算法变形】【3月2】
- OpenSSL “心脏滴血”漏洞
- linux 查看压缩包里文件用什么命令
- 冒泡排序
- flask源码笔记:一,阅读Flask源码前的准备工作
- faac编译