安卓實訓項目:貪喫蛇V1.0

目錄

一、功能要求

1、遊戲規則

2、遊戲設置

3、遊戲排行榜

二、運行效果

1、啓動界面

2、遊戲主控界面

3、選擇地圖

4、設置速度

5、選擇背景音樂

6、開始遊戲

7、遊戲結束

8、查看排行榜

9、查看幫助信息

10、關於作者

三、涉及知識點

1、佈局

2、常用控件

3、線程

4、消息處理器

5、自定義視圖

6、圖形圖像繪製

7、媒體播放器

8、集合類

四、項目結構圖

五、項目實現

1、創建安卓項目:貪喫蛇V1.0

2、修改包名

3、創建子包entity、app與view

4、準備圖片素材,拷貝到drawable-mdpi目錄裏

5、在res裏創建raw文件夾,拷貝幾首mp3音樂

6、在res裏創建drawable目錄,在裏面創建按鈕背景選擇器

(1)開始遊戲按鈕背景選擇器

(2)選擇地圖按鈕背景選擇器

(3)選擇背景音樂按鈕背景選擇器

(4)設置速度按鈕背景選擇器

(5)排行榜按鈕背景選擇器

(6)關於作者按鈕背景選擇器

(7)幫助信息按鈕背景選擇器

(8)退出遊戲按鈕背景選擇器

7、在app子包裏創建應用程序常量接口AppConstants

8、在app子包裏創建貪喫蛇應用程序類SnakeApplicaiton

9、在entity子包裏創建座標實體類Coordinate

10、在view子包裏創建方磚視圖TileView

(1)在values目錄創建屬性文件attrs.xml

(2)方磚視圖類TileView

11、在view子包裏創建貪喫蛇視圖SnakeView

12、在ui子包裏創建貪喫蛇界面類SnakeActivity

(1)貪喫蛇界面佈局文件activity_snake.xml

(2)貪喫蛇界面類SnakeActivity

13、在ui子包裏創建選擇地圖界面類SelectMapActivity

(1)在values裏的styles.xml裏增加單選按鈕和按鈕的樣式定義

(2)選擇地圖界面佈局文件activity_select_map.xml

(3)選擇地圖界面類SelectMapActivity

14、在ui子包裏創建設置速度界面類SetSpeedActivity

(1)設置速度界面佈局文件activity_set_speed.xml

(2)設置速度界面類SetSpeedActivity

15、在ui子包裏創建選擇背景音樂界面類SelectMusicActivity

(1)選擇背景音樂界面佈局文件activity_select_music.xml

(2)音樂列表項模板music_list_item.xml

(3)選擇背景音樂界面類SelectMusicActivity

16、在ui子包裏創建排行榜界面類TopListActivity

(1)排行榜界面佈局文件activity_top_list.xml

(2)在字符串資源文件strings.xml裏定義變量

(3)排行榜界面類TopListActivity

17、在ui子包裏創建主界面類MainActivity

(1)主界面佈局文件activity_main.xml

(2)字符串資源文件strings.xml裏定義變量

(4)主界面類MainActivity

18、在ui子包裏修改啓動界面類SplashScreen

(1)啓動界面佈局文件activity_splash_screen.xml

(2)字符串資源文件strings.xml裏定義變量

(3)啓動界面類SplashScreenActivity

19、項目清單文件設置全部界面都是全屏顯示


項目圖片素材【百度網盤下載網址 密碼:gcpt】

一、功能要求

1、遊戲規則

玩家使用方向鍵或手勢滑動操控一條長長的蛇不斷吞蘋果,同時蛇身隨着吞下的蘋果不斷長,當蛇頭撞到蛇身或障壁時遊戲結束。

2、遊戲設置

玩家可選擇地圖、設置背景音樂、選擇蛇移動速度。

3、遊戲排行榜

提供玩家排行榜,顯示前三名分數(貪喫蛇喫掉蘋果的數量)

二、運行效果

1、啓動界面

2、遊戲主控界面

3、選擇地圖

4、設置速度

5、選擇背景音樂

6、開始遊戲

7、遊戲結束

8、查看排行榜

9、查看幫助信息

10、關於作者

三、涉及知識點

1、佈局

2、常用控件

3、線程

4、消息處理器

5、自定義視圖

6、圖形圖像繪製

7、媒體播放器

8、集合類

四、項目結構圖

五、項目實現

1、創建安卓項目:貪喫蛇V1.0

2、修改包名

說明:net.hw.snake爲根包,ui子包用於存放界面類。

3、創建子包entity、app與view

4、準備圖片素材,拷貝到drawable-mdpi目錄裏

5、在res裏創建raw文件夾,拷貝幾首mp3音樂

6、在res裏創建drawable目錄,在裏面創建按鈕背景選擇器

(1)開始遊戲按鈕背景選擇器

(2)選擇地圖按鈕背景選擇器

(3)選擇背景音樂按鈕背景選擇器

(4)設置速度按鈕背景選擇器

(5)排行榜按鈕背景選擇器

(6)關於作者按鈕背景選擇器

(7)幫助信息按鈕背景選擇器

(8)退出遊戲按鈕背景選擇器

7、在app子包裏創建應用程序常量接口AppConstants

/**
 * 功能:應用程序常量接口
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.app;

import android.app.Activity;

public interface AppConstants {
    /**
    * 應用程序標記
    */
    String TAG = "net.hw.snake";
    
    /**
    * 地圖常量
    */
    int O_SHAPE_MAP = 0;
    int I_SHAPE_MAP = 1;
    int X_SHAPE_MAP = 2;
    int Z_SHAPE_MAP = 3;
    int U_SHAPE_MAP = 4;
    
    /**
    * 速度常量
    */
    int LOW_SPEED = 0;
    int MEDIUM_SPEED = 1;
    int HIGH_SPEED = 2;

    /**
    * 排行榜配置文件名
    */
    String TOP_LIST_FILE_NAME = "toplist";
    /**
    * 文件訪問模式
    */
    int ACCESS_MODE = Activity.MODE_PRIVATE;

    /**
    * 遊戲狀態常量
    */
    int PAUSE = 0;
    int READY = 1;
    int RUNNING = 2;
    int LOSE = 3;

    /**
    * 移動方向常量
    */
    int NORTH = 1;
    int SOUTH = 2;
    int EAST = 3;
    int WEST = 4;

    /**
    * 圖片常量
    */
    int RED_STAR = 1;
    int YELLOW_STAR = 2;
    int GREEN_STAR = 3;
    
    /**
    * 蛇兩次移動之間的間隔
    */
    int SHORT_INTERVAL = 100;
    int MEDIUM_INTERVAL = 350;
    int LONG_INTERVAL = 600;    
    
    /**
    * 前三名分數
    */
    String SCORE_OF_FIRST = "score_of_first";
    String SCORE_OF_SECOND = "score_of_second";
    String SCORE_OF_THIRD = "score_of_third";
}

