A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 Android_Robot 于 2016-5-10 14:55 编辑

Android 机器人带你玩转实现游戏2048   其实2048只是个普通的控件

博主本想踏入游戏开放行业,无奈水太深,不会游泳;于是乎,只能继续开发应用,但是原生Android也能开发游戏么,2048、像素鸟、别踩什么来着;今天给大家带来一篇2048的开发篇,别怕不分上下文,或者1、2、3、4,一篇包你能玩happy~虽然我从来没有玩到过2048!!!其实大家也可以当作自定义控件来看~~~

特别说明一下,游戏2048里面的方块各种颜色来源于:http://download.csdn.net/detail/qq1121674367/7155467,这个2048的代码中,其他代码,太多,未参考;特此感谢分享;大家也可以下载下,对比学习下;

接下来贴个我们项目的效果图:

ok 看完效果图,我就准备带领大家征服这款游戏了~~~

实现分析

贴一张静态图,开始对我们游戏的设计:

可以看到,游戏其实就是一个容器,里面很多个方块,触摸容器,里面的方块的形态会发生变化。那么:

1、容器我们准备自定义ViewGroup ,叫做Game2048Layout ; 里面的块块自定义View ,叫做Game2048Item

接下来从简单的开始:

2、Game2048Item

Game2048Item是个View,并且需要哪些属性呢?

首先得有个number,显示数字嘛,然后绘制的时候根据number绘制背景色;还需要呢?嗯,需要正方形边长,再考虑下,这个边长应该Item自己控制么?显然不是的,Game2048Layout 是个n*n的面板,这个n是不确定的,所以Item的边长肯定是Game2048Layout 计算好传入的。这样必须的属性就这两个。

3、Game2048Layout

Game2048Layout是个容器,我们观察下,里面View是个 n*n的排列,我们准备让其继承RelativeLayout ; 这样可以通过设置Item的RIGHT_OF之类的属性进行定位;

我们在onMeasure里面得到Layout的宽和高,然后根据n*n,生成一定数目的Item,为其设置宽和高,放置到Layout中,这样整个游戏的布局就做好了;绘制的细节上:Item间有横向与纵向的间距,所以需要设置这个值,叫做mMargin。然后Item的边长 =  ( Layout边长 – (n-1)*mMagin ) / n ;

剩下的就是onTouchEvent里面去判断用户手势了,然后就行各种逻辑操作了~

代码之旅

首先来看看我们的Game2048Item

