您的位置:首页 > 移动开发 > Android开发

Android 学习笔记之十一 2048的实现分析

2016-05-23 12:49 411 查看
        上大学时,就听挺哥说过2048是比较简单的程序,网上有一天学会的教程。 

        前几天,找了一下教程,实践成功了。

     问题分析

  做问题之前,分析必不可少,知道目的,才能有的放矢。
          

          1.UI

从界面看,2048  有一个4*4的网格型布局,里面填充有数字,然后有记录分数的文字,

  这个网上主要的实现是GridLayout   + Card  每一个Card代表网格里面的一个数字,通过操作这些Card显示的数字,显示操作结果

   2.流程

        

                主要的流程如下 
  
     完成初始化布局---->
             
                     发动点击事件 --->  

                     判断滑动方向 ---》

                     处理滑动事件 --->

                     更新分数 ---->

                     判断是否结束   
        

    3.算法

      
        前面的都是一下简单的布局相关的布局,

在处理滑动事件的时候,涉及到算法的分析。

这里简单的讲一下,  滑动事件,第一,分为上下左右,他们都是类似的 ;  第二,以向左滑动为例,滑动事件可以分为四行,每一行的分析方式相同,

          第三,每一行由四个元素组成, 需要逐个元素的计算结果。单个元素分析,以向左滑动为例,单个元素的结果它右边首个大于0的元素有关,如果这个元素与待确认的

          元素相等,则相加之后,将该元素值0。 如果待确定的元素,值为0,则直接交换值即可,这里还得回溯一步,加快游戏的节奏,是游戏更紧凑。

         

      具体实现

如下:

MainActivity.java
package com.longquan.a2048;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

public MainActivity(){
mainActivity=this;
}
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvscore= (TextView)findViewById(R.id.tvScore);
}
public void clearScore(){
score=0;
showScore();
}
public void showScore(){
tvscore.setText(score+"");
}
public void addScore(int s){
score+=s;
showScore();
}
private TextView tvscore;
private int score=0;
public static MainActivity mainActivity=null;
public static MainActivity getMainActivity(){
return mainActivity;
}
}


       
MyGridView.java


package com.longquan.a2048;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.GridLayout;
import android.widget.GridView;

import java.util.ArrayList;
import java.util.List;

/**
*
* sca
*/
public class MyGridView extends GridLayout {

private float  mStartX;
private float  mStartY;
private float  mFlipX;
private float mFlipY;

private Card[][] cardMap = new Card[4][4];
private List<Point> emptyPoints = new ArrayList<Point>();

public MyGridView(Context context) {
super(context);
init();
}

public MyGridView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MyGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

private void init() {

setColumnCount(4);
//设置背景颜色
setBackgroundColor(0xffeee4da);

setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
mStartX = event.getX();
mStartY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
;
break;
case MotionEvent.ACTION_UP:
mFlipX = event.getX() - mStartX;
mFlipY = event.getY() - mStartY;
if(Math.abs(mFlipX) >= Math.abs(mFlipY)){
if(mFlipX > 15){
swipeRight();
}else if (mFlipX < -15 ){
swipeLeft();
}
}else{
if(mFlipY > 15){
swipeDown();
}else if(mFlipY < -15){
swipeUp();
}
}
break;
}
return true;
}
});

}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int CardWidth =  (Math.min(w,h))/4;
addCards(CardWidth,CardWidth);
startGame();
}
//在4x4的方格上添加满卡片
public void addCards(int cardwidth,int cardheight){
Card c;
for(int y =0;y <4;y++){
for(int x =0;x <4;x++){
c=new Card(getContext());
c.setNum(0);
//                addView(c,cardwidth,cardheight);
addView(c,cardwidth,cardheight);
cardMap[x][y]=c;
}
}
}

//游戏开始时每个卡片默认值设为0,并随机添加两张带数字的卡片
private void startGame(){
MainActivity.getMainActivity().clearScore();
for(int y =0;y <4;y++){
for(int x =0;x <4;x++){
cardMap[x][y].setNum(0);
}
}
addRandomNum();
addRandomNum();
}

private void addRandomNum(){
//使用emptypoints将数字为0的card提取出来,并随即选择一个空card赋值
emptyPoints.clear();
for(int y =0;y <4;y++){
for(int x =0;x <4;x++){
if(cardMap[x][y].getNum()<=0){
emptyPoints.add(new Point(x,y));
}
}
}
Point p = emptyPoints.remove((int)(Math.random()*emptyPoints.size()));
//2和4出现的概率控制在1:9
cardMap[p.x][p.y].setNum(Math.random()>0.1?2:4);
}

//左滑方法
private void swipeLeft(){
//merge作为判断能否滑动的flag
boolean merge =false;
for(int y =0;y <4;y++){
//以向左滑动为例,滑动事件可以分为四行,每一行的分析方式相同,
for(int x =0;x <4;x++){
//每一行由四个元素组成, 需要逐个元素的计算结果。单个元素分析
for(int x1 =x+1;x1 <4;x1++){
if(cardMap[x1][y].getNum()>0){
if(cardMap[x][y].getNum()<=0){
cardMap[x][y].setNum(cardMap[x1][y].getNum());
cardMap[x1][y].setNum(0);
merge=true;
x--;  // 加这一句是为了在两个连续的**22,*22* 的时候,会自动发生合并
}else if(cardMap[x][y].equal(cardMap[x1][y])){
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
merge=true;
}
break;
}
}
}
}
if(merge){
addRandomNum();
checkComplete();
}
}
//下滑
private void swipeDown(){

boolean merge =false;

for(int x =0;x <4;x++){
for(int y =3;y >=0;y--){

for(int y1 =y-1;y1 >=0;y1--){
if(cardMap[x][y1].getNum()>0){

if(cardMap[x][y].getNum()<=0){
cardMap[x][y].setNum(cardMap[x][y1].getNum());
cardMap[x][y1].setNum(0);

y++;
merge= true;
}else if (cardMap[x][y].equal(cardMap[x][y1])){
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x][y1].setNum(0);
MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
merge= true;
}

break;
}
}
}
}