8、在app子包裏創建貪喫蛇應用程序類SnakeApplicaiton

 

/**
 * 功能:貪喫蛇應用程序類
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.app;

import android.app.Application;
import android.media.MediaPlayer;

public class SnakeApplication extends Application {
    /**
    * 地圖索引
    */
    private int mapIndex;
    /**
    * 背景音樂索引
    */
    private int musicIndex;
    /**
    * 速度索引
    */
    private int speedIndex;
    /**
    * 媒體播放器
    */
    private MediaPlayer mediaPlayer;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public int getMapIndex() {
        return mapIndex;
    }

    public void setMapIndex(int mapIndex) {
        this.mapIndex = mapIndex;
    }

    public int getMusicIndex() {
        return musicIndex;
    }

    public void setMusicIndex(int musicIndex) {
        this.musicIndex = musicIndex;
    }

    public int getSpeedIndex() {
        return speedIndex;
    }

    public void setSpeedIndex(int speedIndex) {
        this.speedIndex = speedIndex;
    }

    public MediaPlayer getMediaPlayer() {
        return mediaPlayer;
    }

    public void setMediaPlayer(MediaPlayer mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
    }
}

9、在entity子包裏創建座標實體類Coordinate

/**
 * 功能:座標實體
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.entity;

public class Coordinate {
    /**
    * 橫座標
    */
    public int x;
    /**
    * 縱座標
    */
    public int y;

    /**
    * 構造方法
    * 
    * @param x
    * @param y
    */
    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    /**
    * 相等方法(判斷兩點是否重合)
    */
    public boolean equals(Object obj) {
        if (obj instanceof Coordinate) {
            Coordinate other = (Coordinate) obj;
            if (x == other.x && y == other.y) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String toString() {
        return "Coordinate: [" + x + "," + y + "]";
    }
}

10、在view子包裏創建方磚視圖TileView

 

(1)在values目錄創建屬性文件attrs.xml

(2)方磚視圖類TileView

/**
 * 功能:方磚視圖
 * 作者:華衛
 * 日期:2017年1月1日
 */

/**
 * 控制方磚尺寸及其在視圖裏顯示範圍的參數,寬度與高度的單位是像素。 
 * 可繪製對象可以縮放來適合方磚尺寸,然後計算出橫向與縱向的方磚數。 
*/
package net.hw.snake.view;

import net.hw.snake.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

public class TileView extends View {
    /**
    * 方磚尺寸
    */
    protected static int mTileSize;
    /**
    * 橫向方磚數量
    */
    protected static int mXTileCount;
    /**
    * 縱向方磚數量
    */
    protected static int mYTileCount; 
    /**
    * 橫向偏移量
    */
    private static int mXOffset;
    /**
    * 縱向偏移量
    */
    private static int mYOffset;
    /**
    * 方磚數組
    */
    private Bitmap[] mTileArray;
    /**
    * 方磚網格
    */
    private int[][] mTileGrid;
    /**
    * 畫筆
    */
    private final Paint mPaint = new Paint();

    /**
    * 構造方法
    * 
    * @param context
    * @param attrs
    * @param defStyle
    */
    public TileView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
        mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
        a.recycle();
    }

    /**
    * 構造方法
    * 
    * @param context
    * @param attrs
    */
    public TileView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
        mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
        a.recycle();
    }

    /**
    * 重置方磚
    * 
    * @param tilecount
    */

    public void resetTiles(int tileCount) {
        mTileArray = new Bitmap[tileCount];
    }

    /**
    * 屏幕尺寸發生變化時觸發,重新計算橫向與縱向方磚數以及橫向與縱向的偏移量,設置方磚網格
    */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // 橫向方磚數
        mXTileCount = (int) Math.floor(w / mTileSize);
        // 縱向方磚數
        mYTileCount = (int) Math.floor(h / mTileSize);

        // 橫向偏移量
        mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
        // 縱向偏移量
        mYOffset = ((h - (mTileSize * mYTileCount)) / 2);

        // 方磚網格
        mTileGrid = new int[mXTileCount][mYTileCount];

        // 清除方磚
        clearTiles();
    }

    /**
    * 加載方磚
    * 
    * @param key
    * @param tile
    */
    public void loadTile(int key, Drawable tile) {
        Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        tile.setBounds(0, 0, mTileSize, mTileSize);
        tile.draw(canvas);
        mTileArray[key] = bitmap;
    }

    /**
    * 清空方磚
    */
    public void clearTiles() {
        for (int x = 0; x < mXTileCount; x++) {
            for (int y = 0; y < mYTileCount; y++) {
                // 第一個參數爲0,表明不畫方磚
                setTile(0, x, y); 
            }
        }
    }

    /**
    * 設置方磚
    * 
    * @param tileIndex
    * @param x
    * @param y
    */
    public void setTile(int tileIndex, int x, int y) {
        // 在x行y列畫指定的方磚
        mTileGrid[x][y] = tileIndex; 
    }

    /**
    * 繪製方法
    */
    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int x = 0; x < mXTileCount; x += 1) {
            for (int y = 0; y < mYTileCount; y += 1) {
                if (mTileGrid[x][y] > 0) { 
                    canvas.drawBitmap(mTileArray[mTileGrid[x][y]], 
                          mXOffset + x * mTileSize, mYOffset + y * mTileSize, mPaint);
                }
            }
        }
    }
}

11、在view子包裏創建貪喫蛇視圖SnakeView

