Java课程设计、安卓开发(AndroidStudio):JavaScript实现贪吃蛇小游戏(手机APK)

1 篇文章 0 订阅
1 篇文章 0 订阅

本人一直想找时间系统整理一下之前做过的一些实验,便于后续用到的时候可以尽快的使用,po出来也便于大家交流学习,有问题欢迎交流指正,与诸君共勉!

这次是Java的课程设计,现在看起来更像是一个安卓开发的课程设计。设计了一个贪吃蛇游戏,有友好的GUI界面和较为全面的功能。

实现效果

开始动画

开启GreegySnake后,显示开始动画,如图1。

开始动画停留三秒后自动进入登录界面,如图2。

图1 开始动画

 图2 登录界面

注册

下载GreedySnake软件并打开后,首次使用需要点击登陆界面的“没有账号?点此注册”按钮进入GamePlayer用户注册界面进行用户注册,如图3。

按提示输入账号:用户名/手机号/邮箱,并设置密码:数字组合,如图4。

注册成功后弹出提示:注册成功,并跳转至登录界面,如图5。

图3 注册界面

 图4 用户注册

图5 注册成功

登录

登陆界面输入用户名和密码,勾选“我同意用户服务协议和隐私政策”,点击登录按钮,进入游戏菜单界面,如图7。

图7 游戏菜单界面

贪吃蛇游戏

从游戏菜单界面点击“新游戏“,进入贪吃蛇游戏初始界面,如图9,小蛇初始身长为4。默认开始播放游戏背景音乐。

单击“开始游戏“按钮,进入游戏主界面,如图10。用户可点击“向右”、“向左”、“向上”、“向下”四个按钮控制小蛇爬动的方向,食物随机出现在游戏界面中,食物种类从下图六种水果中随机产生。

图8 六种食物种类

游戏中,默认背景音乐暂停,小蛇每吃到食物一次,会播放相应音效。

图9 游戏初始界面     

 图10 游戏中

游戏中,点击”游戏暂停“按钮,会播放按钮点击音效并默认继续播放背景音乐,屏幕中央提示”点击按钮,继续游戏“字样,如图11。

单击主界面右上角喇叭图标,图标更换为静音模式,如图12。按钮音效和背景音乐将切换至静音模式。

图11 游戏中点击“游戏暂停”按钮

图12 游戏切换为静音模式

游戏过程中,根据不同得分,将展示不同效果,具体规则如下:

小蛇吃到一次食物加10分,每吃到一个食物,播放win音效;

得分小于250,分数每增加50,播放一次鼓掌音效;

得分大于等于300,小于400,分数每增加20,播放一次鼓掌音效;

得分大于等于400,分数每增加10,播放一次鼓掌音效,屏幕中央显示提示字样“YOU ARE A WINNER!”,中央”游戏暂停”按钮字样变为“WIN”。

分数达到500,自动跳转最终胜利界面,如图13。

查看历史最高分数

每轮游戏过后,若本轮得分超过本用户历史最高分数,将自动更新历史最高分数。在游戏菜单界面点击“查看历史最高分数”可查看最高分数记录,如图14。

在游戏菜单界面点击“退出游戏“按钮,可返回GamePlayer登录界面。

二、数据说明

本客户端无服务器,故用户账户密码数据、历史最高分数数据、游戏界面静音状态数据均存放在手机本地文件与数据库中。

国密SM4加密

对于用户注册信息,将用户密码使用国家密码管理局发布的SM4.0密码算法进行加密后存储,增强用户信息安全性。在Android Studio平台模拟运行,注册后,可看到日志信息如图15,用户密码已被加密处理。

图15 注册账户Log信息

SQLite数据库存储

用户账号密码数据统一存储在snakeUser.db数据库的user表中。

图16 snakeUser.db数据库

文件存储

用户历史最高分数、静音按键状态信息分别存储在文件名为“用户名+scorefile.xml”、“用户名+VolumeFile.xml”的文件中,如图17。

图17 最高分数、音量文件信息

代码说明

 类说明

共设计16个Java文件:

Macro:用于定义游戏界面中的固定参数、文件名称等

User:用于创建用户信息

UserService:用于用户注册、登录功能实现

StartActivity:用于软件开始动画的3秒显示

MainActivity: 用于主游戏界面小蛇移动、游戏进行状态、静音状态、得分反应的逻辑控制;

MyDatabaseHelper:用于连接SQLite数据库,创建user表,存储用户信息

SharedPre:用于用户历史最高分数、静音按钮状态信息存储的文件操作

RegisterActivity:用于注册界面的显示及按钮事件响应

LoginActivity:用于登录界面的显示及按钮事件的响应

FinalwinActivity:用于用户最终胜利界面的显示

SM4:用于国密SM4算法的工具类

Encryption:用于国密SM4算法的加解密

Images:用于游戏主界面的小蛇、食物等动态构件绘制

ChooseActivity:游戏菜单界面的显示及按钮事件响应

SoundPlay:用于游戏中的按钮音效、吃到食物音效、游戏失败音效的短音频控制

BGMServer:用于游戏中的背景音乐(长音频)控制

共设计6个布局文件:

activity_beginning.xml:开始动画界面布局

activity_finalwin.xml:最终胜利界面布局

activity_login.xml:登录界面布局

activity_main.xml:游戏主界面布局

activity_register.xml:注册界面布局

activity_start_view.xml:游戏菜单界面布局

源代码

java文件

BGMServer.java
package com.example.android_snake;

import android.content.Context;
import android.media.MediaPlayer;

/**
 * 贪吃蛇背景音乐工具类
 */
public class BGMserver {
    private static MediaPlayer mp =null;
    public static void play(Context context, int resource){
        stop(context);
        mp = MediaPlayer.create(context, resource);
        mp.setLooping(true);
        mp.start();
    }
    public static void stop(Context context) {
        // TODO Auto-generated method stub
        if(mp!= null){
            mp.stop();
            mp.release();
            mp = null;
        }
    }

}
ChooseActivity.java
package com.example.android_snake;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import java.util.HashMap;


/**
 * The type Star view:游戏开始界面
 */
public class ChooseActivity extends AppCompatActivity implements View.OnClickListener {