1、Game2048Item
  1. package com.zhy.game2048.view;

  2. import android.content.Context;
  3. import android.graphics.Canvas;
  4. import android.graphics.Color;
  5. import android.graphics.Paint;
  6. import android.graphics.Paint.Style;
  7. import android.graphics.Rect;
  8. import android.util.AttributeSet;
  9. import android.util.Log;
  10. import android.view.View;

  11. /**
  12. * 2048的每个Item
  13. *
  14. * @author zhy
  15. *
  16. */
  17. public class Game2048Item extends View
  18. {
  19.         /**
  20.          * 该View上的数字
  21.          */
  22.         private int mNumber;
  23.         private String mNumberVal;
  24.         private Paint mPaint;
  25.         /**
  26.          * 绘制文字的区域
  27.          */
  28.         private Rect mBound;

  29.         public Game2048Item(Context context, AttributeSet attrs, int defStyle)
  30.         {
  31.                 super(context, attrs, defStyle);
  32.                 mPaint = new Paint();

  33.         }

  34.         public Game2048Item(Context context)
  35.         {
  36.                 this(context, null);
  37.         }

  38.         public Game2048Item(Context context, AttributeSet attrs)
  39.         {
  40.                 this(context, attrs, 0);
  41.         }

  42.         public void setNumber(int number)
  43.         {
  44.                 mNumber = number;
  45.                 mNumberVal = mNumber + "";
  46.                 mPaint.setTextSize(30.0f);
  47.                 mBound = new Rect();
  48.                 mPaint.getTextBounds(mNumberVal, 0, mNumberVal.length(), mBound);
  49.                 invalidate();
  50.         }
  51.         
  52.         

  53.         public int getNumber()
  54.         {
  55.                 return mNumber;
  56.         }

  57.         @Override
  58.         protected void onDraw(Canvas canvas)
  59.         {
  60.                
  61.                 super.onDraw(canvas);
  62.                 String mBgColor = "";
  63.                 switch (mNumber)
  64.                 {
  65.                 case 0:
  66.                         mBgColor = "#CCC0B3";
  67.                         break;
  68.                 case 2:
  69.                         mBgColor = "#EEE4DA";
  70.                         break;
  71.                 case 4:
  72.                         mBgColor = "#EDE0C8";
  73.                         break;
  74.                 case 8:
  75.                         mBgColor = "#F2B179";// #F2B179
  76.                         break;
  77.                 case 16:
  78.                         mBgColor = "#F49563";
  79.                         break;
  80.                 case 32:
  81.                         mBgColor = "#F5794D";
  82.                         break;
  83.                 case 64:
  84.                         mBgColor = "#F55D37";
  85.                         break;
  86.                 case 128:
  87.                         mBgColor = "#EEE863";
  88.                         break;
  89.                 case 256:
  90.                         mBgColor = "#EDB04D";
  91.                         break;
  92.                 case 512:
  93.                         mBgColor = "#ECB04D";
  94.                         break;
  95.                 case 1024:
  96.                         mBgColor = "#EB9437";
  97.                         break;
  98.                 case 2048:
  99.                         mBgColor = "#EA7821";
  100.                         break;
  101.                 default:
  102.                         mBgColor = "#EA7821";
  103.                         break;
  104.                 }

  105.                 mPaint.setColor(Color.parseColor(mBgColor));
  106.                 mPaint.setStyle(Style.FILL);
  107.                 canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);

  108.                 if (mNumber != 0)
  109.                         drawText(canvas);

  110.         }

  111.         /**
  112.          * 绘制文字
  113.          *
  114.          * @param canvas
  115.          */
  116.         private void drawText(Canvas canvas)
  117.         {
  118.                
  119.                 mPaint.setColor(Color.BLACK);
  120.                 float x = (getWidth() - mBound.width()) / 2;
  121.                 float y = getHeight() / 2 + mBound.height() / 2;
  122.                 canvas.drawText(mNumberVal, x, y, mPaint);
  123.         }

  124. }
复制代码

很简单,基本就一个onDraw通过number来绘制背景和数字;number通过调用setNumer进行设置;它的宽和高都是固定值,所以我们并不需要自己进行测量~~

2、Game2048Layout

1、成员变量

这就是我们最主要的一个类了,首先我们看看这个类的成员变量,先看看各个成员变量的作用:


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 设置Item的数量n*n;默认为4
  3.          */
  4.         private int mColumn = 4;
  5.         /**
  6.          * 存放所有的Item
  7.          */
  8.         private Game2048Item[] mGame2048Items;

  9.         /**
  10.          * Item横向与纵向的边距
  11.          */
  12.         private int mMargin = 10;
  13.         /**
  14.          * 面板的padding
  15.          */
  16.         private int mPadding;
  17.         /**
  18.          * 检测用户滑动的手势
  19.          */
  20.         private GestureDetector mGestureDetector;

  21.         // 用于确认是否需要生成一个新的值
  22.         private boolean isMergeHappen = true;
  23.         private boolean isMoveHappen = true;

  24.         /**
  25.          * 记录分数
  26.          */
  27.         private int mScore;</font></font>
复制代码


主要的成员变量就这些,直接看注释也比较容易理解~~

了解了成员变量,接下来我们需要在构造方法里面得到一些值和初始化一些变量

2、构造方法


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">public Game2048Layout(Context context, AttributeSet attrs, int defStyle)
  2.         {
  3.                 super(context, attrs, defStyle);

  4.                 mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
  5.                                 mMargin, getResources().getDisplayMetrics());
  6.                 // 设置Layout的内边距,四边一致,设置为四内边距中的最小值
  7.                 mPadding = min(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
  8.                                 getPaddingBottom());

  9.                 mGestureDetector = new GestureDetector(context , new MyGestureDetector());

  10.         }</font></font>