/**
 * 功能:貪喫蛇視圖
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.view;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;
import net.hw.snake.R;
import net.hw.snake.app.AppConstants;
import net.hw.snake.app.SnakeApplication;
import net.hw.snake.entity.Coordinate;

public class SnakeView extends TileView implements AppConstants {
    /**
    * 共享參數
    */
    private SharedPreferences sp;
    /**
    * 編輯器
    */
    private Editor editor;
    /**
    * 名次信息
    */
    private String rankMsg;
    /**
    * 遊戲狀態變量
    */
    public int mMode = READY;

    /**
    * 蛇當前移動方向
    */
    public int mDirection = EAST;
    /**
    * 蛇下一個移動方向
    */
    public int mNextDirection = EAST;
    /**
    * 分數:蛇喫掉的蘋果數量
    */
    private int mScore = 0;
    /**
    * 蛇移動的時間間隔
    */
    private int mMoveDelay = 600;
    /**
    * 蛇上次移動的時間
    */
    private long mLastMove;
    /**
    * 蛇身數組列表
    */
    private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
    /**
    * 蘋果數組列表
    */
    private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();
    /**
    * 牆壁數組列表
    */
    private ArrayList<Coordinate> mWallList = new ArrayList<Coordinate>();
    /**
    * 隨機對象
    */
    private static final Random RNG = new Random();
    /**
    * 貪喫蛇應用程序
    */
    private SnakeApplication app;

    /**
    * 負責重繪的消息處理器
    */
    private class RefreshHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            // 更新貪喫蛇視圖
            SnakeView.this.update();
            // 重繪貪喫蛇視圖
            SnakeView.this.invalidate();
        }

        public void sleep(long delayMillis) {
            // 清空消息隊列
            this.removeMessages(0);
            // 延遲發送消息
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    }

    /**
    * 聲明負責重繪的消息處理器
    */
    private RefreshHandler mRedrawHandler = new RefreshHandler();

    /**
    * 貪喫蛇視圖構造方法
    * 
    * @param context
    * @param attrs
    */
    public SnakeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 獲取貪喫蛇應用程序對象
        app = (SnakeApplication) ((Activity) context).getApplication();
        // 初始化貪喫蛇視圖
        initSnakeView();
    }

    /**
    * 構造貪喫蛇視圖
    * 
    * @param context
    * @param attrs
    * @param defStyle
    */
    public SnakeView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // 獲取貪喫蛇應用程序對象
        app = (SnakeApplication) ((Activity) context).getApplication();
        // 初始化貪喫蛇視圖
        initSnakeView();

    }

    /**
    * 初始化貪喫蛇視圖
    */
    private void initSnakeView() {
        // 設置爲可獲得焦點
        setFocusable(true);
        // 重置方磚(四種方磚)
        resetTiles(4);
        // 加載紅星方磚
        loadTile(RED_STAR, getContext().getResources().getDrawable(R.drawable.redstar));
        // 加載黃星方磚
        loadTile(YELLOW_STAR, getContext().getResources().getDrawable(R.drawable.yellowstar));
        // 加載綠星方磚
        loadTile(GREEN_STAR, getContext().getResources().getDrawable(R.drawable.greenstar));

        // 加載音樂
        switch (app.getMusicIndex()) {
        case 0:
            // 音樂:夢中的婚禮
            app.setMediaPlayer(MediaPlayer.create(getContext(), R.raw.dream));
            break;
        case 1:
            // 音樂:秋日私語
            app.setMediaPlayer(MediaPlayer.create(getContext(), R.raw.autumn));
            break;
        case 2:
            // 音樂:天空之城
            app.setMediaPlayer(MediaPlayer.create(getContext(), R.raw.sky));
            break;
        case 3:
            // 音樂:致愛麗絲
            app.setMediaPlayer(MediaPlayer.create(getContext(), R.raw.alice));
            break;
        case 4:
            // 歌曲:貝爾加湖畔
            app.setMediaPlayer(MediaPlayer.create(getContext(), R.raw.lake));
        case 5:
            // 歌曲:暗香
            app.setMediaPlayer(MediaPlayer.create(getContext(), R.raw.fragrance));
        }
        // 設置爲循環播放
        app.getMediaPlayer().setLooping(true);

        // 創建共享參數對象
        sp = getContext().getSharedPreferences(TOP_LIST_FILE_NAME, ACCESS_MODE);
        // 獲得編輯器
        editor = sp.edit();
    }

    /**
    * 初始化新遊戲
    */
    public void initNewGame() {
        // 清除蛇
        mSnakeTrail.clear();
        // 清除所有蘋果
        mAppleList.clear();

        // 初始蛇,三節,朝東移動
        mSnakeTrail.add(new Coordinate(7, 7));
        mSnakeTrail.add(new Coordinate(6, 7));
        mSnakeTrail.add(new Coordinate(5, 7));
        mNextDirection = EAST;

        // 更新牆壁
        updateWalls();

        // 隨機添加兩個蘋果
        addRandomApple();
        addRandomApple();

        // 根據用戶設置速度修改初始的蛇兩次移動時間間隔初始值
        switch (app.getSpeedIndex()) {
        case LOW_SPEED:
            mMoveDelay = LONG_INTERVAL;
            break;
        case MEDIUM_SPEED:
            mMoveDelay = MEDIUM_INTERVAL;
            break;
        case HIGH_SPEED:
            mMoveDelay = SHORT_INTERVAL;
            break;
        }

        // 初始化遊戲得分
        mScore = 0;
    }

    /**
    * 將數組列表轉換成數組
    * 
    * @param cvec
    *            座標對象的數組列表
    * @return 包含座標值的簡單數組,如 [x1,y1,x2,y2,x3,y3...]
    */
    private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
        int count = cvec.size();
        int[] rawArray = new int[count * 2];
        for (int index = 0; index < count; index++) {
            Coordinate c = cvec.get(index);
            rawArray[2 * index] = c.x;
            rawArray[2 * index + 1] = c.y;
        }
        return rawArray;
    }

    /**
    * 保存遊戲狀態,那麼在後臺遊戲進程被殺掉也不會丟失任何數據
    * 
    * @return 保存視圖狀態的數據包
    */
    public Bundle saveState() {
        Bundle map = new Bundle();

        map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
        map.putInt("mDirection", Integer.valueOf(mDirection));
        map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
        map.putLong("mMoveDelay", Integer.valueOf(mMoveDelay));
        map.putLong("mScore", Integer.valueOf(mScore));
        map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));

        return map;
    }

    /**
    * 恢復遊戲狀態
    * 
    * @param data
    *            包含遊戲狀態的數據包
    */
    public void restoreState(Bundle data) {
        // 設置遊戲模式爲暫停
        setMode(PAUSE);

        mAppleList = coordArrayToArrayList(data.getIntArray("mAppleList"));
        mDirection = data.getInt("mDirection");
        mNextDirection = data.getInt("mNextDirection");
        mMoveDelay = data.getInt("mMoveDelay");
        mScore = data.getInt("mScore");
        mSnakeTrail = coordArrayToArrayList(data.getIntArray("mSnakeTrail"));
    }

    /**
    * 將座標對構成的數組轉換成座標對象的數組列表
    * 
    * @param rawArray
    *            : [x1,y1,x2,y2,...]
    * @return 座標對象的數組列表
    */
    private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
        ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();

        int coordCount = rawArray.length;
        for (int index = 0; index < coordCount; index += 2) {
            Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
            coordArrayList.add(c);
        }
        return coordArrayList;
    }

    /*
    * 按鍵事件處理
    */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg) {
        // 按上方向鍵
        if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
            if (mMode == READY | mMode == LOSE) {
                // 開始新遊戲
                initNewGame();
                // 設置爲運行模式
                setMode(RUNNING);
                // 播放音樂
                app.getMediaPlayer().start();
                // 更新遊戲界面
                update();
                return (true);
            }

            if (mMode == PAUSE) {
                setMode(RUNNING);
                update();
                return (true);
            }

            if (mDirection != SOUTH) {
                mNextDirection = NORTH;
            }
            return (true);
        }

        // 按下方向鍵
        if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
            if (mDirection != NORTH) {
                mNextDirection = SOUTH;
            }
            return (true);
        }

        // 按左方向鍵
        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
            if (mDirection != EAST) {
                mNextDirection = WEST;
            }
            return (true);
        }

        // 按右方向鍵
        if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
            if (mDirection != WEST) {
                mNextDirection = EAST;
            }
            return (true);
        }

        return super.onKeyDown(keyCode, msg);
    }

    /**
    * 設置遊戲模式
    * 
    * @param newMode
    */
    public void setMode(int newMode) {
        int oldMode = mMode;
        mMode = newMode;

        if (newMode == RUNNING & oldMode != RUNNING) {
            update();
            app.getMediaPlayer().start();
            return;
        }

        if (newMode == LOSE) {
            app.getMediaPlayer().stop();
            try {
                app.getMediaPlayer().prepare();
                app.getMediaPlayer().seekTo(0);
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
    * 隨機添加蘋果
    */
    private void addRandomApple() {
        Coordinate newCoord = null;
        boolean found = false;
        while (!found) {
            // 爲蘋果選擇新位置
            int newX = 1 + RNG.nextInt(mXTileCount - 2);
            int newY = 1 + RNG.nextInt(mYTileCount - 2);

            newCoord = new Coordinate(newX, newY);

            boolean collision = false;
            // 判斷新產生的蘋果有沒有與蛇或者牆碰撞
            if (mSnakeTrail.contains(newCoord) || mWallList.contains(newCoord)) {
                collision = true;
            }

            // 如果碰撞了,就表明沒找到合適位置,還要繼續找
            found = !collision;
        }

        if (newCoord == null) {
            Log.e(TAG, "新座標爲空!");
        }

        mAppleList.add(newCoord);
    }

    /**
    * 更新方法:檢查是否處於運行狀態,決定是否該移動,更新蛇的位置
    */
    public void update() {
        if (mMode == RUNNING) {
            long now = System.currentTimeMillis();

            if (now - mLastMove > mMoveDelay) {
                clearTiles();
                updateWalls();
                updateSnake();
                updateApples();
                mLastMove = now;
            }
            mRedrawHandler.sleep(mMoveDelay);
        }
    }

    /**
    * 更新牆壁
    * 
    */
    private void updateWalls() {
        for (int x = 0; x < mXTileCount; x++) {
            setTile(GREEN_STAR, x, 0);
            setTile(GREEN_STAR, x, mYTileCount - 1);
            mWallList.add(new Coordinate(x, 0));
            mWallList.add(new Coordinate(x, mYTileCount - 1));
        }
        for (int y = 1; y < mYTileCount - 1; y++) {
            setTile(GREEN_STAR, 0, y);
            setTile(GREEN_STAR, mXTileCount - 1, y);
            mWallList.add(new Coordinate(0, y));
            mWallList.add(new Coordinate(mXTileCount - 1, y));
        }

        switch (app.getMapIndex()) {
        case I_SHAPE_MAP: // 工字型地圖
            for (int x = 6; x < mXTileCount - 6; x++) {
                setTile(GREEN_STAR, x, 20);
                mWallList.add(new Coordinate(x, 20));
            }
            for (int y = 20; y <= mYTileCount - 20; y++) {
                setTile(GREEN_STAR, mXTileCount / 2, y);
                mWallList.add(new Coordinate(mXTileCount / 2, y));
            }
            for (int x = 6; x < mXTileCount - 6; x++) {
                setTile(GREEN_STAR, x, mYTileCount - 20);
                mWallList.add(new Coordinate(x, mYTileCount - 20));
            }
            break;
        case X_SHAPE_MAP: // X字型地圖
            for (int x = 10, y = 20; x < mXTileCount - 10 && y < mYTileCount - 20; x++, y++) {
                setTile(GREEN_STAR, x, y);
                mWallList.add(new Coordinate(x, y));
            }
            for (int x = mXTileCount - 10, y = 20; x >= 10 && y < mYTileCount - 20; x--, y++) {
                setTile(GREEN_STAR, x, y);
                mWallList.add(new Coordinate(x, y));
            }
            break;
        case Z_SHAPE_MAP: // Z字型地圖
            for (int x = 6; x < mXTileCount - 6; x++) {
                setTile(GREEN_STAR, x, 20);
                mWallList.add(new Coordinate(x, 20));
            }
            for (int x = mXTileCount - 6, y = 20; y <= 35; y++) {
                setTile(GREEN_STAR, x, y);
                mWallList.add(new Coordinate(x, y));
            }

            for (int x = 6; x < mXTileCount - 6; x++) {
                setTile(GREEN_STAR, x, 35);
                mWallList.add(new Coordinate(x, 35));
            }

            for (int x = 6, y = 35; y <= mYTileCount - 20; y++) {
                setTile(GREEN_STAR, x, y);
                mWallList.add(new Coordinate(x, y));
            }

            for (int x = 6; x < mXTileCount - 6; x++) {
                setTile(GREEN_STAR, x, mYTileCount - 20);
                mWallList.add(new Coordinate(x, mYTileCount - 20));
            }
            break;
        case U_SHAPE_MAP: // U字型地圖
            for (int x = 6, y = 20; y < mYTileCount - 20; y++) {
                setTile(GREEN_STAR, x, y);
                mWallList.add(new Coordinate(x, y));
            }
            for (int x = mXTileCount - 7, y = 20; y < mYTileCount - 20; y++) {
                setTile(GREEN_STAR, x, y);
                mWallList.add(new Coordinate(x, y));
            }
            for (int x = 6; x < mXTileCount - 6; x++) {
                setTile(GREEN_STAR, x, mYTileCount - 20);
                mWallList.add(new Coordinate(x, mYTileCount - 20));
            }
            break;
        }
    }

    /**
    * 更新蘋果
    */
    private void updateApples() {
        for (Coordinate c : mAppleList) {
            setTile(YELLOW_STAR, c.x, c.y);
        }
    }

    /**
    * 更新蛇
    */
    private void updateSnake() {
        // 設置蛇生長狀態
        boolean growSnake = false;

        // 通過蛇頭來抓住蛇
        Coordinate head = mSnakeTrail.get(0);
        Coordinate newHead = new Coordinate(1, 1);

        mDirection = mNextDirection;

        // 根據蛇移動方向決定蛇頭座標
        switch (mDirection) {
        case EAST:
            newHead = new Coordinate(head.x + 1, head.y);
            break;
        case WEST:
            newHead = new Coordinate(head.x - 1, head.y);
            break;
        case NORTH:
            newHead = new Coordinate(head.x, head.y - 1);
            break;
        case SOUTH:
            newHead = new Coordinate(head.x, head.y + 1);
            break;
        }

        // 判斷是否撞牆
        if (mWallList.contains(newHead)) {
            // 遊戲失敗
            setMode(LOSE);
            // 更新排行榜
            updateTopList();
            // 停止音樂播放
            app.getMediaPlayer().stop();
            try {
                app.getMediaPlayer().prepare();
                app.getMediaPlayer().seekTo(0);
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
            builder.setIcon(R.drawable.ic_launcher);
            builder.setTitle("遊戲結束");
            if (mScore > 0) {
                builder.setMessage("呵呵,你喫掉了" + mScore + "個蘋果!" + rankMsg);
            } else {
                builder.setMessage("遺憾,你一個蘋果也沒有喫掉!");
            }
            builder.setPositiveButton("返回主界面", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // 設置遊戲模式爲準備就緒
                    setMode(READY);
                    // 關閉當前界面
                    ((Activity) getContext()).finish();
                }
            });
            builder.setNegativeButton("再來玩一次", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // 初始化新遊戲
                    initNewGame();
                    // 設置遊戲模式爲運行
                    setMode(RUNNING);
                    // 播放音樂
                    app.getMediaPlayer().start(); 
                    // 更新遊戲
                    update();
                }
            });
            builder.show();
        }

        // 判斷是否撞到自己
        if (mSnakeTrail.contains(newHead)) {
            // 遊戲失敗
            setMode(LOSE);
            // 更新排行榜
            updateTopList();
            // 停止音樂播放
            app.getMediaPlayer().stop();
            try {
                app.getMediaPlayer().prepare();
                app.getMediaPlayer().seekTo(0);
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
            builder.setIcon(R.drawable.ic_launcher);
            builder.setTitle("遊戲結束");
            if (mScore > 0) {
                builder.setMessage("呵呵,你喫掉了" + mScore + "個蘋果!" + rankMsg);
            } else {
                builder.setMessage("遺憾,你一個蘋果也沒有喫掉!");
            }
            builder.setPositiveButton("返回主界面", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    setMode(READY);
                    ((Activity) getContext()).finish();
                }
            });
            builder.setNegativeButton("再來玩一次", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // 初始化新遊戲
                    initNewGame();
                    // 設置遊戲模式爲運行
                    setMode(RUNNING);
                    // 播放音樂
                    app.getMediaPlayer().start();
                    // 更新遊戲
                    update();
                }
            });
            builder.show();
        }

        // 檢測是否碰撞了蘋果
        int applecount = mAppleList.size();
        for (int appleindex = 0; appleindex < applecount; appleindex++) {
            Coordinate c = mAppleList.get(appleindex);
            if (c.equals(newHead)) {
                mAppleList.remove(c);
                addRandomApple();
                // 得一分
                mScore++;
                switch (app.getSpeedIndex()) {
                case LOW_SPEED:
                    mMoveDelay *= 0.9;
                    break;
                case MEDIUM_SPEED:
                    mMoveDelay *= 0.8;
                    break;
                case HIGH_SPEED:
                    mMoveDelay *= 0.7;
                }
                // 蛇生長狀態設置爲真
                growSnake = true;
            }
        }

        // 在蛇頭添加新元素
        mSnakeTrail.add(0, newHead);
        // 如果蛇不生長,那麼去掉蛇尾一個元素
        if (!growSnake) {
            mSnakeTrail.remove(mSnakeTrail.size() - 1);
        }

        // 重新繪製蛇(蛇頭設置爲黃磚,蛇身設置爲紅磚)
        int index = 0;
        for (Coordinate c : mSnakeTrail) {
            if (index == 0) {
                setTile(YELLOW_STAR, c.x, c.y);
            } else {
                setTile(RED_STAR, c.x, c.y);
            }
            index++;
        }
    }

    /**
    * 更新排行榜
    */
    private void updateTopList() {      
        // 獲取前三名分數
        int scoreOfFirst = sp.getInt(SCORE_OF_FIRST, 0);
        int scoreOfSecond = sp.getInt(SCORE_OF_SECOND, 0);
        int scoreOfThird = sp.getInt(SCORE_OF_THIRD, 0);
        if (mScore > scoreOfFirst) {
            rankMsg = "恭喜你獲得第一名!";
            editor.putInt(SCORE_OF_FIRST, (int) mScore);
            editor.putInt(SCORE_OF_SECOND, scoreOfSecond);
            editor.putInt(SCORE_OF_THIRD, scoreOfThird);
        } else if (mScore > scoreOfSecond) {
            rankMsg = "恭喜你獲得第二名!";
            editor.putInt(SCORE_OF_SECOND, (int) mScore);
            editor.putInt(SCORE_OF_THIRD, scoreOfSecond);
        } else if (mScore > scoreOfThird) {
            rankMsg = "恭喜你獲得第三名!";
            editor.putInt(SCORE_OF_THIRD, (int) mScore);
        } else {
            rankMsg = "加油啊,爭取進入排行榜!";
        }
        // 提交數據
        editor.commit();
    }

    @Override
    public void onDraw(Canvas canvas) {

        super.onDraw(canvas);
        if (mMode == READY) {
            initNewGame();
            setMode(RUNNING);
            app.getMediaPlayer().start();
        }

        if (mMode == LOSE) {
            // 繪製背景圖像
            Paint paint = new Paint();
            Bitmap background = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.lose);
            canvas.drawBitmap(background, 0, 0, paint);
        }
    }
}