    private Button Startbutton,Sbutton,Backbutton;
    private String usern;
    /**
     * The Shared:实例化存储对象
     */
    SharedPre shared;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_start_view);
        Init();

    }

    /**
     * Init:初始化该活动界面信息
     */
    void Init(){
        usern = LoginActivity.usern;
        Startbutton =findViewById(R.id.star_game);
        Sbutton=findViewById(R.id.MostBUtton);
        Backbutton=findViewById(R.id.BackButton);
        shared=new SharedPre(usern,this);
        //soundp = new SoundPlay();
        //soundp.initSoundPool();//初始化声音池
        Startbutton.setOnClickListener(this);
        Sbutton.setOnClickListener(this);
        Backbutton.setOnClickListener(this);
    }
    /**
     * onClick:该界面按钮监听事件
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.star_game:
                //sp.playSound(1, 0);//播放1号声音资源button,且播放一次
                Intent intent=new Intent(ChooseActivity.this,MainActivity.class);
                startActivity(intent);
                break;
            case R.id.BackButton:
                //sp.playSound(1, 0);//播放1号声音资源button,且播放一次
                finish();
                break;
            case R.id.MostBUtton:
                //sp.playSound(1, 0);//播放1号声音资源button,且播放一次
                Sbutton.setText(Macro.H_MOST_SCORE+shared.read());
                break;
        }
    }
}
SM4.java
package com.example.android_snake;


import java.util.Arrays;

public class SM4 {

    private static SM4 mSm4 = null;

    public static SM4 getInstance() {
        if (mSm4 == null) {
            mSm4 = new SM4();
        }
        return mSm4;
    }


    public static final int DECRYPT = 0; // 解密
    public static final int ENCRYPT = 1; // 加密
    public static final int ROUND = 32;
    private static final int BLOCK = 16;

    private byte[] Sbox = { (byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe,
            (byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6,
            0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67,
            (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3,
            (byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06,
            (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91,
            (byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43,
            (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4,
            (byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8,
            (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa,
            0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7,
            (byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83,
            0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8,
            0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda,
            (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56,
            (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1,
            (byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87,
            (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27,
            0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4,
            (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a,
            (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3,
            (byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15,
            (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4,
            (byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32,
            0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d,
            (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca,
            0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f,
            (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd,
            (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b,
            0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb,
            (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41,
            0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31,
            (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d,
            0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4,
            (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c,
            (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09,
            (byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0,
            0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79,
            (byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48 };

    private int[] CK = { 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
            0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5,
            0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81,
            0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d,
            0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
            0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25,
            0x2c333a41, 0x484f565d, 0x646b7279 };

    private int Rotl(int x, int y) {
        return x << y | x >>> (32 - y);
    }

    private int ByteSub(int A) {
        return (Sbox[A >>> 24 & 0xFF] & 0xFF) << 24
                | (Sbox[A >>> 16 & 0xFF] & 0xFF) << 16
                | (Sbox[A >>> 8 & 0xFF] & 0xFF) << 8 | (Sbox[A & 0xFF] & 0xFF);
    }

    private int L1(int B) {
        return B ^ Rotl(B, 2) ^ Rotl(B, 10) ^ Rotl(B, 18) ^ Rotl(B, 24);
        // return B^(B<<2|B>>>30)^(B<<10|B>>>22)^(B<<18|B>>>14)^(B<<24|B>>>8);
    }

    private int L2(int B) {
        return B ^ Rotl(B, 13) ^ Rotl(B, 23);
        // return B^(B<<13|B>>>19)^(B<<23|B>>>9);
    }

    void SMS4Crypt(byte[] Input, byte[] Output, int[] rk) {
        int r, mid;
        int[] x = new int[4];
        int[] tmp = new int[4];
        for (int i = 0; i < 4; i++) {
            tmp[0] = Input[0 + 4 * i] & 0xff;
            tmp[1] = Input[1 + 4 * i] & 0xff;
            tmp[2] = Input[2 + 4 * i] & 0xff;
            tmp[3] = Input[3 + 4 * i] & 0xff;
            x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
            // x[i]=(Input[0+4*i]<<24|Input[1+4*i]<<16|Input[2+4*i]<<8|Input[3+4*i]);
        }
        for (r = 0; r < 32; r += 4) {
            mid = x[1] ^ x[2] ^ x[3] ^ rk[r + 0];
            mid = ByteSub(mid);
            x[0] = x[0] ^ L1(mid); // x4

            mid = x[2] ^ x[3] ^ x[0] ^ rk[r + 1];
            mid = ByteSub(mid);
            x[1] = x[1] ^ L1(mid); // x5

            mid = x[3] ^ x[0] ^ x[1] ^ rk[r + 2];
            mid = ByteSub(mid);
            x[2] = x[2] ^ L1(mid); // x6

            mid = x[0] ^ x[1] ^ x[2] ^ rk[r + 3];
            mid = ByteSub(mid);
            x[3] = x[3] ^ L1(mid); // x7
        }

        // Reverse
        for (int j = 0; j < 16; j += 4) {
            Output[j] = (byte) (x[3 - j / 4] >>> 24 & 0xFF);
            Output[j + 1] = (byte) (x[3 - j / 4] >>> 16 & 0xFF);
            Output[j + 2] = (byte) (x[3 - j / 4] >>> 8 & 0xFF);
            Output[j + 3] = (byte) (x[3 - j / 4] & 0xFF);
        }
    }

    private void SMS4KeyExt(byte[] Key, int[] rk, int CryptFlag) {
        int r, mid;
        int[] x = new int[4];
        int[] tmp = new int[4];
        for (int i = 0; i < 4; i++) {
            tmp[0] = Key[0 + 4 * i] & 0xFF;
            tmp[1] = Key[1 + 4 * i] & 0xff;
            tmp[2] = Key[2 + 4 * i] & 0xff;
            tmp[3] = Key[3 + 4 * i] & 0xff;
            x[i] = tmp[0] << 24 | tmp[1] << 16 | tmp[2] << 8 | tmp[3];
        }
        x[0] ^= 0xa3b1bac6;
        x[1] ^= 0x56aa3350;
        x[2] ^= 0x677d9197;
        x[3] ^= 0xb27022dc;
        for (r = 0; r < 32; r += 4) {
            mid = x[1] ^ x[2] ^ x[3] ^ CK[r + 0];
            mid = ByteSub(mid);
            rk[r + 0] = x[0] ^= L2(mid); // rk0=K4

            mid = x[2] ^ x[3] ^ x[0] ^ CK[r + 1];
            mid = ByteSub(mid);
            rk[r + 1] = x[1] ^= L2(mid); // rk1=K5

            mid = x[3] ^ x[0] ^ x[1] ^ CK[r + 2];
            mid = ByteSub(mid);
            rk[r + 2] = x[2] ^= L2(mid); // rk2=K6

            mid = x[0] ^ x[1] ^ x[2] ^ CK[r + 3];
            mid = ByteSub(mid);
            rk[r + 3] = x[3] ^= L2(mid); // rk3=K7
        }

        // 解密时轮密钥使用顺序:rk31,rk30,...,rk0 //解密时轮密钥反向
        if (CryptFlag == DECRYPT) {
            for (r = 0; r < 16; r++) {
                mid = rk[r];
                rk[r] = rk[31 - r];
                rk[31 - r] = mid;
            }
        }
    }

    public int sms4(byte[] in, int inLen, byte[] key, byte[] out, int CryptFlag) {
        int point = 0;
        int[] round_key = new int[ROUND];
        SMS4KeyExt(key, round_key, CryptFlag);
        byte[] input = new byte[16];
        byte[] output = new byte[16];

        while (inLen >= BLOCK) {
            input = Arrays.copyOfRange(in, point, point + 16);
            SMS4Crypt(input, output, round_key);
            System.arraycopy(output, 0, out, point, BLOCK);
            inLen -= BLOCK;
            point += BLOCK;
        }
        return 0;
    }
}
Encryption.java
package com.example.android_snake;

import android.util.Log;

public class Encryption {

    /**
     * SM4加解密封装
     * @param data 传入参数
     * @param flag 加密为1&解密为0
     * @param key 16字节密钥
     * @return 加解密结果
     */
    public static String deal(String data, int flag, byte[] key)
    {
        StringBuilder builder = new StringBuilder();
        SM4 sm4 = SM4.getInstance();
        String strData = "";
        byte[] bInData;
        byte[] bOutData = new byte[16];
        int nIndex;
        int nDataLen = data.length();
        int nCount = 0;
        int nLastLen = nDataLen%32;
        if (nLastLen > 0) {
            nCount = nDataLen/32 + 1;
            if (flag ==0) {
                Log.i("SM4", "数据长度不正确,需解密数据应是32的倍数");
            }
        } else {
            nCount = nDataLen/32;
        }
        for (int i = 0; i < nCount; i++) {
            if ((i+1) == nCount && nLastLen > 0) {
                nIndex = i*32;
                strData = data.substring(nIndex, nIndex+nLastLen);
                strData = zero(strData);
                bInData = hexStringToBytes(strData);
                sm4.sms4(bInData,bInData.length,key,bOutData,flag);
                strData = bytesToHexString(bOutData,0,16);
                builder.append(strData);
            } else {
                nIndex = i*32;
                strData = data.substring(nIndex, nIndex+32);
                bInData = hexStringToBytes(strData);
                sm4.sms4(bInData,bInData.length,key,bOutData,flag);
                strData = bytesToHexString(bOutData,0,16);
                builder.append(strData);
            }
        }
        return builder.toString();
    }

    /**
     * 补位方法,SM4 分块加密 ,每块16字节,此处不足补0. 扩展说明:有得规则是首字节补80,之后补00,还有区分左补和右补,如果此加解密不能满足要求,可以往这方面去做
     * @param data 需补位的数据
     * @return 补位后的字符串
     */
    public  static String zero(String data) {
        StringBuilder builder = new StringBuilder();
        builder.append(data);
        int nLen = data.length();
        while (nLen < 32) {
            builder.append("0");
            nLen++;
        }
        return builder.toString();
    }

    /**
     *  hexString转byte[]数组
     * @param str 字符串,原串为“112233”
     * @return 转换后的数据, 转换后为 0x11,0x22,0x33
     */
    public static byte[] hexStringToBytes(String str) {
        int charIndex, value;
        int datalen = str.length() / 2;
        byte[] data = new byte[datalen];
        for (int i = 0; i < datalen; i++) {
            charIndex = i * 2;
            value = Integer.parseInt(str.substring(charIndex, charIndex + 2), 16);
            data[i] = (byte) value;
        }

        return data;
    }

    /**
     * byte[]数组转hexString
     * @param data 需转换的数组 0x11,0x22,0x33
     * @param offset 需转换开始位置
     * @param len 需转换长度
     * @return 转换后的字符传为“112233”
     */
    public static String bytesToHexString(byte[] data, int offset, int len) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++)
            sb.append(String.format("%02X", data[i + offset] & 0xff));
        return sb.toString();
    }

}
FinalwinActivity.java
package com.example.android_snake;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