复制代码


我们在构造方法里面得到Item间的边距(margin)和我们容器的内边距(padding,),这个值应该四边一致,于是我们取四边的最小值;这两个属性可以抽取为自定义的属性;然后初始化了我们的mGestureDetector

有了margin和padding,我们就可以计算我们item的边长了。这个计算过程肯定在onMeasure里面,因为我们需要在onMeasure获取容器的宽和高

3、onMeasure


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">private boolean once;

  2.         /**
  3.          * 测量Layout的宽和高,以及设置Item的宽和高,这里忽略wrap_content 以宽、高之中的最小值绘制正方形
  4.          */
  5.         @Override
  6.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  7.         {
  8.                 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  9.                 // 获得正方形的边长
  10.                 int length = Math.min(getMeasuredHeight(), getMeasuredWidth());
  11.                 // 获得Item的宽度
  12.                 int childWidth = (length - mPadding * 2 - mMargin * (mColumn - 1))
  13.                                 / mColumn;

  14.                 if (!once)
  15.                 {
  16.                         if (mGame2048Items == null)
  17.                         {
  18.                                 mGame2048Items = new Game2048Item[mColumn * mColumn];
  19.                         }
  20.                         // 放置Item
  21.                         for (int i = 0; i < mGame2048Items.length; i++)
  22.                         {

  23.                                 Game2048Item item = new Game2048Item(getContext());

  24.                                 mGame2048Items[i] = item;
  25.                                 item.setId(i + 1);
  26.                                 RelativeLayout.LayoutParams lp = new LayoutParams(childWidth,
  27.                                                 childWidth);
  28.                                 // 设置横向边距,不是最后一列
  29.                                 if ((i + 1) % mColumn != 0)
  30.                                 {
  31.                                         lp.rightMargin = mMargin;
  32.                                 }
  33.                                 // 如果不是第一列
  34.                                 if (i % mColumn != 0)
  35.                                 {
  36.                                         lp.addRule(RelativeLayout.RIGHT_OF,//
  37.                                                         mGame2048Items[i - 1].getId());
  38.                                 }
  39.                                 // 如果不是第一行,//设置纵向边距,非最后一行
  40.                                 if ((i + 1) > mColumn)
  41.                                 {
  42.                                         lp.topMargin = mMargin;
  43.                                         lp.addRule(RelativeLayout.BELOW,//
  44.                                                         mGame2048Items[i - mColumn].getId());
  45.                                 }
  46.                                 addView(item, lp);
  47.                         }
  48.                         generateNum();
  49.                 }
  50.                 once = true;

  51.                 setMeasuredDimension(length, length);
  52.         }</font></font>
复制代码

首先设置容器的边长为宽高中的最小值;然后(length – mPadding * 2 – mMargin * (mColumn – 1)) / mColumn ; 去计算Item的边长;

拿到以后,根据我们的mColumn初始化我们的Item数组,然后遍历生成Item,设置Item的LayoutParams以及Rule(RIGHT_OF , BELOW),最后添加到我们的容器中;

最后我们通过setMeasuredDimension(length, length);改变我们布局占据的空间;

到此,我们整个面板绘制完成了;

接下来,就是根据用户的手势,去进行游戏逻辑操作了,手势那么肯定是onTouchEvent了:

4、onTouchEvent


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">@Override
  2.         public boolean onTouchEvent(MotionEvent event)
  3.         {
  4.                 mGestureDetector.onTouchEvent(event);
  5.                 return true;
  6.         }</font></font>
复制代码

我们把触摸事件交给了mGestureDetector,我们去看看我们的mGestureDetector,在构造方法中有这么一句:

mGestureDetector = new GestureDetector(context , new MyGestureDetector());