12、在ui子包裏創建貪喫蛇界面類SnakeActivity

 

(1)貪喫蛇界面佈局文件activity_snake.xml

(2)貪喫蛇界面類SnakeActivity

/**
 * 功能:貪喫蛇界面
 * 作者:華衛
 * 日期:2017年1月1日
 */

/**
 * 貪喫蛇: 人人都可以享受的簡單遊戲。 
 * 這是經典遊戲“貪喫蛇”的一個實現。你控制一條在花園裏遊動着尋找蘋果的蛇。 
 * 當心,蛇吃了蘋果,不僅要變長,而且移動速度會更快,只要蛇撞牆就結束遊戲。
 */
package net.hw.snake.ui;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.widget.TextView;
import net.hw.snake.R;
import net.hw.snake.app.AppConstants;
import net.hw.snake.app.SnakeApplication;
import net.hw.snake.view.SnakeView;

public class SnakeActivity extends Activity implements OnGestureListener, AppConstants {
    /**
    * 貪喫蛇視圖
    */
    private SnakeView mSnakeView;
    /**
    * 手勢偵測器
    */
    private GestureDetector detector;
    /**
    * 貪喫蛇應用程序
    */
    private SnakeApplication app;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_snake);

        // 通過資源標識獲得控件實例
        mSnakeView = (SnakeView) findViewById(R.id.snake);

        // 獲取貪喫蛇應用程序對象
        app = (SnakeApplication) getApplication();
        // 初始化手勢偵測器
        detector = new GestureDetector(this, this);
        // 貪喫蛇視圖獲得焦點
        mSnakeView.requestFocus();

        // 判斷是否保存過遊戲示例狀態數據
        if (savedInstanceState == null) {
            // 剛剛啓動———開啓一次新遊戲
            mSnakeView.setMode(READY);
        } else {
            // 恢復貪喫蛇遊戲狀態數據
            Bundle map = savedInstanceState.getBundle(TAG);
            if (map != null) {
                mSnakeView.restoreState(map);
            } else {
                // 設置貪喫蛇視圖爲暫停模式
                mSnakeView.setMode(PAUSE);
            }
        }
    }

    /**
    * 暫停回調方法
    */
    @Override
    protected void onPause() {
        super.onPause();
        // 隨窗口暫停而將貪喫蛇遊戲視圖設置爲暫停模式
        mSnakeView.setMode(PAUSE);
        // 暫停背景音樂
        app.getMediaPlayer().pause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        // 存儲遊戲狀態數據
        outState.putBundle(TAG, mSnakeView.saveState());
    }

    /**
    * 銷燬回調方法
    */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止背景音樂
        app.getMediaPlayer().stop();
        // 釋放媒體播放器資源
        app.setMediaPlayer(null);
    }

    // 將窗口的觸碰事件交給手勢偵測器來處理
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return detector.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        // 手勢向上滑動
        if (e2.getY() < e1.getY() - 20) {
            if (mSnakeView.mMode == READY | mSnakeView.mMode == LOSE) {
                // 開始新遊戲
                mSnakeView.initNewGame();
                // 設置爲運行模式
                mSnakeView.setMode(RUNNING);
                // 播放音樂
                app.getMediaPlayer().start();
                // 更新遊戲界面
                mSnakeView.update();
            }

            if (mSnakeView.mMode == PAUSE) {
                mSnakeView.setMode(RUNNING);
                mSnakeView.update();
            }

            if (mSnakeView.mDirection != SOUTH) {
                mSnakeView.mNextDirection = NORTH;
            }
        }

        // 手勢向下滑動
        if (e2.getY() > e1.getY() + 20) {
            if (mSnakeView.mDirection != NORTH) {
                mSnakeView.mNextDirection = SOUTH;
            }

        }

        // 手勢向左滑動
        if (e2.getX() < e1.getX() - 20) {
            if (mSnakeView.mDirection != EAST) {
                mSnakeView.mNextDirection = WEST;
            }
        }

        // 手勢向右滑動
        if (e2.getX() > e1.getX() + 20) {
            if (mSnakeView.mDirection != WEST) {
                mSnakeView.mNextDirection = EAST;
            }
        }

        return true;
    }

    @Override
    public void onLongPress(MotionEvent e) {
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }
}