import androidx.appcompat.app.AppCompatActivity;

public class FinalwinActivity extends AppCompatActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //隐藏标题栏以及状态栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_finalwin);
    }
}
Images.java
package com.example.android_snake;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
import android.view.WindowManager;
import android.util.DisplayMetrics;

import java.util.Random;

public class Images extends View {

    private int BitmapX,BitmapY;
    private int FoodX,FoodY;
    private int[] snakeX =new int[Macro.MAX_LENGTH];
    private int[] snakeY =new int[Macro.MAX_LENGTH];
    private int length;
    private int direction;
    private boolean islive;//是否存活 状态
    private Bitmap rbitmaphead,lbitmaphead,ubitmaphead,dbitmaphead,bitmapbody,bitmapfood;//四个方向的头及蛇的身体、食物
    private Bitmap bitmapfoods[] = new Bitmap [6];
    private Bitmap bling;

    public boolean isIslive() {
        return islive;
    }

    public void setIslive(boolean islive) {
        this.islive = islive;
    }

    public int getFoodX() {
        return FoodX;
    }

    public void setFoodX(int foodX) {
        FoodX = foodX;
    }
    public void setBitmapfood(int x){
        if(x < 6 && x >= 0)
        bitmapfood = bitmapfoods[x];
    }

    public int getFoodY() {
        return FoodY;
    }

    public void setFoodY(int foodY) {
        FoodY = foodY;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getBitmapX() {
        return BitmapX;
    }

    public void setBitmapX(int bitmapX) {
        BitmapX = bitmapX;
    }

    public int getBitmapY() {
        return BitmapY;
    }

    public void setBitmapY(int bitmapY) {
        BitmapY = bitmapY;
    }

    public Images(Context context) {
        super(context);
    }

    /**
     * 小蛇初始化
     */
    public void Init(){

        BitmapX=Macro.INIT_SNAKEX;
//        BitmapX = MainActivity.screenWidth/2; //手机屏幕宽度的一半
        BitmapY=Macro.INIT_SNAKEY;
        FoodX=Macro.INIT_FOODX;
        FoodY=Macro.INIT_FOODY;//初始坐标
        Random r = new Random(); //用于生成随机数
        int randomN = r.nextInt(3);//随机生成[0,3)的整数
        snakeX[0]=BitmapX;
        snakeY[0]=BitmapY;//蛇头的位置
        length =Macro.INIT_LENGHT;//蛇的初始长度
        islive=true;

        switch (randomN){//设置蛇的初始状态
            case 0://初始方向向右
                direction=Macro.Right;//蛇的初始方向
                //画出初始的蛇身
                for(int i = 1; i< length; i++){
                    snakeX[i]= snakeX[i-1]-Macro.UNIT;
                    snakeY[i]= snakeY[0];   //初始状态
                }
                break;

            case 1://初始方向向上
                direction=Macro.Up;//蛇的初始方向
                //画出初始的蛇身
                for(int i = 1; i< length; i++){
                    snakeY[i]= snakeY[i-1]+Macro.UNIT;
                    snakeX[i]= snakeX[0];   //初始状态
                }
                break;
            case 2://初始方向向下
                direction=Macro.Down;//蛇的初始方向
                //画出初始的蛇身
                for(int i = 1; i< length; i++){
                    snakeY[i]= snakeY[i-1]-Macro.UNIT;
                    snakeX[i]= snakeX[0];   //初始状态
                }
                break;
        }

    }


    /**
     * 引用小蛇图片,并设置图片大小
     */
    public void Size(){

        //避免大图片解析问题--头部
        BitmapFactory.Options headoptions = new BitmapFactory.Options();
        //调整
        headoptions.inSampleSize= Macro.IMAGE_HEAD_SIZE;   //蛇头缩放倍数
        headoptions.inJustDecodeBounds=false;

        //避免大图片解析问题--身体
        BitmapFactory.Options bodyoptions = new BitmapFactory.Options();
        bodyoptions.inSampleSize=Macro.IMAGE_BODY_SIZE;        //蛇身的缩放倍数
        bodyoptions.inJustDecodeBounds=false;


        //六种食物随机产生
        bitmapfoods[0]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food);
        bitmapfoods[1]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food1);
        bitmapfoods[2]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food2);
        bitmapfoods[3]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food3);
        bitmapfoods[4]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food4);
        bitmapfoods[5]= BitmapFactory.decodeResource(this.getResources(),R.drawable.food50);

        rbitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.rhead0, headoptions); //
        ubitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.uhead0, headoptions);
        lbitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.lhead0, headoptions);
        dbitmaphead= BitmapFactory.decodeResource(this.getResources(),R.drawable.dhead0, headoptions);
        bitmapbody= BitmapFactory.decodeResource(this.getResources(),R.drawable.body_0, bodyoptions);
        Random r = new Random(); //用于生成随机数
        int randomN = r.nextInt(6);//随机生成[0,6)的整数
        bitmapfood= bitmapfoods[randomN];

    }


    /**
     * 画蛇,将蛇画在屏幕上 canvas用于绘制背景的画布
     * Canvas 画布
     * Paint 画笔
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);      //Canvas(画纸,通常称为画布)
        Paint paint=new Paint();   //生成一个画笔对象
        //画食物

        /**
         * @param bitmap The bitmap to be drawn
         * @param left The position of the left side of the bitmap being drawn
         * @param top The position of the top side of the bitmap being drawn
         * @param paint The paint used to draw the bitmap (may be null)
         */


        canvas.drawBitmap(bitmapfood, FoodX, FoodY, paint);//画第一食物, 第一个参数为要绘制的bitmap对象,第四个参数为Paint对象。


        snakeX[0]=BitmapX;
        snakeY[0]=BitmapY;
        switch (direction){
            case Macro.Right:
                canvas.drawBitmap(rbitmaphead, snakeX[0], snakeY[0],paint);//蛇头往右,读取x、y坐标,填充画笔
                //canvas.drawBitmap(bling, snakeX[0], snakeY[0],paint);
                break;
            case Macro.Up:
                canvas.drawBitmap(ubitmaphead, snakeX[0], snakeY[0],paint);
               // canvas.drawBitmap(bling, snakeX[0], snakeY[0],paint);//蛇头往右,读取x、y坐标,填充画笔
                break;
            case Macro.Left:
                canvas.drawBitmap(lbitmaphead, snakeX[0], snakeY[0],paint);
                break;
            case Macro.Down:
                canvas.drawBitmap(dbitmaphead, snakeX[0], snakeY[0],paint);
                break;
        }
        //画出小蛇身体的位置
        for(int i = 1; i< length; i++) {
            canvas.drawBitmap(bitmapbody, snakeX[i], snakeY[i], paint);
        }
        //小蛇咬住身体了 生存位置零
        for(int i = 1; i< length; i++){
            if(snakeX[0]== snakeX[i]&& snakeY[0]== snakeY[i]){
                islive=false;
            }
        }
        //身体从最后一节向前一节位置移动
        for(int i = length; i>0; i--){
            snakeX[i]= snakeX[i-1];
            snakeY[i]= snakeY[i-1];
        }
        /**
         * Returns true if this bitmap has been recycled. If so, then it is an error
         * to try to access its pixels, and the bitmap will not draw.
         **如果该位图已被回收,则返回true。如果是,则是false
         *             *试图访问其像素,位图将不会绘制。
         *             *如果位图已经被回收,返回true
         * @return true if the bitmap has been recycled
         */

        //图片回收
        if(rbitmaphead.isRecycled()){
            rbitmaphead.recycle();
        }
        if(bitmapbody.isRecycled()){
            bitmapbody.recycle();
        }
        if(ubitmaphead.isRecycled()){
            rbitmaphead.recycle();
        }
        if(lbitmaphead.isRecycled()){
            rbitmaphead.recycle();
        }
        if(dbitmaphead.isRecycled()){
            rbitmaphead.recycle();
        }
    }
}
LoginActivity.java
package com.example.android_snake;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class LoginActivity extends AppCompatActivity{
    /**
     * 定义私有成员文本 账号密码etc
     */
    private EditText username;
    private EditText password;
    private int remarks;//初始值为0
    private Button Loginbutton; //登录按钮
    private Button goRegisterButton;//打开注册界面
    static public String usern;  //工具字符串,用于用户分数信息存储文件命名
    private Button Rbtlogin;
    boolean agree = false;//是否同意用户协议 标志位

    private void findViews(){
        username = (EditText) findViewById(R.id.username_login);
        password = (EditText) findViewById(R.id.password_login);
        //remarks = 0;//初始化为0
        Loginbutton = (Button) findViewById(R.id.bt_login);
        goRegisterButton =(Button) findViewById(R.id.bt_goRegister);
        Rbtlogin = (Button) findViewById(R.id.rbt_login);
    }

    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);//即activity_login.xml
        findViews();


        //设置按钮点击监听器
        Rbtlogin.setOnClickListener(new View.OnClickListener() {
            //按钮点击
            @Override
            public void onClick(View view) {

                if (view.getId() == R.id.rbt_login) {//同意协议
                    Log.i("TAG","同意用户协议");
                    agree = true;
                }
            }
        });

        Loginbutton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //MainActivity.playSound();
                if(!agree) {//未勾选同意用户协议
                    Toast.makeText(LoginActivity.this,"请先勾选同意用户协议",Toast.LENGTH_LONG).show();
                    return;
                }
                String name = username.getText().toString().trim();
                //System.out.println(name);
                String pass = password.getText().toString().trim();
                //System.out.println(pass);

                //对密码进行国密SM4加密,密文与用户数据库中数据表字段比较
                String strKey = "0123456789abcdeffedcba9876543210";//密钥
                byte[] bKey = Encryption.hexStringToBytes(strKey);
                String CPass = Encryption.deal(pass,SM4.ENCRYPT,bKey);//密码加密后的密文CPass

                Log.i("TAG","username_"+name+"_ CPassword_"+CPass);

                UserService uService = new UserService(LoginActivity.this);
                boolean flag = uService.login(name,CPass);
                if(flag){
                    Log.i("TAG","登陆成功");
                    Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_LONG).show();
                    usern = name;//便于用户最高分数文件存储 ,为了安全性,只赋值用户名
                    //登录成功,跳转游戏选择界面
                    Intent intent = new Intent(LoginActivity.this,ChooseActivity.class);
                    startActivity(intent);
                }else{
                    Log.i("TAG","登陆失败");
                    Toast.makeText(LoginActivity.this,"登录失败",Toast.LENGTH_LONG).show();
                }
            }
        });

        goRegisterButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {//跳转注册界面
                startActivity(new Intent(LoginActivity.this,RegisterActivity.class));

            }
        });
    }

}