if(merge){
addRandomNum();
checkComplete();
}
}
//上滑
private void swipeUp(){

boolean merge =false;

for(int x =0;x <4;x++){
for(int y =0;y <4;y++){

for(int y1 =y+1;y1 <4;y1++){
if(cardMap[x][y1].getNum()>0){

if(cardMap[x][y].getNum()<=0){
cardMap[x][y].setNum(cardMap[x][y1].getNum());
cardMap[x][y1].setNum(0);

y--;

merge= true;
}else if (cardMap[x][y].equal(cardMap[x][y1])){
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x][y1].setNum(0);
MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
merge= true;
}

break;

}
}
}
}

if(merge){
addRandomNum();
checkComplete();
}
}
//右滑
private void swipeRight(){
boolean merge =false;
for(int y =0;y <4;y++){
for(int x =3;x >=0;x--){
for(int x1 =x-1;x1 >=0;x1--){
if(cardMap[x1][y].getNum()>0){
if(cardMap[x][y].getNum()<=0){
cardMap[x][y].setNum(cardMap[x1][y].getNum());
cardMap[x1][y].setNum(0);
x++;
merge=true;
}else if(cardMap[x][y].equal(cardMap[x1][y])){
cardMap[x][y].setNum(cardMap[x][y].getNum()*2);
cardMap[x1][y].setNum(0);
MainActivity.getMainActivity().addScore(cardMap[x][y].getNum());
merge=true;
}
break;
}
}
}
}
if(merge){
addRandomNum();
checkComplete();
}
}
//如果有空卡片或者相邻的值相同卡片则游戏还能进行
public void checkComplete(){
boolean complete=true;
ALL:
for(int y =0;y <4;y++){
for(int x =0;x <4;x++){
if(cardMap[x][y].getNum()==0||
x>0&&cardMap[x][y].equal(cardMap[x-1][y])||
x<3&&cardMap[x][y].equal(cardMap[x+1][y])||
y>0&&cardMap[x][y].equal(cardMap[x][y-1])||
y<3&&cardMap[x][y].equal(cardMap[x][y+1])){
complete=false;
break ALL;
}
}
}
//游戏结束弹出alert提示窗口
if(complete){
new AlertDialog.Builder(getContext()).setTitle("大林哥温馨提示").setMessage("游戏结束").setPositiveButton("重来",new DialogInterface.OnClickListener(){

@Override
public void onClick(DialogInterface arg0,int arg1){
startGame();
}
}).show();
}

}

}


              MianLayout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.longquan.a2048.MainActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="@dimen/activity_left_magin"
android:id="@+id/score"/>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/tvScore"/>

</LinearLayout>

//使用自定义的GridLayout
<com.longquan.a2048.MyGridView
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/GameView">
</com.longquan.a2048.MyGridView>

</LinearLayout>


card .java 

package com.longquan.a2048;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;

/**
* Created by user on 16-5-20.
*/
public class Card extends FrameLayout {

//卡片显示的数字
private int n;
//
private TextView labal;

public Card(Context context) {
super(context);
init();
}

public Card(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public Card(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public Card(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}

private void init (){
LayoutParams lp = null;
lp = new LayoutParams(-1,-1);
View background  = new  View(getContext());
//间隔
lp.setMargins(10,10,0,0);
background.setBackgroundColor(0x33ffffff);
addView(background,lp);

labal = new TextView(getContext());
labal.setTextSize(28);
labal.setGravity(Gravity.CENTER);
lp.setMargins(10,10,0,0);
addView(labal,lp);

}

public int getNum(){
return n;
}

//设置数字及对应的背景颜色
public void setNum(int n){
this.n=n;
if(n<=0){
labal.setText("");
}else{
labal.setText(n+"");
}
switch (n) {
case 0:
labal.setBackgroundColor(0x00000000);
break;
case 2:
labal.setBackgroundColor(0xffeee4da);
break;
case 4:
labal.setBackgroundColor(0xffede0c8);
break;
case 8:
labal.setBackgroundColor(0xfff2b179);
break;
case 16:
labal.setBackgroundColor(0xfff59563);
break;
case 32:
labal.setBackgroundColor(0xfff67c5f);
break;
case 64:
labal.setBackgroundColor(0xfff65e3b);
break;
case 128:
labal.setBackgroundColor(0xffedcf72);
break;
case 256:
labal.setBackgroundColor(0xffedcc61);
break;
case 512:
labal.setBackgroundColor(0xffedc850);
break;
case 1024:
labal.setBackgroundColor(0xffedc53f);
break;
case 2048:
labal.setBackgroundColor(0xffedc22e);
break;
default:
labal.setBackgroundColor(0xff3c3a32);
break;
}
}

//判断卡片是否相等
public boolean equal(Card o){
return getNum()==o.getNum();
}

}


Manifst.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.longquan.a2048">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
>
<activity android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>


源码

             

           
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android开发 android