13、在ui子包裏創建選擇地圖界面類SelectMapActivity

 

(1)在values裏的styles.xml裏增加單選按鈕和按鈕的樣式定義

(2)選擇地圖界面佈局文件activity_select_map.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/mapback"
    android:gravity="center"
    android:orientation="vertical" >

    <RadioGroup
        android:id="@+id/rg_map"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp" >

        <RadioButton
            android:id="@+id/rb_o_map"
            style="@style/rb_style"
            android:checked="true"
            android:text="@string/o_map" />

        <RadioButton
            android:id="@+id/rb_i_map"
            style="@style/rb_style"
            android:text="@string/i_map" />

        <RadioButton
            android:id="@+id/rb_x_map"
            style="@style/rb_style"
            android:text="@string/x_map" />

        <RadioButton
            android:id="@+id/rb_z_map"
            style="@style/rb_style"
            android:text="@string/z_map" />

        <RadioButton
            android:id="@+id/rb_u_map"
            style="@style/rb_style"
            android:text="@string/u_map" />
    </RadioGroup>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:gravity="center_horizontal" >

        <Button
            android:id="@+id/btn_ok"
            style="@style/btn_style"
            android:onClick="doOK"
            android:text="@string/ok" />

        <Button
            android:id="@+id/btn_cancel"
            style="@style/btn_style"
            android:onClick="doCancel"
            android:text="@string/cancel" />
    </LinearLayout>