Macro.java
package com.example.android_snake;

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;

/**
 * The type Macro:宏的定义
 */
public class Macro {
    /**
     * The constant Up:定义小蛇头部方向向上
     */
    public static final int Up=0x11;
    /**
     * The constant :定义小蛇头部方向向下
     */
    public static final int Down=0x12;
    /**
     * The constant Left:定义小蛇头部方向向左
     */
    public static final int Left=0x13;
    /**
     * The constant Right:定义小蛇头部方向向右
     */
    public static final int Right=0x14;
    /**
     * The constant UNIT:定义小蛇移动的基本单位
     */
    public static final int UNIT=50;
    /**
     * The constant LEAST_SIZE:小蛇生活区最小单位
     */
    public static final int LEAST_SIZE=1;
    /**
     * The constant DELAY:定时器响应run延时
     */
    public static final int DELAY=100;
    /**
     * The constant PERIOD:定时器启动的间隔时间
     */
    public static final int PERIOD=300;
    /**
     * The constant INIT_SNAKEX:小蛇初始头部的X坐标
     */
//    public static final int INIT_SNAKEX =600;
    public static final int INIT_SNAKEX =MainActivity.screenWidth/2;
    /**
     * The constant INIT_SNAKEY:小蛇初始头部的Y坐标
     */
    public static final int INIT_SNAKEY =400;
    /**
     * The constant INIT_FOODX:食物初始的X坐标
     */
    public static final int INIT_FOODX=400;
    /**
     * The constant INIT_FOODY:食物初始的Y坐标
     */
    public static final int INIT_FOODY=500;
    /**
     * The constant INIT_LENGHT:小蛇初始长度
     */
    public static final int INIT_LENGHT=4;
    /**
     * The constant IMAGE_HEAD_SIZE:小蛇头图片缩小倍数
     */
    //public static final int IMAGE_HEAD_SIZE=20;
    public static final int IMAGE_HEAD_SIZE=1;
    /**
     * The constant IMAGE_BODY_SIZE:小蛇身体图片缩小倍数
     */
    //public static final int IMAGE_BODY_SIZE=46;
    public static final int IMAGE_BODY_SIZE=1;
    /**
     * The constant SCORE_UNIT:每次加分
     */
    public static final int SCORE_UNIT=10;
    /**
     * The constant MAX_LENGTH:小蛇最大长度
     */
    public static final int MAX_LENGTH =900;
    /**
     * The constant SPEED:小蛇的初始速度
     */
    public static final int SPEED=20;
    /**
     * The constant FOOD:小蛇吃到食物就会加速的基本单位
     */
    public static final int FOOD=40;
    /**
     * The constant MAX_SPEED:小蛇移动的最大速度
     */
    public static final int MAX_SPEED =270;
    /**
     * The constant SCORE:此局的分数显示
     */
    public static final String SCORE="当前分数:";
    /**
     * The constant MOST_SCORE:历史最高分数显示
     */
    public static final String MOST_SCORE="最高分数:";
    /**
     * The constant H_MOST_SCORE:主界面查询分数显示
     */
    public static final String H_MOST_SCORE="最高分数(点击更新):";
    /**
     * The constant Pname:分数存储文件名统一名称后缀
     */
    public static final String Pname="scorefile";
    /**
     * The constant Pname:静音选中存储文件名统一名称后缀
     */
    public static final String PnameFlag="Volumefile";


    /**
     * 获得屏幕的宽度
     * @param ctx
     * @return
     */
    public static int getScreenWidth(Context ctx) {
        // 从系统服务中获取窗口管理器
        WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        // 从默认显示器中获取显示参数保存到dm对象中
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.widthPixels; // 返回屏幕的宽度数值

    }

    /**
     * 获得屏幕的高度
     */
    public static int getScreenHeight(Context ctx) {
        // 从系统服务中获取窗口管理器
        WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        // 从默认显示器中获取显示参数保存到dm对象中
        wm.getDefaultDisplay().getMetrics(dm);
        return dm.heightPixels; // 返回屏幕的高度数值
    }

}
MainActivity.java
package com.example.android_snake;

import static com.example.android_snake.BGMserver.play;
import static com.example.android_snake.BGMserver.stop;

import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