so,我们需要去看看MyGestureDetector:



  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">class MyGestureDetector extends GestureDetector.SimpleOnGestureListener
  2.         {

  3.                 final int FLING_MIN_DISTANCE = 50;

  4.                 @Override
  5.                 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  6.                                 float velocityY)
  7.                 {
  8.                         float x = e2.getX() - e1.getX();
  9.                         float y = e2.getY() - e1.getY();

  10.                         if (x > FLING_MIN_DISTANCE
  11.                                         && Math.abs(velocityX) > Math.abs(velocityY))
  12.                         {
  13.                                 action(ACTION.RIGHT);

  14.                         } else if (x < -FLING_MIN_DISTANCE
  15.                                         && Math.abs(velocityX) > Math.abs(velocityY))
  16.                         {
  17.                                 action(ACTION.LEFT);

  18.                         } else if (y > FLING_MIN_DISTANCE
  19.                                         && Math.abs(velocityX) < Math.abs(velocityY))
  20.                         {
  21.                                 action(ACTION.DOWM);

  22.                         } else if (y < -FLING_MIN_DISTANCE
  23.                                         && Math.abs(velocityX) < Math.abs(velocityY))
  24.                         {
  25.                                 action(ACTION.UP);
  26.                         }
  27.                         return true;

  28.                 }

  29.         }</font></font>
复制代码

很简单,就是判读用户上、下、左、右滑动;然后去调用action(ACTION)方法;ACTION是个枚举:


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 运动方向的枚举
  3.          *
  4.          * @author zhy
  5.          *
  6.          */
  7.         private enum ACTION
  8.         {
  9.                 LEFT, RIGHT, UP, DOWM
  10.         }</font></font>
复制代码

这么看,核心代码都在action方法里面了:

5、根据用户手势重绘Item

看代码前,先考虑下,用户从右向左滑动时,面板应该如何变化;取其中一行,可能性为:

0 0 0 2 -> 2 0 0 0

2 0 4 0 -> 2 4 0 0

2 2 4 0 -> 4 4 0 0

大概就这3中可能;

我们算法是这么做的:

拿2 2 4 0 来说:

1、首先把每行有数字的取出来,临时存储下来,即[ 2, 2, 4 ];

2、然后遍历合并第一个相遇的相同的,即[ 4, 4 ,0 ]

3、然后直接放置到原行,不足补0,即[ 4, 4, 0 ,0 ];

中间还有几个操作:

1、生成一个新的数字,游戏在每次用户滑动时,可能会生成一个数字;我们的生成策略:如果发生移动或者合并,则生成一个数字;

移动的判断,拿原数据,即【 2 ,2,4,0】和我们第一步临时存储的做比较,一一对比(遍历临时表),发现不同,则认为移动了;

合并的判断,在合并的时候会设置合并的标志位为true;

2、加分,如果发生合并,则加分,分值为合并得到的数字,比如 4,4 -> 8 ,即加8分 ; 也只需要在合并的时候进行相加就行了;

介绍完了,来看我们的代码:


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 根据用户运动,整体进行移动合并值等
  3.          */
  4.         private void action(ACTION action)
  5.         {
  6.                 // 行|列
  7.                 for (int i = 0; i < mColumn; i++)
  8.                 {
  9.                         List<Game2048Item> row = new ArrayList<Game2048Item>();
  10.                         // 行|列
  11.                         for (int j = 0; j < mColumn; j++)
  12.                         {
  13.                                 // 得到下标
  14.                                 int index = getIndexByAction(action, i, j);

  15.                                 Game2048Item item = mGame2048Items[index];
  16.                                 // 记录不为0的数字
  17.                                 if (item.getNumber() != 0)
  18.                                 {
  19.                                         row.add(item);
  20.                                 }
  21.                         }
  22.                         
  23.                         for (int j = 0; j < mColumn && j < row.size(); j++)
  24.                         {
  25.                                 int index = getIndexByAction(action, i, j);
  26.                                 Game2048Item item = mGame2048Items[index];

  27.                                 if (item.getNumber() != row.get(j).getNumber())
  28.                                 {
  29.                                         isMoveHappen = true;
  30.                                 }
  31.                         }
  32.                         
  33.                         // 合并相同的
  34.                         mergeItem(row);
  35.                         
  36.                         
  37.                         // 设置合并后的值
  38.                         for (int j = 0; j < mColumn; j++)
  39.                         {
  40.                                 int index = getIndexByAction(action, i, j);
  41.                                 if (row.size() > j)
  42.                                 {
  43.                                         mGame2048Items[index].setNumber(row.get(j).getNumber());
  44.                                 } else
  45.                                 {
  46.                                         mGame2048Items[index].setNumber(0);
  47.                                 }
  48.                         }

  49.                 }
  50.                 generateNum();

  51.         }</font></font>