</LinearLayout>

(3)選擇地圖界面類SelectMapActivity

/**
 * 功能:選擇地圖
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.ui;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.RadioButton;
import net.hw.snake.R;
import net.hw.snake.app.AppConstants;
import net.hw.snake.app.SnakeApplication;

public class SelectMapActivity extends Activity implements AppConstants {
    /**
    * 口字型地圖的單選按鈕
    */
    private RadioButton rbOMap;
    /**
    * 工字型地圖的單選按鈕
    */
    private RadioButton rbIMap;
    /**
    * X型地圖的單選按鈕
    */
    private RadioButton rbXMap;
    /**
    * Z型地圖的單選按鈕
    */
    private RadioButton rbZMap;
    /**
    * U型地圖的單選按鈕
    */
    private RadioButton rbUMap;
    /**
    * 貪喫蛇應用程序
    */
    private SnakeApplication app;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_select_map);

        // 通過資源標識獲得界面控件實例
        rbOMap = (RadioButton) findViewById(R.id.rb_o_map);
        rbIMap = (RadioButton) findViewById(R.id.rb_i_map);
        rbXMap = (RadioButton) findViewById(R.id.rb_x_map);
        rbZMap = (RadioButton) findViewById(R.id.rb_z_map);
        rbUMap = (RadioButton) findViewById(R.id.rb_u_map);
        
        // 獲取貪喫蛇應用程序對象
        app = (SnakeApplication) getApplication();

        // 設置地圖單選按鈕初始狀態
        switch (app.getMapIndex()) {
        case O_SHAPE_MAP: // 口字型地圖
            rbOMap.setChecked(true);
            break;
        case I_SHAPE_MAP: // I字型地圖
            rbIMap.setChecked(true);
            break;
        case X_SHAPE_MAP: // X字型地圖
            rbXMap.setChecked(true);
            break;
        case Z_SHAPE_MAP: // Z字型地圖
            rbZMap.setChecked(true);
            break;
        case U_SHAPE_MAP: // U字型地圖
            rbUMap.setChecked(true);
            break;
        }       
    }
    
    /**
    * 確定按鈕單擊事件處理方法
    * 
    * @param view
    */
    public void doOK(View view) {
        // 根據單選按鈕選中狀態設置地圖索引
        if (rbOMap.isChecked()) {
            // 設置口字型地圖
            app.setMapIndex(O_SHAPE_MAP);
        } else if (rbIMap.isChecked()) {
            // 設置工字型地圖
            app.setMapIndex(I_SHAPE_MAP);
        } else if (rbXMap.isChecked()) {
            // 設置X字型地圖
            app.setMapIndex(X_SHAPE_MAP);
        } else if (rbZMap.isChecked()) {
            // 設置Z字型地圖
            app.setMapIndex(Z_SHAPE_MAP);
        } else if (rbUMap.isChecked()) {
            // 設置U字型地圖
            app.setMapIndex(U_SHAPE_MAP);
        }
        
        // 關閉當前界面
        finish();
    }
    
    /**
    * 取消按鈕單擊事件處理方法
    * 
    * @param view
    */
    public void doCancel(View view) {
        // 關閉當前界面
        finish();
    }
}

14、在ui子包裏創建設置速度界面類SetSpeedActivity