/**
 * The type Main activity:小蛇移动及方向控制逻辑处理
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private String usern;
    private FrameLayout Living_Space;
    private boolean isstart =false;
    private boolean volumeflag = true;
    private Timer timer;
    private Button BT_isstar,BT_RIGHT,BT_UP,BT_DOWN,BT_LEFT,BT_Volume;
    private TextView prompt;
    private TextView ScoreView;
    private int direction;
    private int weight,height;
    public static int screenWidth,screenHeight;
    private int countVolume;//计数点击音量按键的次数 判断奇偶性
    private boolean islive;
    private int score,time,mostscore;
    Images images;
    MoveSnake movesnake;
    SharedPre shared;
    private Handler handler;
   // RelativeLayout frameLayout_add_imageview;

    SoundPool sp;//声明SoundPool的引用
    HashMap<Integer, Integer> hm;//声明HashMap来存放声音文件
    int currStaeamId;//当前正播放的streamId

    private void initSoundPool() {//初始化声音池
        sp = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);//创建SoundPool对象
        hm = new HashMap<Integer, Integer>();//创建HashMap对象
        //加载声音文件,并且设置为1号 2号 3号声音放入hm中
        hm.put(1, sp.load(this, R.raw.button0, 1));
        hm.put(2, sp.load(this, R.raw.win, 1));
        hm.put(3, sp.load(this, R.raw.fail, 1));
        hm.put(4, sp.load(this, R.raw.applause, 1));

    }

    private void playSound(int sound, int loop) {//获取AudioManager引用
        AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        //获取当前音量
        float streamVolumeCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC);
        //获取系统最大音量
        float streamVolumeMax = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        //计算得到播放音量
        float volume = streamVolumeCurrent / streamVolumeMax;
        //调用SoundPool的play方法来播放声音文件
        currStaeamId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
    }


    //BGM设置工具函数
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        stop(this);
    }
    //BGM设置工具函数
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        play(this, R.raw.bgm);
    }

    class MoveSnake extends Thread{
        @SuppressLint("HandlerLeak")
       // @Override
        public void run() {
            Looper.prepare();//调用 Looper.prepare()来给线程创建消息循环
          handler=new Handler(){
                public void handleMessage(Message msg) {
                    if(isstart ==true&& islive==true){
                        //方向控制
                        if(msg.what==Macro.Right) {
                            images.setBitmapX(images.getBitmapX() + Macro.UNIT);//The constant UNIT:定义小蛇移动的基本单位,x轴+一个移动单位
                            images.setBitmapY(images.getBitmapY());
                        }
                        if(msg.what==Macro.Up) {
                            images.setBitmapY(images.getBitmapY()-Macro.UNIT);
                            images.setBitmapX(images.getBitmapX());
                        }
                        if(msg.what==Macro.Left) {
                            images.setBitmapX(images.getBitmapX()-Macro.UNIT);
                            images.setBitmapY(images.getBitmapY());
                        }
                        if(msg.what==Macro.Down) {
                            images.setBitmapY(images.getBitmapY()+Macro.UNIT);
                            images.setBitmapX(images.getBitmapX());
                        }
                        //吃掉食物处理
                        if((images.getBitmapX()==images.getFoodX())&&(images.getBitmapY()==images.getFoodY())){

                            //加吃到食物的光效
//                            ImageView imageView = new ImageView(MainActivity.this);
//                            imageView.setImageResource(R.drawable.bling);
//                            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(100,
//                                    30);//为添加图片的大小
//                            imageView.setLayoutParams(params);
//                            //imageView.setBackgroundColor();
//                            frameLayout_add_imageview.addView(imageView);

                           // if(shared.readVolumeFlag()==1) playSound(2, 0);//播放2号声音资源win,且播放一次

                            if(volumeflag) playSound(2, 0);//播放2号声音资源win,且播放一次
                            images.setLength(images.getLength()+1);//蛇身长度加1
                            int FoodX=(int)(Math.random()*(weight/Macro.UNIT))*Macro.UNIT; //食物随机位置出现
                            int FoodY=(int)(Math.random()*(height/Macro.UNIT))*Macro.UNIT;
                            if(FoodX==images.getBitmapX()&&FoodY==images.getBitmapY()){
                                FoodX= FoodX+Macro.UNIT;
                                FoodY= FoodY-Macro.UNIT;
                            }
                            Random r = new Random(); //用于生成随机数
                            int randomN = r.nextInt(6);//随机生成[0,6)的整数
                            images.setBitmapfood(randomN);//随机生成食物种类
                            images.setFoodX(FoodX);
                            images.setFoodY(FoodY);
                            score+=Macro.SCORE_UNIT;
                            if(score%Macro.FOOD==0){
                                timer.cancel();
                                if(time<=Macro.MAX_SPEED)
                                    time+=Macro.SPEED;
                                StartView(time);  //循环任务, sendmessage
                            }
                        }
                        images.invalidate();//invalidate()方法是用来刷新重绘当前的View的,如果当前View的布局尺寸、位置没有变化,仅仅是绘制内容变化了,那么我们就可以调用invalidate()方法。

                        //线程中更新控件,用来计分
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                ScoreView.setText(Macro.SCORE+String.valueOf(score));
                                if(shared.readVolumeFlag()==0) onPause();
                                //if(!volumeflag)onPause();//检测到静音设置,停止播放背景音

                                if(score % 50 == 0 && score > 0 && score < 250 && shared.readVolumeFlag()==1) //250分以下,每满50分 掌声响起一次
                                    playSound(4, 0);//播放2号声音资源applause,且播放一次
                                if(score >= 300 && score < 400 && score%20==0 && shared.readVolumeFlag()==1) //分数满300分,小于400分 每20分 掌声响起一次
                                {
                                    playSound(4, 0);//播放声音资源applause,且播放一次
                                }
                                if(score >= 400  ) //分数大于四百 提示胜利祝贺 每10分掌声响一次
                                {
                                    if(shared.readVolumeFlag()==1) playSound(4, 0);//播放2号声音资源applause,且播放一次
                                    prompt.setText(R.string.congratulations);//屏幕提示:YOU ARE A WINNER!
                                    prompt.setVisibility(View.VISIBLE);
                                    BT_isstar.setText("WIN!");

                                }
                                if(score == 500 ) //分数等于五百 提示奖励祝贺
                                {
                                    //游戏胜利,跳转奖励界面!
                                    Intent intent = new Intent(MainActivity.this,FinalwinActivity.class);
                                    startActivity(intent);
                                }
                            }
                        });

                        //死亡判断
                        if(images.getBitmapX()>=weight||images.getBitmapX()<0
                                ||images.getBitmapY()<0||images.getBitmapY()>=height
                                ||images.isIslive()==false){
                            islive=false;
                            //小蛇死亡提醒
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    if(shared.readVolumeFlag()==1) playSound(3, 0);//播放3号声音资源fail,且播放一次
                                    BT_isstar.setText("重新开始");
                                    prompt.setText(R.string.promptrestar);
                                    prompt.setVisibility(View.VISIBLE);
                                    if(score>mostscore)
                                        mostscore=score;
                                        shared.save(usern,mostscore);//创分数新纪录 存入文件
                                }
                            });
                        }
                    }
                    super.handleMessage(msg);
                }


            };
            Looper.loop();
        }
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MoveSnake movesnake =new MoveSnake();
        movesnake.start();     //启动一个线程
        Init();
        StartView(time);

        if(shared.readVolumeFlag()==1)onResume();//播放BGM


        screenWidth = Macro.getScreenWidth(MainActivity.this);
        screenHeight = Macro.getScreenHeight(MainActivity.this);
        //将图片加载到布局中
        Living_Space.addView(images);
        //获取活动空间的宽和高
        Living_Space.post(new Runnable() {
            @Override
            public void run() {
                weight=Living_Space.getWidth();
                height=Living_Space.getHeight();
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
        timer.cancel();
    }

    /**
     * Init:初始化该界面控件及参数
     */
