转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40020137,本文出自:【张鸿洋的博客】

1、概述

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

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

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

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

2、实现分析

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

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

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里面去判断用户手势了,然后就行各种逻辑操作了~

3、代码之旅

首先来看看我们的Game2048Item

1、Game2048Item

  1. package com.zhy.game2048.view;
  2.  
  3. import android.content.Context;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Paint;
  7. import android.graphics.Paint.Style;
  8. import android.graphics.Rect;
  9. import android.util.AttributeSet;
  10. import android.util.Log;
  11. import android.view.View;
  12.  
  13. /**
  14. * 2048的每个Item
  15. *
  16. * @author zhy
  17. *
  18. */
  19. public class Game2048Item extends View
  20. {
  21. /**
  22. * 该View上的数字
  23. */
  24. private int mNumber;
  25. private String mNumberVal;
  26. private Paint mPaint;
  27. /**
  28. * 绘制文字的区域
  29. */
  30. private Rect mBound;
  31.  
  32. public Game2048Item(Context context, AttributeSet attrs, int defStyle)
  33. {
  34. super(context, attrs, defStyle);
  35. mPaint = new Paint();
  36.  
  37. }
  38.  
  39. public Game2048Item(Context context)
  40. {
  41. this(context, null);
  42. }
  43.  
  44. public Game2048Item(Context context, AttributeSet attrs)
  45. {
  46. this(context, attrs, 0);
  47. }
  48.  
  49. public void setNumber(int number)
  50. {
  51. mNumber = number;
  52. mNumberVal = mNumber + "";
  53. mPaint.setTextSize(30.0f);
  54. mBound = new Rect();
  55. mPaint.getTextBounds(mNumberVal, 0, mNumberVal.length(), mBound);
  56. invalidate();
  57. }
  58.  
  59. public int getNumber()
  60. {
  61. return mNumber;
  62. }
  63.  
  64. @Override
  65. protected void onDraw(Canvas canvas)
  66. {
  67.  
  68. super.onDraw(canvas);
  69. String mBgColor = "";
  70. switch (mNumber)
  71. {
  72. case 0:
  73. mBgColor = "#CCC0B3";
  74. break;
  75. case 2:
  76. mBgColor = "#EEE4DA";
  77. break;
  78. case 4:
  79. mBgColor = "#EDE0C8";
  80. break;
  81. case 8:
  82. mBgColor = "#F2B179";// #F2B179
  83. break;
  84. case 16:
  85. mBgColor = "#F49563";
  86. break;
  87. case 32:
  88. mBgColor = "#F5794D";
  89. break;
  90. case 64:
  91. mBgColor = "#F55D37";
  92. break;
  93. case 128:
  94. mBgColor = "#EEE863";
  95. break;
  96. case 256:
  97. mBgColor = "#EDB04D";
  98. break;
  99. case 512:
  100. mBgColor = "#ECB04D";
  101. break;
  102. case 1024:
  103. mBgColor = "#EB9437";
  104. break;
  105. case 2048:
  106. mBgColor = "#EA7821";
  107. break;
  108. default:
  109. mBgColor = "#EA7821";
  110. break;
  111. }
  112.  
  113. mPaint.setColor(Color.parseColor(mBgColor));
  114. mPaint.setStyle(Style.FILL);
  115. canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
  116.  
  117. if (mNumber != 0)
  118. drawText(canvas);
  119.  
  120. }
  121.  
  122. /**
  123. * 绘制文字
  124. *
  125. * @param canvas
  126. */
  127. private void drawText(Canvas canvas)
  128. {
  129.  
  130. mPaint.setColor(Color.BLACK);
  131. float x = (getWidth() - mBound.width()) / 2;
  132. float y = getHeight() / 2 + mBound.height() / 2;
  133. canvas.drawText(mNumberVal, x, y, mPaint);
  134. }
  135.  
  136. }

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

2、Game2048Layout

1、成员变量

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

  1. /**
  2. * 设置Item的数量n*n;默认为4
  3. */
  4. private int mColumn = 4;
  5. /**
  6. * 存放所有的Item
  7. */
  8. private Game2048Item[] mGame2048Items;
  9.  
  10. /**
  11. * Item横向与纵向的边距
  12. */
  13. private int mMargin = 10;
  14. /**
  15. * 面板的padding
  16. */
  17. private int mPadding;
  18. /**
  19. * 检测用户滑动的手势
  20. */
  21. private GestureDetector mGestureDetector;
  22.  
  23. // 用于确认是否需要生成一个新的值
  24. private boolean isMergeHappen = true;
  25. private boolean isMoveHappen = true;
  26.  
  27. /**
  28. * 记录分数
  29. */
  30. private int mScore;

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

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

