<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:background="#ffffff" android:padding="20dip"> <LinearLayout android:layout_width="match_parent" android:layout_height="100dip" android:orientation="horizontal"> <TextView android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/text_yellow_bg" android:textColor="#ffffff" android:gravity="center" android:text="2048" android:textStyle="bold" android:textSize="30dip"/> <LinearLayout android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:layout_marginLeft="15dip"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="3" android:orientation="vertical" android:background="@drawable/text_grey_bg" android:gravity="center_vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#E3D4C1" android:textSize="16sp" android:text="分数"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#ffffff" android:textSize="20dip" android:text="400"/> </LinearLayout> <TextView android:layout_marginTop="15dip" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="2" android:background="@drawable/text_red_bg" android:textColor="#ffffff" android:textSize="20dip" android:gravity="center" android:text="设置"/> </LinearLayout> <LinearLayout android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical" android:layout_marginLeft="15dip"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="3" android:orientation="vertical" android:background="@drawable/text_grey_bg" android:gravity="center_vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#E3D4C1" android:textSize="16sp" android:text="最高分数"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#ffffff" android:textSize="20dip" android:text="400"/> </LinearLayout> <TextView android:layout_marginTop="15dip" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="2" android:background="@drawable/text_red_bg" android:textColor="#ffffff" android:textSize="20dip" android:gravity="center" android:text="分享"/> </LinearLayout> </LinearLayout> <com.example.my2048.My2048View android:layout_marginTop="20dip" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/my2048view_bg"/> </LinearLayout>
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); this.mViewWidth = w; this.mViewHeight = h; cellSpace = ((float)mViewWidth - (TOTAL_COL + 1) * SPACE) / TOTAL_COL; textPaint.setTextSize(cellSpace / 3); }在View的onSizeChanged的方法中获取View的宽度和高度,并根据宽度计算出每个小格子的长度(宽度和高度),其中TOTAL_COL=4表示四列,SPACE表示间隔宽度。最后一行的textPaint.setTextSize(cellSpace / 3)是设置文字画笔的字体大小(现在不明白没关系,一会就会明白)。相关定义如下:
private static final int TOTAL_ROW = 4; //行 private static final int TOTAL_COL = 4; //列 private static final int SPACE = 15; //行和列之间的间隙 private int mViewWidth; //View的宽度 private int mViewHeight; //View的高度 private float cellSpace; //每个格子的大小下面就开始在onDraw方法中绘制小方格
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); String showNum; for(int i=0; i<TOTAL_ROW; i++){ for(int j=0; j<TOTAL_COL; j++){ pointX = SPACE * (j + 1) + j * cellSpace; pointY = SPACE * (i + 1) + i * cellSpace; //绘制背景 rectf.set(pointX, pointY, pointX + cellSpace, pointY + cellSpace); paint.setColor(Color.rgb(204, 192, 178)); canvas.drawRect(rectf, paint); } } }pintX和pointY是计算绘制方块的起始位置,如下图红点位置分别为方块1、2、3的起始位置。
private int[] colors = { Color.rgb(204, 192, 178), //1 Color.rgb(253, 235, 213), //2 Color.rgb(252, 224, 174), //4 Color.rgb(255, 95, 95), //8 Color.rgb(255, 68, 68), //16 Color.rgb(248, 58, 58), //32 Color.rgb(240, 49, 49), //64 Color.rgb(233, 39, 39), //128 Color.rgb(226, 29, 29), //256 Color.rgb(219, 19, 19), //562 Color.rgb(211, 10, 10), //1024 Color.rgb(204, 0, 0) //2048 };找这么多颜色还真不容易,我就索性将Android的设计规范中红色的后9个颜色取到了这里(这里大家可以自己变成喜欢的颜色)
/** * 模拟测试数据 */ private void initData(){ for(int i=0; i<TOTAL_ROW; i++){ for(int j=0; j<TOTAL_COL; j++){ int a = (i+1) * (j+1); if(a < 12){ datas[i][j] = a; }else{ datas[i][j] = 0; } } } }上面的datas就是我们模拟的整型数据数组,我们再改写onDraw方法将这些数据绘制出来。
private float pointX; private float pointY; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); String showNum; for(int i=0; i<TOTAL_ROW; i++){ for(int j=0; j<TOTAL_COL; j++){ pointX = SPACE * (j + 1) + j * cellSpace; pointY = SPACE * (i + 1) + i * cellSpace; //绘制背景 rectf.set(pointX, pointY, pointX + cellSpace, pointY + cellSpace); paint.setColor(colors[datas[i][j]]); canvas.drawRect(rectf, paint); if(datas[i][j] != 0){ //绘制数字 if(datas[i][j] == 1 || datas[i][j] == 2){ textPaint.setColor(Color.rgb(0, 0, 0)); }else{ textPaint.setColor(Color.rgb(255, 255, 255)); } showNum = (int)Math.pow(2, datas[i][j]) + ""; canvas.drawText(showNum, pointX + (cellSpace - textPaint.measureText(showNum)) / 2, pointY + (cellSpace + textPaint.measureText(showNum, 0, 1)) / 2, textPaint); } } } }上面对随机数字2、4的字体颜色和其他的字体颜色进行了区分,运行效果如下:
/** * 随机的产生1或者2 */ private void randomOneOrTwo(){ int row = random.nextInt(TOTAL_ROW); int col = random.nextInt(TOTAL_COL); //判断在该位置是否已存在数据 if(datas[row][col] != 0){ randomOneOrTwo(); }else{ datas[row][col] = random.nextInt(2) + 1; } }上面代码用到了递归,为什么要使用递归呢?这是因为我们在放置这个随机产生的数字的时候需要随机产生一个x和y方向的坐标值,如果(x,y)坐标处已经存在数据则需要重新获取,直到该(x,y)处没有数据。
private enum Directory{ LEFT, RIGHT, BOTTOM, TOP }
private float mDownX; private float mDownY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = event.getX(); mDownY = event.getY(); return true; case MotionEvent.ACTION_MOVE: float disX = event.getX() - mDownX; float disY = event.getY() - mDownY; if(Math.abs(disX) > touchSlop || Math.abs(disY) > touchSlop){ System.out.println("isMove"); isMoved = true; if(Math.abs(disX) > Math.abs(disY)){ if(disX > 0){ currentDirectory = Directory.RIGHT; }else{ currentDirectory = Directory.LEFT; } }else{ if(disY > 0){ currentDirectory = Directory.BOTTOM; }else{ currentDirectory = Directory.TOP; } } } return true; case MotionEvent.ACTION_UP: if(isMoved == true){ changeState(); randomOneOrTwo(); invalidate(); isMoved = false; } } return super.onTouchEvent(event); }
private void changeState(){ switch (currentDirectory) { case TOP: toTop(); break; case BOTTOM: toBottom(); break; case LEFT: toLeft(); break; case RIGHT: toRight(); break; } }
private void toLeft(){ int temp; //向左移动 for(int i=0; i<TOTAL_ROW; i++){ for(int j=0; j<TOTAL_COL; j++){ for(int k=0; k<TOTAL_COL - j -1; k++){ if(datas[i][k] == 0){ temp = datas[i][k]; datas[i][k] = datas[i][k+1]; datas[i][k+1] = temp; } } } } //合并数字 for(int i=0; i<TOTAL_ROW; i++){ for(int j=0; j<TOTAL_COL; j++){ for(int k=0; k<TOTAL_COL - j -1; k++){ if(datas[i][k] !=0 && datas[i][k] == datas[i][k+1]){ datas[i][k] = datas[i][k] + 1; datas[i][k+1] = 0; } } } } }我在这个方法中共进行了两大步操作,一个是整体向左移动,并将值为0的方格向右移动,第二个操作是合并相邻的相同数字。
/** * 随机的产生1或者2 */ private void randomOneOrTwo() { if(count >= TOTAL_COL * TOTAL_ROW){ currentState = State.FAILL; return; } int row = random.nextInt(TOTAL_ROW); int col = random.nextInt(TOTAL_COL); // 判断在该位置是否已存在数据 if (datas[row][col] != 0) { randomOneOrTwo(); } else { datas[row][col] = random.nextInt(2) + 1; count++; } }将randomOneOrTwo函数修改如上,定义了一个记录当前格子使用情况的变量count,没当增加一个格子后就会增加1.同样在消除格子的时候就要减少1.
// 合并数字 for (int i = 0; i < TOTAL_ROW; i++) { for (int j = 0; j < TOTAL_COL; j++) { for (int k = 0; k < TOTAL_COL - j - 1; k++) { if (datas[i][k] != 0 && datas[i][k] == datas[i][k + 1]) { datas[i][k] = datas[i][k] + 1; datas[i][k + 1] = 0; count--; } } } }并且在状态改变的时候绘制“游戏结束”文字进行提醒
if(currentState == State.FAILL){ textPaint.setColor(Color.rgb(255, 255, 255)); canvas.drawText("游戏结束", (mViewWidth - textPaint.measureText("游戏结束")) / 2, mViewHeight / 2, textPaint); }
case MotionEvent.ACTION_DOWN: mDownX = event.getX(); mDownY = event.getY(); if(currentState == State.FAILL){ if(mDownY < mViewHeight && mDownY > mViewHeight - cellSpace){ currentState = State.RUNNING; initData(); invalidate(); } } return true;
if(currentState == State.FAILL){ rectf.set(0 , mViewHeight - cellSpace, mViewWidth, mViewHeight); paint.setColor(colors[5]); canvas.drawRect(rectf, paint); textPaint.setColor(Color.rgb(255, 255, 255)); canvas.drawText("游戏结束", (mViewWidth - textPaint.measureText("游戏结束")) / 2, mViewHeight / 2, textPaint); canvas.drawText("重新开始", (mViewWidth - textPaint.measureText("游戏结束")) / 2, mViewHeight - textPaint.measureText("游戏结束", 0, 1), textPaint); }
// 合并数字 for (int i = 0; i < TOTAL_ROW; i++) { for (int j = 0; j < TOTAL_COL; j++) { for (int k = 0; k < TOTAL_COL - j - 1; k++) { if (datas[i][k] != 0 && datas[i][k] == datas[i][k + 1]) { datas[i][k] = datas[i][k] + 1; datas[i][k + 1] = 0; score = score + (int)Math.pow(2, datas[i][k]); count--; } } } }
/** * 随机的产生1或者2 */ private void randomOneOrTwo() { if(count >= TOTAL_COL * TOTAL_ROW){ int maxScore = sharedPreference.getInt("maxScore", 0); if(score > maxScore){ Editor edit = sharedPreference.edit(); edit.putInt("maxScore", score); edit.commit(); } gameChangeListener.onChangedGameOver(score, maxScore); currentState = State.FAILL; return; } int row = random.nextInt(TOTAL_ROW); int col = random.nextInt(TOTAL_COL); // 判断在该位置是否已存在数据 if (datas[row][col] != 0) { randomOneOrTwo(); } else { datas[row][col] = random.nextInt(2) + 1; count++; } }上面将最高记录保持在了SharedPreference中,每次结束时和当前分数进行判断,如果游戏过程中突然退出我们也要考虑到记录当前最高记录值,代码如下:
@Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if(visibility != View.VISIBLE){ int maxScore = sharedPreference.getInt("maxScore", 0); if(score > maxScore){ Editor edit = sharedPreference.edit(); edit.putInt("maxScore", score); edit.commit(); } } }那么现在我们如何将这些结果显示到MainActivity的TextView上面呢?我们在My2048View中定义一个接口如下:
public interface GameChangeListener{ public void onChangedGameOver(int score, int maxScore); public void onChangedScore(int score); }并提供接口的注册方法:
public void setOnGameChangeListener(GameChangeListener gameChangeListener){ this.gameChangeListener = gameChangeListener; gameChangeListener.onChangedGameOver(score, sharedPreference.getInt("maxScore", 0)); gameChangeListener.onChangedScore(score); }在我们结束游戏或者消除方块加分的时候进行回调显示,MainActivity中的代码如下:
my2048View = (My2048View) findViewById(R.id.my2048view); my2048View.setOnGameChangeListener(new GameChangeListener() { @Override public void onChangedScore(int score) { scoreText.setText(score + ""); } @Override public void onChangedGameOver(int score, int maxScore) { scoreText.setText(score + ""); maxScoreText.setText(maxScore + ""); } });
是男人就下100层【第五层】——2048游戏,布布扣,bubuko.com
原文:http://blog.csdn.net/dawanganban/article/details/37863693