<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