复制代码

大体上是两层循环,外层循环代码循环次数,内层有3个for循环;

第一个for循环,对应上述:首先把每行有数字的取出来,临时存储下来,即[ 2, 2, 4 ];

第二个for循环,判断是否发生移动;

// 合并相同的
mergeItem(row); 是去进行合并操作,对应上述:然后遍历合并第一个相遇的相同的,即[ 4, 4 ,0 ];以及加分和设置合并标志位都在方法中;

第三个for循环:设置合并后的值,对应上述:然后直接放置到原行,不足补0,即[ 4, 4, 0 ,0 ];

最后生成数字,方法内部会进行判断游戏是否结束,是否需要生成数字;

那么先看mergeItem的代码:


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 合并相同的Item
  3.          *
  4.          * @param row
  5.          */
  6.         private void mergeItem(List<Game2048Item> row)
  7.         {
  8.                 if (row.size() < 2)
  9.                         return;

  10.                 for (int j = 0; j < row.size() - 1; j++)
  11.                 {
  12.                         Game2048Item item1 = row.get(j);
  13.                         Game2048Item item2 = row.get(j + 1);

  14.                         if (item1.getNumber() == item2.getNumber())
  15.                         {
  16.                                 isMergeHappen = true;

  17.                                 int val = item1.getNumber() + item2.getNumber();
  18.                                 item1.setNumber(val);

  19.                                 // 加分
  20.                                 mScore += val;
  21.                                 if (mGame2048Listener != null)
  22.                                 {
  23.                                         mGame2048Listener.onScoreChange(mScore);
  24.                                 }

  25.                                 // 向前移动
  26.                                 for (int k = j + 1; k < row.size() - 1; k++)
  27.                                 {
  28.                                         row.get(k).setNumber(row.get(k + 1).getNumber());
  29.                                 }
  30.                                 
  31.                                 row.get(row.size() - 1).setNumber(0);
  32.                                 return;
  33.                         }

  34.                 }

  35.         }</font></font>
复制代码

也比较简单,循环查找相同的number,发现合并数字,加分;

加分我们设置了一个回调,把分数回调出去:


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">if (mGame2048Listener != null)
  2. {
  3. mGame2048Listener.onScoreChange(mScore);
  4. }</font></font>
复制代码
最后看我们生成数字的代码:

  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 产生一个数字
  3.          */
  4.         public void generateNum()
  5.         {

  6.                 if (checkOver())
  7.                 {
  8.                         Log.e("TAG", "GAME OVER");
  9.                         if (mGame2048Listener != null)
  10.                         {
  11.                                 mGame2048Listener.onGameOver();
  12.                         }
  13.                         return;
  14.                 }

  15.                 if (!isFull())
  16.                 {
  17.                         if (isMoveHappen || isMergeHappen)
  18.                         {
  19.                                 Random random = new Random();
  20.                                 int next = random.nextInt(16);
  21.                                 Game2048Item item = mGame2048Items[next];

  22.                                 while (item.getNumber() != 0)
  23.                                 {
  24.                                         next = random.nextInt(16);
  25.                                         item = mGame2048Items[next];
  26.                                 }

  27.                                 item.setNumber(Math.random() > 0.75 ? 4 : 2);

  28.                                 isMergeHappen = isMoveHappen = false;
  29.                         }

  30.                 }
  31.         }</font></font>
复制代码