2、构造方法

  1. public Game2048Layout(Context context, AttributeSet attrs, int defStyle)
  2. {
  3. super(context, attrs, defStyle);
  4.  
  5. mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
  6. mMargin, getResources().getDisplayMetrics());
  7. // 设置Layout的内边距,四边一致,设置为四内边距中的最小值
  8. mPadding = min(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
  9. getPaddingBottom());
  10.  
  11. mGestureDetector = new GestureDetector(context , new MyGestureDetector());
  12.  
  13. }

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

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

3、onMeasure

  1. private boolean once;
  2.  
  3. /**
  4. * 测量Layout的宽和高,以及设置Item的宽和高,这里忽略wrap_content 以宽、高之中的最小值绘制正方形
  5. */
  6. @Override
  7. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  8. {
  9. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  10. // 获得正方形的边长
  11. int length = Math.min(getMeasuredHeight(), getMeasuredWidth());
  12. // 获得Item的宽度
  13. int childWidth = (length - mPadding * 2 - mMargin * (mColumn - 1))
  14. / mColumn;
  15.  
  16. if (!once)
  17. {
  18. if (mGame2048Items == null)
  19. {
  20. mGame2048Items = new Game2048Item[mColumn * mColumn];
  21. }
  22. // 放置Item
  23. for (int i = 0; i < mGame2048Items.length; i++)
  24. {
  25.  
  26. Game2048Item item = new Game2048Item(getContext());
  27.  
  28. mGame2048Items[i] = item;
  29. item.setId(i + 1);
  30. RelativeLayout.LayoutParams lp = new LayoutParams(childWidth,
  31. childWidth);
  32. // 设置横向边距,不是最后一列
  33. if ((i + 1) % mColumn != 0)
  34. {
  35. lp.rightMargin = mMargin;
  36. }
  37. // 如果不是第一列
  38. if (i % mColumn != 0)
  39. {
  40. lp.addRule(RelativeLayout.RIGHT_OF,//
  41. mGame2048Items[i - 1].getId());
  42. }
  43. // 如果不是第一行,//设置纵向边距,非最后一行
  44. if ((i + 1) > mColumn)
  45. {
  46. lp.topMargin = mMargin;
  47. lp.addRule(RelativeLayout.BELOW,//
  48. mGame2048Items[i - mColumn].getId());
  49. }
  50. addView(item, lp);
  51. }
  52. generateNum();
  53. }
  54. once = true;
  55.  
  56. setMeasuredDimension(length, length);
  57. }

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

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

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

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

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

4、onTouchEvent

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event)
  3. {
  4. mGestureDetector.onTouchEvent(event);
  5. return true;
  6. }

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

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

so,我们需要去看看MyGestureDetector:

  1. class MyGestureDetector extends GestureDetector.SimpleOnGestureListener
  2. {
  3.  
  4. final int FLING_MIN_DISTANCE = 50;
  5.  
  6. @Override
  7. public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
  8. float velocityY)
  9. {
  10. float x = e2.getX() - e1.getX();
  11. float y = e2.getY() - e1.getY();
  12.  
  13. if (x > FLING_MIN_DISTANCE
  14. && Math.abs(velocityX) > Math.abs(velocityY))
  15. {
  16. action(ACTION.RIGHT);
  17.  
  18. } else if (x < -FLING_MIN_DISTANCE
  19. && Math.abs(velocityX) > Math.abs(velocityY))
  20. {
  21. action(ACTION.LEFT);
  22.  
  23. } else if (y > FLING_MIN_DISTANCE
  24. && Math.abs(velocityX) < Math.abs(velocityY))
  25. {
  26. action(ACTION.DOWM);
  27.  
  28. } else if (y < -FLING_MIN_DISTANCE
  29. && Math.abs(velocityX) < Math.abs(velocityY))
  30. {
  31. action(ACTION.UP);
  32. }
  33. return true;
  34.  
  35. }
  36.  
  37. }

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

  1. /**
  2. * 运动方向的枚举
  3. *
  4. * @author zhy
  5. *
  6. */
  7. private enum ACTION
  8. {
  9. LEFT, RIGHT, UP, DOWM
  10. }