//初始化
    void Init(){

        initSoundPool();//初始化声音池的方法


        usern = LoginActivity.usern;
        Living_Space=findViewById(R.id.framelayout);
        BT_isstar=findViewById(R.id.isstar);
        BT_RIGHT=findViewById(R.id.Right);
        BT_UP=findViewById(R.id.Up);
        BT_DOWN=findViewById(R.id.Down);
        BT_LEFT=findViewById(R.id.Left);
        BT_Volume = findViewById(R.id.Volume);
        prompt=findViewById(R.id.prompt);
        ScoreView=findViewById(R.id.Score);
        TextView mostScore = findViewById(R.id.MostScore);

//        if(shared.readVolumeFlag()==1){
//            volumeflag = true;
//            //BT_Volume.setBackground(R.drawable.volumeon);
//        }else{
//            volumeflag = false;
//            BT_Volume.setBackgroundDrawable(getResources().getDrawable(R.drawable.volumeoff));//背景设置为静音
//        }

        countVolume = 0;
        images=new Images(MainActivity.this);
        shared=new SharedPre(usern,MainActivity.this);
        direction=Macro.Right;
        images.Init();
        //direction = images.getDirection();

        images.Size();
        islive=true;
        score=0;
        mostscore=shared.read();
        time=0;

        if(shared.readVolumeFlag()==1)onResume();

        BT_isstar.setOnClickListener(this);
        BT_RIGHT.setOnClickListener(this);
        BT_UP.setOnClickListener(this);
        BT_LEFT.setOnClickListener(this);
        BT_DOWN.setOnClickListener(this);
        BT_Volume.setOnClickListener(this);
        mostScore.setText(Macro.MOST_SCORE+shared.read());
    }

    /**
     * onClick:该界面按钮监听事件
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            //游戏的开始、暂停按钮
            case R.id.isstar:

                if(volumeflag)onResume();
                if(volumeflag)playSound(1, 0);//播放1号声音资源,且播放一次
                //提示播放即时音效
                //Toast.makeText(MainActivity.this, "播放音效", Toast.LENGTH_SHORT).show();

                isstart =!isstart;
                if(isstart &&islive) {
                    BT_isstar.setText(R.string.stop);
                    onPause();//bgm停止
                    prompt.setVisibility(View.GONE);
                }
                else if(isstart ==false&&islive==true){
                    BT_isstar.setText("继续游戏");
                    prompt.setText("点击按钮继续游戏");
                    prompt.setVisibility(View.VISIBLE);
                }
                else if(islive==false){
//                    finish();
                    Intent intent=new Intent(MainActivity.this,MainActivity.class);
                    startActivity(intent);
                }
                break;
            //控制小蛇方向向右
            case R.id.Right:
                if(isstart &&direction!=Macro.Left) {
                    direction = Macro.Right;
                    images.setDirection(direction);
                }
                break;
            //控制小蛇方向向上
            case R.id.Up:
                if(isstart &&direction!=Macro.Down) {
                    direction = Macro.Up;
                    images.setDirection(direction);
                }
                break;
            //控制小蛇方向向左
            case R.id.Left:
                if(isstart &&direction!=Macro.Right) {
                    direction = Macro.Left;
                    images.setDirection(direction);
                }
                break;
            //控制小蛇方向向下
            case R.id.Down:
                if(isstart &&direction!=Macro.Up) {
                    direction = Macro.Down;
                    images.setDirection(direction);
                }
                break;
            case R.id.Volume:
                countVolume++;
                if(countVolume%2!=0){
                    volumeflag = false;
                    shared.saveFlag(volumeflag); //存入文件
                    BT_Volume.setBackgroundDrawable(getResources().getDrawable(R.drawable.volumeoff));//背景设置为静音
                }
                else
                {
                    volumeflag = true;
                    shared.saveFlag(volumeflag); //存入文件
                    BT_Volume.setBackgroundDrawable(getResources().getDrawable(R.drawable.volumeon));//背景设置
                }

        }
    }


    /**
     * 定时器,定时发送小蛇方向信息,控制小蛇速度
     * @param time
     */

    void StartView(int time) {
        timer=new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                handler.sendEmptyMessage(direction);
            }
        };
        timer.schedule(task,Macro.DELAY,Macro.PERIOD-time);
    }
}

MydatabaseHelper.java
package com.example.android_snake;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.content.Context;
import android.widget.Toast;

public class MyDatabaseHelper  extends SQLiteOpenHelper {
    public static final String CREATE_User = "create table user ("
            + "id integer primary key autoincrement, "
            + "userName varchar(20), "
            + "Password varchar(40), "
            + "Remarks integer)";
    private  Context mContext;
    static int dbVersion = 1;//版本
    static String name = "snakeUser.db";//数据库名

    /**
     *
     * @param context
     */
    public MyDatabaseHelper( Context context) {
        super(context, name, null, dbVersion);
        mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_User);
       // Toast.makeText(mContext, "用户信息创建成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}
RegisterActivity.java
package com.example.android_snake;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class RegisterActivity extends AppCompatActivity {
    private EditText username;
    private EditText password;
    private Button register;

    private void findViews(){
        username = (EditText) findViewById(R.id.username_register);
        password = (EditText) findViewById(R.id.password_register);
        register = (Button) findViewById(R.id.registerButton);
    }

    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);
        findViews();
        register.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                //MainActivity.playSound();
                String name = username.getText().toString().trim();
                String pass = password.getText().toString().trim();

                //对密码进行国密SM4加密,密文存入数据库
                String strKey = "0123456789abcdeffedcba9876543210";//密钥
                //String strKey = "0123456789abcdef";//密钥
                byte[] bKey = Encryption.hexStringToBytes(strKey);
                String CPass = Encryption.deal(pass,SM4.ENCRYPT,bKey);//密码加密后的密文CPass

                int remarks = 0;
                Log.i("TAG","username_"+name+"_ CPassword_"+CPass);
                Log.i("TAG","注册成功");
                UserService uService = new UserService(RegisterActivity.this);
                User user = new User();
                user.setUsername(name);
                user.setPassword(CPass);
                uService.register(user);
                Toast.makeText(RegisterActivity.this,"注册成功",Toast.LENGTH_LONG).show();
                //注册成功,跳转登录界面
                Intent intent = new Intent(RegisterActivity.this,LoginActivity.class);
                startActivity(intent);
            }
        });
    }


}
SharedPre.java
package com.example.android_snake;

import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;


/**
 * 用户最高分数记录信息
 */
public class SharedPre {
    private int mostscore;
    private Context context;
    String FileName;
    String FileNameVolume;
    private boolean volumeflag;//是否选中静音

    public SharedPre() {
    }

    public SharedPre(String usern,Context context) {
        FileName = usern+Macro.Pname;  //文件名:用户名加scorefile
        FileNameVolume = usern+Macro.PnameFlag; //文件名:用户名加VolumeFile
        this.context = context;
        volumeflag = true;  //默认选中
    }

    public void setvolumeFlag(boolean flag){
        volumeflag = flag;
    }


    /**
     * 存储最高游戏分数记录
     * @param usern 用户名
     * @param mscore 最高分数
     */
    public void save(String usern,int mscore) {
        SharedPreferences sp = context.getSharedPreferences(FileName, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.putInt("score",mscore);
        editor.apply();//文件存储结束

        //更新用户数据库
        UserService uService = new UserService(context);
        uService.updateRemarks(usern,mscore);

//        //更新用户数据库
//        MyDatabaseHelper  dbHelper = new MyDatabaseHelper(context);
//
//        SQLiteDatabase db = dbHelper.getWritableDatabase();
//        /*实例化内容值*/
//        ContentValues values = new ContentValues();
//
//        /*在values中添加内容*/
//        values.put("Remarks", mscore);
//        /*修改条件*/
//        String whereClause = "userName=?";
//        /*修改添加参数*/
//        String[] whereArgs = {usern};
//        /*修改*/
//        db.update("user", values, whereClause, whereArgs);
//        db.close();

    }

//读出并显示最高游戏分数记录
    public int read(){
        SharedPreferences sharedPreferences=context.getSharedPreferences(FileName,Context.MODE_PRIVATE);
        mostscore=sharedPreferences.getInt("score",0);
        return mostscore;
    }



    /**
     * 存储静音开关是否选中信息
     * @param flag
     */
    public void saveFlag(boolean flag) {
        SharedPreferences sp = context.getSharedPreferences(FileNameVolume, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        if(flag) editor.putInt("state",1);//选中存1
        else editor.putInt("state",0);
        editor.apply();//文件存储结束

    }

    /**
     * 读出静音开关状态
     * @return
     */
    public int readVolumeFlag(){
        SharedPreferences sharedPreferences=context.getSharedPreferences(FileNameVolume,Context.MODE_PRIVATE);
        int state=sharedPreferences.getInt("state",1);//默认1
        return state;
    }
}

SoundPlay.java
package com.example.android_snake;

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;

import androidx.appcompat.app.AppCompatActivity;

import java.util.HashMap;

/**
 * 贪吃蛇button/win/fail音效工具类
 */
public class SoundPlay  extends AppCompatActivity {

    static SoundPool sp;//声明SoundPool的引用
    static HashMap<Integer, Integer> hm;//声明HashMap来存放声音文件
    static int currStaeamId;//当前正播放的streamId

    public void initSoundPool() {//初始化声音池
        sp = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);//创建SoundPool对象
        hm = new HashMap<Integer, Integer>();//创建HashMap对象
        //加载声音文件,并且设置为1号 2号 3号声音放入hm中
        hm.put(1, sp.load(this, R.raw.button0, 1));
        hm.put(2, sp.load(this, R.raw.win, 1));
        hm.put(3, sp.load(this, R.raw.fail, 1));
        hm.put(4, sp.load(this, R.raw.applause, 1));
    }

    public void playSound(int sound, int loop) {//获取AudioManager引用
        AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        //获取当前音量
        float streamVolumeCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC);
        //获取系统最大音量
        float streamVolumeMax = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        //计算得到播放音量
        float volume = streamVolumeCurrent / streamVolumeMax;
        //调用SoundPool的play方法来播放声音文件
        currStaeamId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
    }


}