首先判断是否结束,如果结束了,依然是回调出去,得让玩的人知道结束了;

然后判断当然面板是有木有空的格子,如果没有,在判断需要生成新的数字么,需要则随机生成一个新的2或4;

那么如何判断是否结束呢?

首先肯定是没有空格了,然后四个方向上没有相同的数字就结束了:


  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 检测当前所有的位置都有数字,且相邻的没有相同的数字
  3.          *
  4.          * @return
  5.          */
  6.         private boolean checkOver()
  7.         {
  8.                 // 检测是否所有位置都有数字
  9.                 if (!isFull())
  10.                 {
  11.                         return false;
  12.                 }

  13.                 for (int i = 0; i < mColumn; i++)
  14.                 {
  15.                         for (int j = 0; j < mColumn; j++)
  16.                         {

  17.                                 int index = i * mColumn + j;

  18.                                 // 当前的Item
  19.                                 Game2048Item item = mGame2048Items[index];
  20.                                 // 右边
  21.                                 if ((index + 1) % mColumn != 0)
  22.                                 {
  23.                                         Log.e("TAG", "RIGHT");
  24.                                         // 右边的Item
  25.                                         Game2048Item itemRight = mGame2048Items[index + 1];
  26.                                         if (item.getNumber() == itemRight.getNumber())
  27.                                                 return false;
  28.                                 }
  29.                                 // 下边
  30.                                 if ((index + mColumn) < mColumn * mColumn)
  31.                                 {
  32.                                         Log.e("TAG", "DOWN");
  33.                                         Game2048Item itemBottom = mGame2048Items[index + mColumn];
  34.                                         if (item.getNumber() == itemBottom.getNumber())
  35.                                                 return false;
  36.                                 }
  37.                                 // 左边
  38.                                 if (index % mColumn != 0)
  39.                                 {
  40.                                         Log.e("TAG", "LEFT");
  41.                                         Game2048Item itemLeft = mGame2048Items[index - 1];
  42.                                         if (itemLeft.getNumber() == item.getNumber())
  43.                                                 return false;
  44.                                 }
  45.                                 // 上边
  46.                                 if (index + 1 > mColumn)
  47.                                 {
  48.                                         Log.e("TAG", "UP");
  49.                                         Game2048Item itemTop = mGame2048Items[index - mColumn];
  50.                                         if (item.getNumber() == itemTop.getNumber())
  51.                                                 return false;
  52.                                 }

  53.                         }

  54.                 }

  55.                 return true;

  56.         }</font></font>
复制代码
  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px">/**
  2.          * 是否填满数字
  3.          *
  4.          * @return
  5.          */
  6.         private boolean isFull()
  7.         {
  8.                 // 检测是否所有位置都有数字
  9.                 for (int i = 0; i < mGame2048Items.length; i++)
  10.                 {
  11.                         if (mGame2048Items[i].getNumber() == 0)
  12.                         {
  13.                                 return false;
  14.                         }
  15.                 }
  16.                 return true;
  17.         }</font></font>
复制代码

到此,我们的代码介绍完毕~~~完成了我们的Game2048Layout ; 接下来看如何使用呢?

写游戏的过程很艰辛,但是用起来,看看什么叫so easy ; 当成普通的View即可:

实践1、布局文件:
  1. <font color="rgb(46, 46, 46)"><font style="font-size: 15px"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.     xmlns:tools="http://schemas.android.com/tools"
  3.     android:layout_width="fill_parent"
  4.     android:layout_height="fill_parent" >

  5.     <com.zhy.game2048.view.Game2048Layout
  6.         android:id="@+id/id_game2048"
  7.         android:layout_width="fill_parent"
  8.         android:layout_height="fill_parent"
  9.         android:layout_centerInParent="true"
  10.         android:background="#ffffff"
  11.         android:padding="10dp" >
  12.     </com.zhy.game2048.view.Game2048Layout>

  13.     <LinearLayout
  14.         android:layout_width="wrap_content"
  15.         android:layout_height="wrap_content"
  16.         android:layout_above="@id/id_game2048"
  17.         android:layout_centerHorizontal="true"
  18.         android:layout_marginBottom="20dp"
  19.         android:background="#EEE4DA"
  20.         android:orientation="horizontal" >

  21.         <TextView
  22.             android:id="@+id/id_score"
  23.             android:layout_width="wrap_content"
  24.             android:layout_height="wrap_content"
  25.             android:padding="4dp"
  26.             android:text="Score: 0"
  27.             android:textColor="#EA7821"
  28.             android:textSize="30sp"
  29.             android:textStyle="bold" />
  30.     </LinearLayout>

  31. </RelativeLayout></font></font>