这么看,核心代码都在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. /**
  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.  
  16. Game2048Item item = mGame2048Items[index];
  17. // 记录不为0的数字
  18. if (item.getNumber() != 0)
  19. {
  20. row.add(item);
  21. }
  22. }
  23.  
  24. for (int j = 0; j < mColumn && j < row.size(); j++)
  25. {
  26. int index = getIndexByAction(action, i, j);
  27. Game2048Item item = mGame2048Items[index];
  28.  
  29. if (item.getNumber() != row.get(j).getNumber())
  30. {
  31. isMoveHappen = true;
  32. }
  33. }
  34.  
  35. // 合并相同的
  36. mergeItem(row);
  37.  
  38. // 设置合并后的值
  39. for (int j = 0; j < mColumn; j++)
  40. {
  41. int index = getIndexByAction(action, i, j);
  42. if (row.size() > j)
  43. {
  44. mGame2048Items[index].setNumber(row.get(j).getNumber());
  45. } else
  46. {
  47. mGame2048Items[index].setNumber(0);
  48. }
  49. }
  50.  
  51. }
  52. generateNum();
  53.  
  54. }

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

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

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

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

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

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

那么先看mergeItem的代码:

  1. /**
  2. * 合并相同的Item
  3. *
  4. * @param row
  5. */
  6. private void mergeItem(List<Game2048Item> row)
  7. {
  8. if (row.size() < 2)
  9. return;
  10.  
  11. for (int j = 0; j < row.size() - 1; j++)
  12. {
  13. Game2048Item item1 = row.get(j);
  14. Game2048Item item2 = row.get(j + 1);
  15.  
  16. if (item1.getNumber() == item2.getNumber())
  17. {
  18. isMergeHappen = true;
  19.  
  20. int val = item1.getNumber() + item2.getNumber();
  21. item1.setNumber(val);
  22.  
  23. // 加分
  24. mScore += val;
  25. if (mGame2048Listener != null)
  26. {
  27. mGame2048Listener.onScoreChange(mScore);
  28. }
  29.  
  30. // 向前移动
  31. for (int k = j + 1; k < row.size() - 1; k++)
  32. {
  33. row.get(k).setNumber(row.get(k + 1).getNumber());
  34. }
  35.  
  36. row.get(row.size() - 1).setNumber(0);
  37. return;
  38. }
  39.  
  40. }
  41.  
  42. }

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

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

if (mGame2048Listener != null)
{
mGame2048Listener.onScoreChange(mScore);
}

