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


        上大学时,就听挺哥说过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>







源码



             


           
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
package com.example.game2048; import java.util.ArrayList; public class GameView extends GridLayout { private List<Point> emptyPoints=new ArrayList<Point>(); public GameView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub initGameView(); } public GameView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub initGameView(); } public GameView(Context context) { super(context); // TODO Auto-generated constructor stub initGameView(); } /*public void initGameView(){ setColumnCount(4); setBackgroundColor(0xffbbada0); setOnTouchListener(new View.OnTouchListener() { private float startX,startY,offsetX, offsetY; @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX=event.getX(); startY=event.getY(); break; case MotionEvent.ACTION_UP: offsetX=event.getX()-startX; offsetY=event.getY()-startY; if(Math.abs(offsetX)>Math.abs(offsetY)){ if(offsetX<-5){ swipeLeft(); System.out.println("left"); } else if(offsetX>5){ swipeRight(); System.out.println("right"); } }else{ if(offsetY<-5){ swipeUp(); System.out.println("up"); } else if(offsetY>5){ swipeDown(); System.out.println("down"); } } break; } return true; } }); }*/ private void initGameView(){ setColumnCount(4); setBackgroundColor(0xffbbada0); setOnTouchListener(new View.OnTouchListener() { private float startX,startY,offsetX,offsetY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: offsetX = event.getX()-startX; offsetY = event.getY()-startY; if (Math.abs(offsetX)>Math.abs(offsetY)) { if (offsetX<-5) { swipeLeft(); }else if (offsetX>5) { swipeRight(); } }else{ if (offsetY<-5) { swipeUp(); }else if (offsetY>5) { swipeDown(); } } break; } return true; } }); } protected void onSizeChanged(int w, int h, int oldw, int oldh) { // TODO Auto-generated method stub super.onSizeChanged(w, h, oldw, oldh); int cardWidth=(Math.min(w,h)-10)/4; addCards(cardWidth,cardWidth); startGame(); } private void startGame() { // TODO Auto-generated method stub for(int y=0;y<4;y++){ for(int x=0;x<4;x++){ cardsMap[x][y].setNum(0); } } addRandomNum(); addRandomNum(); addRandomNum(); addRandomNum(); addRandomNum(); addRandomNum(); addRandomNum(); addRandomNum(); } private void addCards(int cardWidth, int cardHeight) { // TODO Auto-generated method stub 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); cardsMap[x][y]=c; } } } private void addRandomNum(){ emptyPoints.clear(); for(int y=0;y<4;y++){ for(int x=0;x<4;x++){ if(cardsMap[x][y].getNum()<=0) emptyPoints.add(new Point(x,y)); } } int a=(int)(Math.random()*emptyPoints.size()); Point p=emptyPoints.remove(a);//(int)(Math.random()*emptyPoints.size()) cardsMap[p.x][p.y].setNum(Math.random()>0.2?2:4); } private void swipeLeft(){ for(int y=0;y<4;y++){ for(int x=0;x<4;x++){ for(int x1=x+1;x1<4;x++){ if(cardsMap[x1][y].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ cardsMap[x][y].setNum(cardsMap[x1][y].getNum()); cardsMap[x1][y].setNum(0); x--; break; }else if( cardsMap[x][y].equals(cardsMap[x1][y])){ cardsMap[x][y].setNum(cardsMap[x1][y].getNum()*2); cardsMap[x1][y].setNum(0); break; } } } } } } private void swipeRight(){ for(int y=0;y<4;y++){ for(int x=3;x>=0;x--){ for(int x1=x-1;x1>=0;x--){ if(cardsMap[x1][y].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ cardsMap[x][y].setNum(cardsMap[x1][y].getNum()); cardsMap[x1][y].setNum(0); x++; break; }else if( cardsMap[x][y].equals(cardsMap[x1][y])){ cardsMap[x][y].setNum(cardsMap[x1][y].getNum()*2); cardsMap[x1][y].setNum(0); break; } } } } } } private void swipeUp(){ for(int x=0;x<4;x++){ for(int y=0;y<4;y++){ for(int y1=y+1;y1<4;y1++){ if(cardsMap[x][y1].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ cardsMap[x][y].setNum(cardsMap[x][y1].getNum()); cardsMap[x][y1].setNum(0); y--; break; }else if( cardsMap[x][y].equals(cardsMap[x][y1])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x][y1].setNum(0); break; } } } } } } private void swipeDown(){ for(int x=0;x<4;x++){ for(int y=3;y>=0;y--){ for(int y1=y-1;y1>=0;y1--){ if(cardsMap[x][y1].getNum()>0){ if(cardsMap[x][y].getNum()<=0){ cardsMap[x][y].setNum(cardsMap[x][y1].getNum()); cardsMap[x][y1].setNum(0); y++; break; }else if( cardsMap[x][y].equals(cardsMap[x][y1])){ cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2); cardsMap[x][y1].setNum(0); break; } } } } } } private Card[][] cardsMap=new Card[4][4]; }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值