StartActivity.java
package com.example.android_snake;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Window;
import android.view.WindowManager;


public class StartActivity  extends Activity {
    private final int SPLASH_DISPLAY_LENGHT = 3000;  //延迟3秒

    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        //隐藏标题栏以及状态栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_beginning);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent(StartActivity.this, LoginActivity.class);
                StartActivity.this.startActivity(intent);
                StartActivity.this.finish();
            }
        }, SPLASH_DISPLAY_LENGHT);
    }

//    private Handler handler = new Handler() {
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//        }
//    };

    /**
     * 跳转至选择界面
     */
    public void getHome(){
        Intent intent = new Intent(StartActivity.this, ChooseActivity.class);
        startActivity(intent);
        finish();
    }

}

User.java
package com.example.android_snake;

import java.io.Serializable;

public class User implements Serializable {
    private int id;
    private String username;
    private String password;
    private int remarks;

    public User(){
        super();
    }
    public User(String username,String password,int remarks){
        super();
        this.username = username;
        this.password = password;
        this.remarks = remarks;
    }
    public int getId(){
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
    public String getUsername(){
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword(){
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getRemarks(){
        return remarks;
    }

    public void setRemarks(int remarks) {
        this.remarks = remarks;
    }

    /**
     * 描述用户信息
     * @return
     */
    public String desString(){
        return "User [id="+id+",username=" + username +",password="
                +password+",remarks="+remarks+"]";
    }
}

UserService.java
package com.example.android_snake;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

/**
 * 本类用于用户注册登录/更新数据库user表中最高分数功能的实现
 */
public class UserService {
    private MyDatabaseHelper dbHelper;
    public UserService(Context context){
        dbHelper = new MyDatabaseHelper(context);
    }

    public boolean login(String username,String password){
        SQLiteDatabase sdb = dbHelper.getReadableDatabase();
        String sql = "select * from user where username=? and password=?";
        Cursor cursor = sdb.rawQuery(sql, new String[]{username,password});
        if(cursor.moveToFirst()==true){
            cursor.close();
            return true;
        }
        return false;
    }

    public boolean register(User user){
        SQLiteDatabase sdb = dbHelper.getReadableDatabase();
        String sql = "insert into user(username,password,remarks) values(?,?,?)";
        Object[] obj = {user.getUsername(),user.getPassword(),user.getRemarks()};
        sdb.execSQL(sql,obj);
        return true;

    }

    public void updateRemarks(String username,int remarks){
        SQLiteDatabase sdb = dbHelper.getReadableDatabase();
        String remarksS = String.valueOf(remarks);
        String updateSql = "update user set Remarks=? where userName=?";
        sdb.execSQL(updateSql, new String[]{remarksS, username});

    }

}

布局文件

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

    <ImageView
        android:id="@+id/imageView6"
        android:layout_width="match_parent"
        android:layout_height="575dp"
        app:srcCompat="@drawable/cover_02" />

<!--    <TextView-->
<!--        android:id="@+id/textView2"-->
<!--        android:layout_width="match_parent"-->
<!--        android:layout_height="200dp"-->
<!--        android:layout_weight="1"-->
<!--        android:text="Play games to please yourself!"-->
<!--        android:textColor="@color/black"-->
<!--        android:textSize="25dp" />-->

    <TextView
        android:id="@+id/txt_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="70dp"
        android:text="Game player ⭐⭐⭐小游戏"
        android:textSize="30sp"
        android:textColor="@color/white"/>


</LinearLayout>
Activity_finalwin.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="252dp"
        android:layout_height="445dp"
        android:layout_centerInParent="true"
        android:layout_weight="1"
        app:srcCompat="@drawable/finalwin" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="@string/finalwin"
        android:layout_centerHorizontal="true"
        android:textColor="@color/purple_500"
        android:textSize="22sp" />
</RelativeLayout>
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/top_login"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:background="@drawable/pic_top2" />

    <TextView
        android:id="@+id/login_tip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="70dp"
        android:text="@string/welcome"
        android:textSize="25sp" />

    <EditText
        android:id="@+id/username_login"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/top_login"
        android:layout_marginLeft="35dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="35dp"
        android:background="@drawable/input_line1"
        android:hint="@string/login_tip1"
        android:inputType="text"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:textSize="18sp"
        tools:ignore="autofill" />

    <EditText
        android:id="@+id/password_login"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/username_login"
        android:layout_marginLeft="35dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="35dp"
        android:background="@drawable/input_line1"
        android:hint="@string/login_tip2"
        android:inputType="textPassword"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:textSize="18sp"
        tools:ignore="autofill" />

    <Button
        android:id="@+id/bt_login"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/password_login"
        android:layout_marginLeft="35dp"
        android:layout_marginTop="15dp"
        android:layout_marginRight="35dp"
        android:background="@drawable/start_button"
        android:text="@string/login_tip3"
        android:textColor="@color/white" />

    <RadioButton
        android:id="@+id/rbt_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/bt_login"
        android:layout_marginStart="25dp"
        android:layout_marginLeft="25dp"
        android:layout_marginTop="10dp"
        android:checked="false"
        android:text="@string/login_tip5"
        android:textSize="14sp" />

    <Button
        android:id="@+id/bt_goRegister"
        android:layout_width="match_parent"
        android:layout_height="18dp"
        android:layout_below="@id/rbt_login"
        android:layout_marginLeft="145dp"
        android:layout_marginTop="19dp"
        android:layout_marginRight="35dp"
        android:background="@null"
        android:text="@string/login_tip6"
        android:textColor="@color/grey"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/txt_loginother"
        android:layout_width="match_parent"
        android:layout_height="25dp"
        android:layout_below="@id/rbt_login"
        android:layout_centerVertical="true"
        android:layout_gravity="center"
        android:layout_marginLeft="65dp"
        android:layout_marginTop="145dp"
        android:layout_marginRight="65dp"
        android:gravity="center"
        android:text="@string/login_tip4"
        android:textSize="14sp" />

    <Button
        android:id="@+id/bt_wechat"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_below="@id/txt_loginother"
        android:layout_marginStart="65dp"
        android:layout_marginTop="30dp"
        android:background="@drawable/logo_login4"
        android:layout_marginLeft="65dp" />

    <Button
        android:id="@+id/bt_QQ"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_below="@id/txt_loginother"
        android:layout_marginStart="55dp"
        android:layout_marginLeft="55dp"
        android:layout_marginTop="30dp"
        android:layout_toEndOf="@id/bt_wechat"
        android:layout_toRightOf="@id/bt_wechat"
        android:background="@drawable/logo_login5" />
    <Button
        android:id="@+id/bt_weibo"
        android:layout_width="55dp"
        android:layout_height="55dp"
        android:layout_below="@id/txt_loginother"
        android:layout_toEndOf="@id/bt_QQ"
        android:layout_marginStart="55dp"
        android:layout_marginTop="30dp"
        android:background="@drawable/logo_login1"
        android:layout_toRightOf="@id/bt_QQ"
        android:layout_marginLeft="55dp" />



</RelativeLayout>

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical"
    tools:context="com.example.android_snake.MainActivity">

    <FrameLayout
        android:id="@+id/framelayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignBottom="@id/RelativeLayout"
        android:layout_marginBottom="145dp"
        android:background="@color/pink_1">

        <TextView
            android:id="@+id/prompt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="点击按钮,开始游戏!"
            android:textColor="@color/red_1"
            android:textSize="25sp" />

        <TextView
            android:id="@+id/Score"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="当前分数"
            android:textColor="#000000"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/MostScore"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="200dp"
            android:text="@string/MOSTSCORE"
            android:textColor="#000000"
            android:textSize="20sp" />

        <Button
            android:id="@+id/Volume"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginLeft="350dp"
            android:background="@drawable/volumeon" />

        <RelativeLayout
            android:id="@+id/addBling"
            android:layout_width="110dp"
            android:layout_height="60dp"
            android:layout_marginTop="70dp"
             />


    </FrameLayout>

    <RelativeLayout
        android:id="@+id/RelativeLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/isstar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/Up"
            android:layout_centerHorizontal="true"
            android:background="@drawable/downbutton"
            android:text="@string/star" />

        <Button
            android:id="@+id/Up"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_gravity="center"
            android:background="@drawable/upbutton"
            android:text="@string/UP" />

        <Button
            android:id="@+id/Left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/Up"
            android:layout_toStartOf="@id/Up"
            android:layout_toLeftOf="@id/Up"
            android:background="@drawable/leftbutton"
            android:text="@string/LEFT" />

        <Button
            android:id="@+id/Down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/Left"
            android:layout_centerHorizontal="true"
            android:background="@drawable/upbutton"
            android:text="@string/DOWN" />

        <Button
            android:id="@+id/Right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/Up"
            android:layout_toEndOf="@id/Up"
            android:layout_toRightOf="@id/Up"
            android:background="@drawable/rightbutton"
            android:text="@string/RIGHT" />

    </RelativeLayout>


</RelativeLayout>
activity_register.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/top_register"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:background="@drawable/pic_top2" />

    <TextView
        android:id="@+id/register_tip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="70dp"
        android:text="@string/welcomeregister"
        android:textSize="25sp" />

    <EditText
        android:id="@+id/username_register"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/top_register"
        android:layout_marginLeft="35dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="35dp"
        android:background="@drawable/input_line1"
        android:hint="@string/login_tip1"
        android:inputType="text"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:textSize="18sp"
        tools:ignore="autofill" />

    <EditText
        android:id="@+id/password_register"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/username_register"
        android:layout_marginLeft="35dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="35dp"
        android:background="@drawable/input_line1"
        android:hint="@string/register_tip1"
        android:inputType="textPassword"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:textSize="18sp"
        tools:ignore="autofill" />

    <Button
        android:id="@+id/registerButton"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_below="@id/password_register"
        android:layout_marginLeft="35dp"
        android:layout_marginTop="15dp"
        android:layout_marginRight="35dp"
        android:background="@drawable/start_button"
        android:text="@string/register_tip5"
        android:textColor="@color/white" />


<!--    <RadioButton-->
<!--        android:id="@+id/rbt_register"-->
<!--        android:layout_width="wrap_content"-->
<!--        android:layout_height="wrap_content"-->
<!--        android:layout_below="@id/registerButton"-->
<!--        android:layout_marginStart="25dp"-->
<!--        android:layout_marginLeft="25dp"-->
<!--        android:layout_marginTop="10dp"-->
<!--        android:checked="false"-->
<!--        android:text="@string/login_tip5"-->
<!--        android:textSize="14sp" />-->


    <TextView
        android:id="@+id/txt_2"
        android:layout_width="match_parent"
        android:layout_height="25dp"
        android:layout_below="@id/registerButton"
        android:layout_centerVertical="true"
        android:layout_gravity="center"
        android:layout_marginLeft="65dp"
        android:layout_marginTop="145dp"
        android:layout_marginRight="65dp"
        android:gravity="center"
        android:text="@string/login_tip4"
        android:textSize="14sp" />

    <Button
        android:id="@+id/bt_wechat"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_below="@id/txt_2"
        android:layout_marginStart="65dp"
        android:layout_marginLeft="65dp"
        android:layout_marginTop="30dp"
        android:background="@drawable/logo_login4" />

    <Button
        android:id="@+id/bt_QQ"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_below="@id/txt_2"
        android:layout_marginStart="55dp"
        android:layout_marginLeft="55dp"
        android:layout_marginTop="30dp"
        android:layout_toEndOf="@id/bt_wechat"
        android:layout_toRightOf="@id/bt_wechat"
        android:background="@drawable/logo_login5" />
    <Button
        android:id="@+id/bt_weibo"
        android:layout_width="55dp"
        android:layout_height="55dp"
        android:layout_below="@id/txt_2"
        android:layout_toEndOf="@id/bt_QQ"
        android:layout_marginStart="55dp"
        android:layout_marginTop="30dp"
        android:background="@drawable/logo_login1"
        android:layout_toRightOf="@id/bt_QQ"
        android:layout_marginLeft="55dp" />



</RelativeLayout>

activity_start_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.android_snake.ChooseActivity"
    android:orientation="vertical"
    android:background="@color/white"
    >
    <TextView
        android:id="@+id/top_1"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:background="@drawable/pic_top2" />

    <TextView
        android:id="@+id/txt_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="70dp"
        android:text="Game player\n贪吃蛇小游戏"
        android:textSize="40sp" />

    <Button
        android:id="@+id/star_game"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="50dp"
        android:layout_marginTop="100dp"
        android:layout_marginRight="50dp"
        android:background="@drawable/start_button"
        android:text="@string/stargame" />

    <Button
        android:id="@+id/MostBUtton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="50dp"
        android:background="@drawable/start_button"
        android:text="@string/historyscore" />

    <Button
        android:id="@+id/BackButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/back"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:layout_marginTop="10dp"
        android:background="@drawable/start_button"/>

</LinearLayout>

个人体会及存在的问题

本次课程设计目的在于使用java语言设计一款手机贪吃蛇小游戏,为了达到最好的用户体验效果,我学习使用Android Studio进行项目开发,在项目开发过程中,学到了很多新的知识与技能,切实体会到项目开发的完整流程,并在此过程中不断增加需求、完善功能、优化结构,从最开始的游戏逻辑到用户界面一步步进行完善与修改,从数据库连接、文件操作到apk打包等种种操作,见证自己的第一个apk诞生的全过程,获得感颇丰。

下面例举一些项目开发中遇到的问题及解决方案:

一些资源文件引用、调用错误:如引入png图片文件时文件路径不能有中文,value中的颜色 定义文件colors.xml中定义了一个格式不支持的颜色;音频文件引用时的后缀名称问题,提示编码错误,改为.text后缀类型即可正常使用

一些代码逻辑需要学习巩固:如贪吃蛇游戏中的多线程处理、Android Studio中的SQLite连接及文件操作、布局xml文件的代码编写方法、布局与构件种类、SM4国密密码算法等。

一些小功能实现需要去学习:隐藏标题栏、隐藏手机状态栏、开机动画停留3秒、各个界面之间的自动跳转、获取手机屏幕宽度、音频导入与播放、APK打包、APK属性信息设置。

一些小bug需要一个个改正:如贪吃蛇初始化时头的方向若向左,初始运动方向设置为向右便会出场阵亡;食物的图标尺寸大于运动单元,则会出现小蛇看上去吃到食物实际上却并没有到达那个坐标;小蛇的初始位置太偏不容易调整初始进入游戏的状态;由于未在AndroidManifest.xml文件中注册界面的Activity,导致界面跳转时出现软件闪退情况;游戏菜单界面的三个按钮都加音效会导致软件闪退等等……

解决各种问题的过程正是自我提高的过程,弥足珍贵。

开发过程中遇到的这样各种各样的小问题还有很多,现已难以尽数列出……

GreedySnake这个程序虽然已经尽力去完善与改进了,但仍然存在一些小问题,如游戏界面中背景音乐播放使用了MediaPlayer类,按钮音效等短音频处理使用了SoundPool,而MediaPlayer仅支持同时播放一个音频,故播放背景音乐时按钮音效并不能体现……

错误和bug不让人感觉快乐,但程序员的乐趣就在于解决一个又一个报错,修改一个又一个bug。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值