最后看我们生成数字的代码:

  1. /**
  2. * 产生一个数字
  3. */
  4. public void generateNum()
  5. {
  6.  
  7. if (checkOver())
  8. {
  9. Log.e("TAG", "GAME OVER");
  10. if (mGame2048Listener != null)
  11. {
  12. mGame2048Listener.onGameOver();
  13. }
  14. return;
  15. }
  16.  
  17. if (!isFull())
  18. {
  19. if (isMoveHappen || isMergeHappen)
  20. {
  21. Random random = new Random();
  22. int next = random.nextInt(16);
  23. Game2048Item item = mGame2048Items[next];
  24.  
  25. while (item.getNumber() != 0)
  26. {
  27. next = random.nextInt(16);
  28. item = mGame2048Items[next];
  29. }
  30.  
  31. item.setNumber(Math.random() > 0.75 ? 4 : 2);
  32.  
  33. isMergeHappen = isMoveHappen = false;
  34. }
  35.  
  36. }
  37. }

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

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

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

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

  1. /**
  2. * 检测当前所有的位置都有数字,且相邻的没有相同的数字
  3. *
  4. * @return
  5. */
  6. private boolean checkOver()
  7. {
  8. // 检测是否所有位置都有数字
  9. if (!isFull())
  10. {
  11. return false;
  12. }
  13.  
  14. for (int i = 0; i < mColumn; i++)
  15. {
  16. for (int j = 0; j < mColumn; j++)
  17. {
  18.  
  19. int index = i * mColumn + j;
  20.  
  21. // 当前的Item
  22. Game2048Item item = mGame2048Items[index];
  23. // 右边
  24. if ((index + 1) % mColumn != 0)
  25. {
  26. Log.e("TAG", "RIGHT");
  27. // 右边的Item
  28. Game2048Item itemRight = mGame2048Items[index + 1];
  29. if (item.getNumber() == itemRight.getNumber())
  30. return false;
  31. }
  32. // 下边
  33. if ((index + mColumn) < mColumn * mColumn)
  34. {
  35. Log.e("TAG", "DOWN");
  36. Game2048Item itemBottom = mGame2048Items[index + mColumn];
  37. if (item.getNumber() == itemBottom.getNumber())
  38. return false;
  39. }
  40. // 左边
  41. if (index % mColumn != 0)
  42. {
  43. Log.e("TAG", "LEFT");
  44. Game2048Item itemLeft = mGame2048Items[index - 1];
  45. if (itemLeft.getNumber() == item.getNumber())
  46. return false;
  47. }
  48. // 上边
  49. if (index + 1 > mColumn)
  50. {
  51. Log.e("TAG", "UP");
  52. Game2048Item itemTop = mGame2048Items[index - mColumn];
  53. if (item.getNumber() == itemTop.getNumber())
  54. return false;
  55. }
  56.  
  57. }
  58.  
  59. }
  60.  
  61. return true;
  62.  
  63. }

  1. /**
  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. }

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

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

4、实践

1、布局文件:

  1. <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.  
  6. <com.zhy.game2048.view.Game2048Layout
  7. android:id="@+id/id_game2048"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. android:layout_centerInParent="true"
  11. android:background="#ffffff"
  12. android:padding="10dp" >
  13. </com.zhy.game2048.view.Game2048Layout>
  14.  
  15. <LinearLayout
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:layout_above="@id/id_game2048"
  19. android:layout_centerHorizontal="true"
  20. android:layout_marginBottom="20dp"
  21. android:background="#EEE4DA"
  22. android:orientation="horizontal" >
  23.  
  24. <TextView
  25. android:id="@+id/id_score"
  26. android:layout_width="wrap_content"
  27. android:layout_height="wrap_content"
  28. android:padding="4dp"
  29. android:text="Score: 0"
  30. android:textColor="#EA7821"
  31. android:textSize="30sp"
  32. android:textStyle="bold" />
  33. </LinearLayout>
  34.  
  35. </RelativeLayout>

2、MainActivity

  1. package com.zhy.game2048;
  2.  
  3. import android.app.Activity;
  4. import android.app.AlertDialog;
  5. import android.content.DialogInterface;
  6. import android.content.DialogInterface.OnClickListener;
  7. import android.os.Bundle;
  8. import android.widget.TextView;
  9.  
  10. import com.zhy.game2048.view.Game2048Layout;
  11. import com.zhy.game2048.view.Game2048Layout.OnGame2048Listener;
  12.  
  13. public class MainActivity extends Activity implements OnGame2048Listener
  14. {
  15. private Game2048Layout mGame2048Layout;
  16.  
  17. private TextView mScore;
  18.  
  19. @Override
  20. protected void onCreate(Bundle savedInstanceState)
  21. {
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_main);
  24.  
  25. mScore = (TextView) findViewById(R.id.id_score);
  26. mGame2048Layout = (Game2048Layout) findViewById(R.id.id_game2048);
  27. mGame2048Layout.setOnGame2048Listener(this);
  28. }
  29.  
  30. @Override
  31. public void onScoreChange(int score)
  32. {
  33. mScore.setText("SCORE: " + score);
  34. }
  35.  
  36. @Override
  37. public void onGameOver()
  38. {
  39. new AlertDialog.Builder(this).setTitle("GAME OVER")
  40. .setMessage("YOU HAVE GOT " + mScore.getText())
  41. .setPositiveButton("RESTART", new OnClickListener()
  42. {
  43. @Override
  44. public void onClick(DialogInterface dialog, int which)
  45. {
  46. mGame2048Layout.restart();
  47. }
  48. }).setNegativeButton("EXIT", new OnClickListener()
  49. {
  50.  
  51. @Override
  52. public void onClick(DialogInterface dialog, int which)
  53. {
  54. finish();
  55. }
  56. }).show();
  57. }
  58.  
  59. }

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

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

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

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

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

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

2048源码点击下载

war3版2048点击下载

---------------------------------------------------------------------------------------------------------

我建了一个QQ群,方便大家交流。群号:55032675



----------------------------------------------------------------------------------------------------------

博主部分视频已经上线,如果你不喜欢枯燥的文本,请猛戳(初录,期待您的支持):

1、高仿微信5.2.1主界面及消息提醒

2、高仿QQ5.0侧滑

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android 带你玩转实现游戏2048 其实2048只是个普通的控件的更多相关文章

  1. Android 带你玩转实现游戏2048 其实2048只是个普通的控件(转)

    1.概述 博主本想踏入游戏开放行业,无奈水太深,不会游泳:于是乎,只能继续开发应用,但是原生Android也能开发游戏么,2048.像素鸟.别踩什么来着:今天给大家带来一篇2048的开发篇,别怕不分上 ...

  2. android多框架实现短视频应用、3D手势旋转、banner控件、指南针、智能管家等应用源码

    Android精选源码 android智能管家app源码 Android高仿拼多多分类列表 Android百度地图实例详解之仿摩拜单车APP RecyclerView的LayoutManager搭建流 ...

  3. Android Material Design(一)史上最全的材料设计控件大全

    主要内容: 本文将要介绍Material design和Support library控件,主要包括TextInputLayout.SwitchCompat.SnackBar.FloatingActi ...

  4. Android动画效果 translate、scale、alpha、rotate 切换Activity动画 控件位置调整

    2011.10.28注:如果需要控件停在动画后的位置,需要设置android:fillAfter属性为true,在set节点中.默认在动画结束后回到动画前位置.设置android:fillAfter后 ...

  5. 【开源java游戏框架libgdx专题】-14-系统控件-Skin类

    Skin类主要用于存储用户界面的资源,该资源主要用于窗口部件.这些资源也包括纹理图片.位图画笔.颜色等内容.方便创建游戏组件,同时使用Skin也可以批量的粗略处理一些窗口部件. test.json { ...

  6. 【开源java游戏框架libgdx专题】-15-系统控件-Button类

    Button类继承与Actor类,可以在舞台中使用,但是它也继承了许多Actor的子类,如Group.Table.WidgetGroup灯. 常用构造方法: Button():创建按钮对象,不设置其样 ...

  7. 【Android Studio】安卓开发初体验3.1——UI设计之常用控件

    常用控件 首先对xml文件的编辑有三种模式 Code为纯代码 Split是一边代码,一边预览效果图 Designer就是有UI设计界面 TextView 用于在界面上显示一段文本信息 所有控件都可以在 ...

  8. 玩转百度地图API(地图,坐标,标记,添加控件,2D图,混合图,智能搜索,地址解析器,信息窗口)

    1.注册得到appkey 2.直接上代码 <!DOCTYPE html> <html> <head> <meta http-equiv="Conte ...

  9. 玩转Android之在线视频播放控件Vitamio的使用

    其实Android中自带的MediaPlayer本身就能播放在线视频,MediaPlayer结合SurfaceView播放在线视频也是不错的选择(如果你没有性能或者用户体验上的要求),关于MediaP ...

随机推荐

  1. 学习Qt,Getting started

    在界面的设计中,现在用的比较多的是Qt和WPF(C#),以前的MFC已出现衰老趋势.本人最近在学习Qt,觉得很有用,遂决定将学习历程记录下来,或许有感于后之来者,不亦乐哉. 一.Hello Qt #i ...

  2. 拆轮子之Fish动画分析

    概述 最近发现一个很好玩的动画库,纯代码实现的而不是通过图片叠加唬人的,觉得很有意思看了下源码https://github.com/dinuscxj/LoadingDrawable, 这个动画效果使用 ...

  3. App Store10大被拒理由

    最近,苹果在官网给出了截至2015年2月份应用被拒绝的十大理由,其中50%以上的应用被拒绝都是因为这10个原因,其中7个理由和2014年相同,其中排名前三的原因分别是:需要补充更多信息.存在明显的bu ...

  4. 演进之美,越来越美:三分钟看尽 iOS 1 ~ iOS 8 的进化史

    演进之美,越来越美:三分钟看尽 iOS 1 ~ iOS 8 的进化史 原文出处: 少数派 9 月 18 日苹果就将推出 iOS 8 正式版了,从 2007 年发布第一代 iPhone 时搭载在 iPh ...

  5. 你真的知道.NET Framework中的阻塞队列BlockingCollection的妙用吗?

    BlockingCollection集合是一个拥有阻塞功能的集合,它就是完成了经典生产者消费者的算法功能.一般情况下,我们可以基于 生产者 - 消费者模式来实现并发.BlockingCollectio ...

  6. 我对AOP的理解

    1.问题 问题:想要添加日志记录.性能监控.安全监测 2.最初解决方案 2.1.最初解决方案 缺点:太多重复代码,且紧耦合 2.2.抽象类进行共性设计,子类进行个性设计,此处不讲解,缺点一荣俱荣,一损 ...

  7. go 实现struct转map

    从python转golang大约一个月了,对struct的使用还算顺手,但是很多时候还是会想念python的便捷.比如同时遍历两个字典,python使用for (x, y) in zip(map1, ...

  8. linux下redis单机版搭建

    1.1.什么是redis Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库.它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下: ...

  9. Angular集成admin-lte框架

    其实上一篇里面提到的集成datatables.net就是admin-lte里面的一个子插件,不过这个子插件,他是自带types定义文件的,admin-lte这个东西在DefinitelyTyped里面 ...

  10. how to select checkbox on cli environment?

    generally , u can focus on this checkbox and press blank key Ok,that's shit.