(1)設置速度界面佈局文件activity_set_speed.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/speedback"
    android:gravity="center"
    android:orientation="vertical" >

    <RadioGroup
        android:id="@+id/rg_speed"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <RadioButton
            android:id="@+id/rb_low_speed"
            style="@style/rb_style"
            android:checked="true"
            android:text="@string/low_speed" />

        <RadioButton
            android:id="@+id/rb_medium_speed"
            style="@style/rb_style"
            android:text="@string/medium_speed" />

        <RadioButton
            android:id="@+id/rb_high_speed"
            style="@style/rb_style"
            android:text="@string/high_speed" />
    </RadioGroup>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:gravity="center_horizontal" >

        <Button
            android:id="@+id/btn_ok"
            style="@style/btn_style"
            android:text="@string/ok"
            android:onClick="doOK" />

        <Button
            android:id="@+id/btn_cancel"
            style="@style/btn_style"
            android:text="@string/cancel" 
            android:onClick="doCancel"/>
    </LinearLayout>

</LinearLayout>

(2)設置速度界面類SetSpeedActivity

/**
 * 功能:設置速度
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.ui;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.RadioButton;
import net.hw.snake.R;
import net.hw.snake.app.AppConstants;
import net.hw.snake.app.SnakeApplication;

public class SetSpeedActivity extends Activity implements AppConstants{
    /**
    * 低速單選按鈕
    */
    private RadioButton rbLowSpeed;
    /**
    * 中速單選按鈕
    */
    private RadioButton rbMediumSpeed;
    /**
    * 高速單選按鈕
    */
    private RadioButton rbHighSpeed;
    /**
    * 貪喫蛇應用程序
    */
    private SnakeApplication app;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_set_speed);

        // 通過資源索引獲得界面控件實例
        rbLowSpeed = (RadioButton) findViewById(R.id.rb_low_speed);
        rbMediumSpeed = (RadioButton) findViewById(R.id.rb_medium_speed);
        rbHighSpeed = (RadioButton) findViewById(R.id.rb_high_speed);

        // 獲取貪喫蛇應用程序對象
        app = (SnakeApplication) getApplication();

        // 設置速度單選按鈕的初始狀態
        switch (app.getSpeedIndex()) {
        case LOW_SPEED: // 低速
            rbLowSpeed.setChecked(true);
            break;
        case MEDIUM_SPEED: // 中速
            rbMediumSpeed.setChecked(true);
            break;
        case HIGH_SPEED: // 高速
            rbHighSpeed.setChecked(true);
            break;
        }
    }
    
    /**
    * 確定按鈕單擊事件處理方法
    * 
    * @param view
    */
    public void doOK(View view) {
        // 設置速度索引
        if (rbLowSpeed.isChecked()) {
            // 設置低速
            app.setSpeedIndex(LOW_SPEED);
        } else if (rbMediumSpeed.isChecked()) {
            // 設置中速
            app.setSpeedIndex(MEDIUM_SPEED);
        } else if (rbHighSpeed.isChecked()) {
            // 設置高速
            app.setSpeedIndex(HIGH_SPEED);
        }
        // 關閉當前界面
        finish();
    }

    /**
    * 取消按鈕單擊事件處理方法
    * 
    * @param view
    */
    public void doCancel(View view) {
        // 關閉當前界面
        finish();
    }
}

15、在ui子包裏創建選擇背景音樂界面類SelectMusicActivity

(1)選擇背景音樂界面佈局文件activity_select_music.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/musicback"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="20dp" >

    <ListView
        android:id="@+id/lv_music"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" >
    </ListView>

</LinearLayout>

(2)音樂列表項模板music_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:gravity="center"
    android:textColor="#000000"
    android:textSize="20sp" />

(3)選擇背景音樂界面類SelectMusicActivity

/**
 * 功能:選擇背景音樂
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.ui;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import net.hw.snake.R;
import net.hw.snake.app.SnakeApplication;

public class SelectMusicActivity extends Activity {
    /**
    * 音樂數組
    */
    private String[] musics;
    /**
    * 數組適配器
    */
    private ArrayAdapter<String> adapter;
    /**
    * 音樂列表控件
    */
    private ListView lvMusic;
    /**
    * 貪喫蛇應用程序
    */
    private SnakeApplication app;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_select_music);

        // 通過資源索引獲得界面控件實例
        lvMusic = (ListView) findViewById(R.id.lv_music);

        // 獲取貪喫蛇應用程序對象
        app = (SnakeApplication) getApplication();

        // 初始化音樂數組
        musics = new String[] { "夢中的婚禮", "秋日的私語", "天空之城", "致愛麗絲", "貝爾加湖畔", "暗香" };
        // 創建數組適配器
        adapter = new ArrayAdapter<String>(this, R.layout.music_list_item, musics);
        // 給列表視圖對象設置適配器
        lvMusic.setAdapter(adapter);

        // 給音樂列表控件註冊監聽器
        lvMusic.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
                // 設置背景音樂索引
                app.setMusicIndex(position);
                // 彈出吐司提示用戶
                Toast.makeText(SelectMusicActivity.this, "您選擇了【" + musics[position] + "】作爲背景音樂!", Toast.LENGTH_LONG)
                        .show();
                // 關閉當前界面
                finish();
            }
        });
    }
}

16、在ui子包裏創建排行榜界面類TopListActivity

(1)排行榜界面佈局文件activity_top_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/toplistback"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        android:text="@string/first"
        android:textColor="#0000ff"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_second"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="@string/second"
        android:textColor="#0000ff"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_third"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="@string/third"
        android:textColor="#0000ff"
        android:textSize="20sp" />

    <Button
        android:id="@+id/btn_back"
        style="@style/btn_style"
        android:layout_marginTop="50dp"
        android:text="@string/back"
        android:onClick="doBack" />

</LinearLayout>

(2)在字符串資源文件strings.xml裏定義變量

(3)排行榜界面類TopListActivity

/**
 * 功能:排行榜
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.ui;

import net.hw.snake.R;
import net.hw.snake.app.AppConstants;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class TopListActivity extends Activity implements AppConstants {
    /**
    * 第一名標籤
    */
    private TextView tvFirst;
    /**
    * 第二名標籤
    */
    private TextView tvSecond;
    /**
    * 第三名標籤
    */
    private TextView tvThird;
    /**
    * 共享參數對象
    */
    private SharedPreferences sp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_top_list);

        // 通過資源索引獲得界面控件實例
        tvFirst = (TextView) findViewById(R.id.tv_first);
        tvSecond = (TextView) findViewById(R.id.tv_second);
        tvThird = (TextView) findViewById(R.id.tv_third);

        // 獲取共享參數對象
        sp = getSharedPreferences(TOP_LIST_FILE_NAME, ACCESS_MODE);

        // 在標籤上顯示前三名分數
        tvFirst.setText("第一名:" + sp.getInt(SCORE_OF_FIRST, 0));
        tvSecond.setText("第二名:" + sp.getInt(SCORE_OF_SECOND, 0));
        tvThird.setText("第三名:" + sp.getInt(SCORE_OF_THIRD, 0));     
    }
    
    /**
    * 返回按鈕單擊事件處理方法
    * 
    * @param view
    */
    public void doBack(View view) {
        // 關閉當前界面
        finish();
    }
}