复制代码

2、MainActivity
  1. package com.zhy.game2048;

  2. import android.app.Activity;
  3. import android.app.AlertDialog;
  4. import android.content.DialogInterface;
  5. import android.content.DialogInterface.OnClickListener;
  6. import android.os.Bundle;
  7. import android.widget.TextView;

  8. import com.zhy.game2048.view.Game2048Layout;
  9. import com.zhy.game2048.view.Game2048Layout.OnGame2048Listener;

  10. public class MainActivity extends Activity implements OnGame2048Listener
  11. {
  12.         private Game2048Layout mGame2048Layout;

  13.         private TextView mScore;

  14.         @Override
  15.         protected void onCreate(Bundle savedInstanceState)
  16.         {
  17.                 super.onCreate(savedInstanceState);
  18.                 setContentView(R.layout.activity_main);

  19.                 mScore = (TextView) findViewById(R.id.id_score);
  20.                 mGame2048Layout = (Game2048Layout) findViewById(R.id.id_game2048);
  21.                 mGame2048Layout.setOnGame2048Listener(this);
  22.         }

  23.         @Override
  24.         public void onScoreChange(int score)
  25.         {
  26.                 mScore.setText("SCORE: " + score);
  27.         }

  28.         @Override
  29.         public void onGameOver()
  30.         {
  31.                 new AlertDialog.Builder(this).setTitle("GAME OVER")
  32.                                 .setMessage("YOU HAVE GOT " + mScore.getText())
  33.                                 .setPositiveButton("RESTART", new OnClickListener()
  34.                                 {
  35.                                         @Override
  36.                                         public void onClick(DialogInterface dialog, int which)
  37.                                         {
  38.                                                 mGame2048Layout.restart();
  39.                                         }
  40.                                 }).setNegativeButton("EXIT", new OnClickListener()
  41.                                 {

  42.                                         @Override
  43.                                         public void onClick(DialogInterface dialog, int which)
  44.                                         {
  45.                                                 finish();
  46.                                         }
  47.                                 }).show();
  48.         }

  49. }
复制代码

很简单,代码主要就是设置个接口,当发生加分已经游戏结束时会交给Activity去处理~~~如果喜欢,你可以在一个界面放4个游戏~~~

当然了游戏Item的个数也可以动态设置~~~最后贴一个5*5游戏的截图~~


好了,2048到此结束,拿只笔开始设计,然后根据自定义View的经验去写,相信你可以学会不少东西~~~

并且我们的View是抽取出来的,其实换成图片也很简单~~

今天又看了war3十大经典战役,献上war3版,代码就不贴了,改动也就几行代码,贴个截图,纪念我们曾经的war3~~~:

额,咋都弄成5*5了~大家可以把mColumn改为4~~~


2048完整源码下载地址【回帖可见】:
war3版2048下载地址【回帖可见】:

其他资源分享:

新手连载】二:MAC下AndroidStudio开发环境搭建

【新手连载】三:ubuntu下android开发环境的搭建(as)文字版
【新手连载】四:ubuntu下android开发环境的搭建(as)图文版
AndroidStudio签名打包



6 个回复

倒序浏览
感谢分享!!!!
回复 使用道具 举报
学习是万万万岁的
回复 使用道具 举报
感谢楼主 感谢黑马
回复 使用道具 举报
谢谢分享!
回复 使用道具 举报
酷酷酷kkk
回复 使用道具 举报
多谢分享
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马