17、在ui子包裏創建主界面類MainActivity

(1)主界面佈局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/mainback"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="100dp"
        android:text="@string/version"
        android:textColor="#ff0000"
        android:textSize="30sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:gravity="center"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn_start_game"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/start_game_button_selector"
            android:onClick="doStartGame" />

        <Button
            android:id="@+id/btn_select_map"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/select_map_button_selector"
            android:onClick="doSelectMap" />
    </LinearLayout>

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

        <Button
            android:id="@+id/btn_set_speed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/set_speed_button_selector"
            android:onClick="doSetSpeed" />

        <Button
            android:id="@+id/btn_select_music"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/select_music_button_selector"
            android:onClick="doSelectMusic" />
    </LinearLayout>

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

        <Button
            android:id="@+id/btn_top_list"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/top_list_button_selector"
            android:onClick="doTopList" />

        <Button
            android:id="@+id/btn_help"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/help_button_selector"
            android:onClick="doHelp" />
    </LinearLayout>

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

        <Button
            android:id="@+id/btn_about_author"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/about_author_button_selector"
            android:onClick="doAboutAuthor" />

        <Button
            android:id="@+id/btn_exit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/exit_game_button_selector"
            android:onClick="doExit" />
    </LinearLayout>

</LinearLayout>

(2)字符串資源文件strings.xml裏定義變量

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">貪喫蛇V1.0</string>
    <string name="version">Version1.0</string>
    <string name="o_map">口字型地圖</string>
    <string name="i_map">工字型地圖</string>
    <string name="x_map">X字型地圖</string>
    <string name="u_map">U字型地圖</string>
    <string name="z_map">Z字型地圖</string>
    <string name="low_speed">蛇移動速度:低速</string>
    <string name="medium_speed">蛇移動速度:中速</string>
    <string name="high_speed">蛇移動速度:高速</string>
    <string name="ok">確定</string>
    <string name="cancel">取消</string>
    <string name="first">第一名:</string>
    <string name="second">第二名:</string>
    <string name="third">第三名:</string>
    <string name="back">返回</string> 
    <string name="author_info">單位:瀘職院信息工程系\n作者:華衛\n電話:15892921170\n郵箱:[email protected]</string>
    <string name="game_help">貪喫蛇又名貪食蛇,是一款經典的小遊戲。玩家使用方向鍵操控一條長長的蛇不斷吞蘋果,同時蛇身隨着吞下的蘋果不斷長,當蛇頭撞到蛇身或障壁時遊戲結束。</string>
    <string name="action_settings">設置</string>

</resources>

(4)主界面類MainActivity

/**
 * 功能:貪喫蛇主控界面
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import net.hw.snake.R;

public class MainActivity extends Activity {    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_main);     
    }

    /**
    * 啓動遊戲
    * 
    * @param view
    */
    public void doStartGame(View view) {
        startActivity(new Intent(MainActivity.this, SnakeActivity.class));
    }

    /**
    * 選擇地圖
    * 
    * @param view
    */
    public void doSelectMap(View view) {
        startActivity(new Intent(MainActivity.this, SelectMapActivity.class));
    }

    /**
    * 設置速度
    * 
    * @param view
    */
    public void doSetSpeed(View view) {
        startActivity(new Intent(MainActivity.this, SetSpeedActivity.class));
    }

    /**
    * 選擇背景音樂
    * 
    * @param view
    */
    public void doSelectMusic(View view) {
        startActivity(new Intent(MainActivity.this, SelectMusicActivity.class));
    }

    /**
    * 排行榜
    * 
    * @param view
    */
    public void doTopList(View view) {
        startActivity(new Intent(MainActivity.this, TopListActivity.class));
    }

    /**
    * 幫助信息
    * 
    * @param view
    */
    public void doHelp(View view) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setIcon(R.drawable.ic_launcher);
        builder.setTitle("幫助信息");
        builder.setMessage(getResources().getString(R.string.game_help));
        builder.setPositiveButton("確定", null);
        builder.show();
    }

    /**
    * 關於作者
    * 
    * @param view
    */
    public void doAboutAuthor(View view) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setIcon(R.drawable.ic_launcher);
        builder.setTitle("關於作者");
        builder.setMessage(getResources().getString(R.string.author_info));
        builder.setPositiveButton("確定", null);
        builder.show();
    }

    /**
    * 退出程序
    * 
    * @param view
    */
    public void doExit(View view) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setIcon(R.drawable.snake);
        builder.setTitle("溫馨提示");
        builder.setMessage("您是否要退出【貪喫蛇】遊戲?");
        builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.exit(0);
            }
        });
        builder.setNegativeButton("否", null);
        builder.show();
    }
}

18、在ui子包裏修改啓動界面類SplashScreen

(1)啓動界面佈局文件activity_splash_screen.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/splashlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/splashback"
    android:gravity="center_vertical"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_version"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="170dp"
        android:text="@string/version"
        android:textColor="#ff00ff"
        android:textSize="50sp" />

    <TextView
        android:id="@+id/tv_author"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="100dp"
        android:text="@string/author"
        android:textColor="#0000ff"
        android:textSize="16sp" />

</LinearLayout>

(2)字符串資源文件strings.xml裏定義變量

(3)啓動界面類SplashScreenActivity

/**
 * 功能:啓動界面
 * 作者:華衛
 * 日期:2017年1月1日
 */
package net.hw.snake.ui;

import net.hw.snake.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;

public class SplashScreenActivity extends Activity {
    /**
    * 啓動畫面延遲時間
    */
    private final int DELAY_TIME = 3000;    

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 利用佈局資源文件設置用戶界面
        setContentView(R.layout.activity_splash_screen);        
        
        // 利用消息處理器延遲處理
        new Handler().postDelayed(new Runnable() {
            public void run() {
                // 跳轉到主控界面
                startActivity(new Intent(SplashScreenActivity.this,
                        MainActivity.class));
                // 關閉啓動界面
                finish(); 
            }
        }, DELAY_TIME);
    }
}

19、項目清單文件設置全部界面都是全屏顯示

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.hw.snake"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />

    <application
        android:name="net.hw.snake.app.SnakeApplication"
        android:icon="@drawable/snake"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
        <activity android:name="net.hw.snake.ui.SplashScreenActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="net.hw.snake.ui.MainActivity" />
        <activity android:name="net.hw.snake.ui.SnakeActivity" />
        <activity android:name="net.hw.snake.ui.SelectMapActivity" />
        <activity android:name="net.hw.snake.ui.SelectMusicActivity" />
        <activity android:name="net.hw.snake.ui.SelectSpeedActivity" />
        <activity android:name="net.hw.snake.ui.TopListActivity" >
        </activity>
    </application>

</manifest>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章