【读书笔记《Android游戏编程之从零开始》】20.游戏开发基础(游戏数据存储)...

article/2025/3/1 7:31:10

对于数据的存储,Android 提供了4种保存方式。

(1)SharedPreference

此方法适用于简单数据的保持,文如其名,属于配置性质的保存,不适合比较大的情况,默认存放在手机内存里

(2)FileInputStream/FileOutputStream

此方式比较适合游戏的保存和使用,流文件数据存储可以保持较大的数据,而且通过此方式不仅能把数据存储在手机内存中,也能将数据保存到手机额SDcard中。

(3)SQLite

此方式也适合游戏的保存和使用,不仅可以保存较大的数据,而且可以将自己的数据存储到文件系统或者数据库当中,如SQLite数据库,也能将数据保存到SDcard 中。

(4)ContentProvider

此方式不推荐用于游戏保存,虽然此方式能存储较大数据,还支持多个程序之间的数据进行交换,但游戏中基本就不可能去访问外部应用程序的数据。

 

1.SharedPreference

SharedPreference 实例是通过Context 对象得到的:

Context.getSharePreference(String name,int mode)

作用:利用Context 对象获取一个SharedPreference 实例

参数1:生成保持记录的文件名

参数2:操作模式

 

SharedPreference 实例的操作模式一共有四种:

Context.MODE_PRIVATE:新内容覆盖原内容。

Context.MODE_APPEND:新内容追加到原内容后。

Context.MODE_WORLD_READABLE:允许其他应用程序读取。

Context.MODE_WORLD_WRITEABLE:允许其他应用程序写入,会覆盖原数据。

 

SharedPreference 常用函数:

getFloat(String key,float defValue)

getInt(String key,int defValue)

getLong(String key,long defValue)

getString(String key,String defValue)

getBoolean(String key,boolean defValue)

 

SharedPreference 常用函数的作用是获取存储文件中的值,根据方法不同获取不同对应的类型值,一般第一个参数为索引Key值,第二个参数为在存储文件中找不到对应 value 值时,默认的返回值。

 

对应的,在对存储文件的数据进行存入操作时,首先需要利用 SharedPreference 实例得到一个编辑对象:

SharedPreference.Editor edit;

得到编辑对象后就可以对 SharedPreference 中的数据进行操作。

SharedPreference.Editor.putFloat(arg0,arg1)

SharedPreference.Editor.putInt(arg0,arg1)

SharedPreference.Editor.putLong(arg0,arg1)

SharedPreference.Editor.putString(arg0,arg1)

SharedPreference.Editor.putBoolean(arg0,arg1)

以上方法的作用是对存储的数据进行操作(写入、保存),其中第一个参数是需要保存数据额Key值索引,第二个参数是需要保存的数据。

到此进行了保存和修改,还需要将其编辑的数据进行提交方可完成存入和修改:

SharedPreference.Editor.commit()

如果想删除存储文件中的一条数据,可以使用以下函数:

SharedPreference.Editor.clear()

 

下面用一个简单小游戏进行说明,先看下效果图:

 

 新建项目,游戏框架为 SurfaceView 游戏框架,修改 MySurfaceView 类如下:

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;public class MySurfaceView extends SurfaceView implements Callback, Runnable {private SurfaceHolder sfh;private Paint paint;private Thread th;private boolean flag;private Canvas canvas;private int screenW, screenH;//记录当前圆形所在九宫格的位置下标private int creentTileIndex;//声明一个SharedPreferences对象private SharedPreferences sp;/*** SurfaceView初始化函数*/public MySurfaceView(Context context) {super(context);sfh = this.getHolder();sfh.addCallback(this);paint = new Paint();paint.setColor(Color.BLACK);paint.setAntiAlias(true);setFocusable(true);//通过Context获取SharedPreference实例sp = context.getSharedPreferences("SaveName", Context.MODE_PRIVATE);//每次程序运行时获取圆形的下标int tempIndex = sp.getInt("CirCleIndex", -1);//判定如果返回-1 说明没有找到,就不对当前记录圆形的变量进行赋值if (tempIndex != -1) {creentTileIndex = tempIndex;}}/*** SurfaceView视图创建,响应此函数*/@Overridepublic void surfaceCreated(SurfaceHolder holder) {screenW = this.getWidth();screenH = this.getHeight();flag = true;//实例线程th = new Thread(this);//启动线程
        th.start();}/*** 游戏绘图*/public void myDraw() {try {canvas = sfh.lockCanvas();if (canvas != null) {canvas.drawColor(Color.WHITE);paint.setColor(Color.BLACK);paint.setStyle(Style.STROKE);//绘制九宫格(将屏幕九等份)//得到每个方格的宽高int tileW = screenW / 3;int tileH = screenH / 3;for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {canvas.drawRect(i * tileW, j * tileH, (i + 1) * tileW, (j + 1) * tileH, paint);}}//根据得到的圆形下标位置进行绘制相应的方格中
                paint.setStyle(Style.FILL);canvas.drawCircle(creentTileIndex % 3 * tileW + tileW / 2, creentTileIndex / 3 * tileH + tileH / 2, 30, paint);//操作说明canvas.drawText("上键:保存游戏", 0, 20, paint);canvas.drawText("下键:读取游戏", 110, 20, paint);canvas.drawText("左右键:移动圆形", 215, 20, paint);}} catch (Exception e) {// TODO: handle exception} finally {if (canvas != null)sfh.unlockCanvasAndPost(canvas);}}/*** 触屏事件监听*/@Overridepublic boolean onTouchEvent(MotionEvent event) {return true;}/*** 按键事件监听*/@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {//上键保存游戏状态if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {sp.edit().putInt("CirCleIndex", creentTileIndex).commit();//下键读取游戏状态} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {int tempIndex = sp.getInt("CirCleIndex", -1);if (tempIndex != -1) {creentTileIndex = tempIndex;}//圆形的移动} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {if (creentTileIndex > 0) {creentTileIndex -= 1;}} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {if (creentTileIndex < 8) {creentTileIndex += 1;}}return super.onKeyDown(keyCode, event);}/*** 游戏逻辑*/private void logic() {}@Overridepublic void run() {while (flag) {long start = System.currentTimeMillis();myDraw();logic();long end = System.currentTimeMillis();try {if (end - start < 50) {Thread.sleep(50 - (end - start));}} catch (InterruptedException e) {e.printStackTrace();}}}/*** SurfaceView视图状态发生改变,响应此函数*/@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}/*** SurfaceView视图消亡时,响应此函数*/@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {flag = false;}
}
View Code

 

2.流文件存储

利用前面 SharedPreference 的例子,去除 SharedPreference 存储部分,改用流文件形式进行保存,只需要修改其中“保存”和“读取”操作,也就是修改“按键事件”:

    /*** 按键事件监听*/@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// 用到的读出、写入流FileOutputStream fos = null;FileInputStream fis = null;DataOutputStream dos = null;DataInputStream dis = null;// 上键保存游戏状态if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {try {// 利用Activity 实例打开流文件得到一个写入流fos = MainActivity.instance.openFileOutput("save.yc",Context.MODE_PRIVATE);// 将写入流封装在数据写入流中dos = new DataOutputStream(fos);//写入一个int 类型(将圆形所在格子的下表写入流文件中)
                dos.writeInt(creentTileIndex);} catch (Exception e) {// TODO: handle exception
                e.printStackTrace();}finally{//即使保存时发生异常也要关闭流try {if(fos!=null) fos.close();if(dos!=null)dos.close();} catch (Exception e) {// TODO: handle exception
                    e.printStackTrace();}}// 下键读取游戏状态} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {try {if(MainActivity.instance.openFileInput("save.yc")!=null){try {//利用Activity 实例打开流文件得到一个读出流fis = MainActivity.instance.openFileInput("save.yc");//将读出流封装在数据读入流中dis = new DataInputStream(fis);//读出一个int 类型赋值与圆形所在格子的下标creentTileIndex =dis.readInt();} catch (Exception e) {// TODO: handle exception}finally{if(fis!=null)fis.close();if(dis!=null)dis.close();}}} catch (Exception e) {// TODO: handle exception
            }// 圆形的移动} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {if (creentTileIndex > 0) {creentTileIndex -= 1;}} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {if (creentTileIndex < 8) {creentTileIndex += 1;}}return super.onKeyDown(keyCode, event);}

不管是读入还是写入,都是通过Activity 打开流文件得到输入输出流。当需要写入流文件时,如果打开的流文件不存在,那么Android 会自动生成对应的流文件;而当需要读入流文件时,首先应判断流文件是否存在,一旦流文件不存在,就会抛出异常。

这里的流形式的保存操作比较简单,需要注意的是:

● 读流时,一定要记得判断是否存在需要操作的流文件;

● 写入和读入的数据类型要配对,顺序也不能错;例如:写入时,先写入了一个 Int值,然后又写入了一个String 值;那么读入时,也应该先读 Int 类型,然后再读 String 类型;

● 流一旦打开一定要关闭,为了避免流操作出现异常,需确保正常关闭流,应该将关闭操作写在finally 语句中;

● file 流使用 Data 流进行了封装,这样做的原因是可以获得更多的操作方式,便于对数据的处理。

以上是使用流文件的保存方式,但是也只是将保存后的流文件默认放在了系统内存里。一般游戏的数据可能会有很多,所以不应该放在手机内存中,而是放在 SDCard 中,这样就不要担心系统因游戏保存的数据过多导致内存不足等问题。

将流文件保存在 SDCard 中的详细步骤如下:

(1)声明读取权限:

Android 中的一些操作,比如:读取通讯录信息、发送信息、使用联网、GPRS等功能都需要在项目 AndroidManifest.xml 中声明使用权限,然后才可以正常使用其功能。

当然在很多时候,是不知道是否需要声明添加权限的,其实这个也不用知道,因为如果用到这些需要声明权限的功能,且恰好没有声明的情况下,在LogCat 中是会报异常的,其异常则提醒需要添加对应的权限。

写入权限如下:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

(2)创建目录和存储文件

 使用 SDCard 的方式进行存储数据,写入的时候 Android 不会跟存储系统默认路径那样默认生成存储文件,所以必须自己来创建;如果存储的文件有自定义路径的话,那么这个路径也需要手动添加。

假定存储文件在 SDCard 的路径为 /sdcard/yc/save.yc 。

① 首先需要创建路径 /sdcard/yc

File path = new File(" /sdcard/yc");// 创建目录  
if(!path.exists())// 目录存在返回true
{path.mkdirs();// 创建一个目录                      
}

boolean File.exists()

 作用:判断是否存在当前目录

返回值:当前目录存在返回true

boolean File.mkdirs()

作用:创建一个目录

返回值:当创建成功返回true

 

②然后创建存储文件:/sdcard/yc/save.yc

File path_File = new File(" /sdcard/yc/save.yc");// 创建文件  
if(!path_File.exists())// 文件存在返回true
{path_File.createNewFile();// 创建一个文件                      
}

boolean File.createNewFile()

作用:创建一个文件

返回值:创建成功返回true

 

(3)通过加载指定路径的存储文件获取输入输出流

● 输入流:FileInputStream fis = new FileInputStream(File file);

● 输出流:FileOutputStream fos = new FileOutputStream(File file);

 

除此之外还需要知道一点,因为有时手机并没有安装 SDCard ,或者当前SDCard 处于被移除的状态时,为了避免这两种情况带来的异常,需要通过下面方法获取当前手机设备SDCard 的状态:

String Environment.getExternalStorageStorageState()

作用:获取当前SDCard的状态

返回值:当前 SDCard 不存在时,返回null ;当 SDCard 处于移除状态时,返回“removed”;

 

所以当默认手机设备存在 SDCard 时,保存在 SDCard 中 ;当 SDCard 不存在或者正处于被移除的状态时,默认将数据保存在手机内存中。上面的“按键事件”修改如下:

    /*** 按键事件监听*/@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {//用到的读出、写入流FileOutputStream fos = null;FileInputStream fis = null;DataOutputStream dos = null;DataInputStream dis = null;//上键保存游戏状态if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {try {// 从SDcard中写入数据// 试探终端是否有sdcard! 并且探测SDCard是否处于被移除的状态if (Environment.getExternalStorageState() != null && !Environment.getExternalStorageState().equals("removed")) {Log.v("yc", "写入,有SD卡");File path = new File("/sdcard/yc");// 创建目录  File f = new File("/sdcard/yc/save.yc");// 创建文件  if (!path.exists()) {// 目录存在返回true  path.mkdirs();// 创建一个目录  
                    }if (!f.exists()) {// 文件存在返回true  f.createNewFile();// 创建一个文件  
                    }fos = new FileOutputStream(f);// 将数据存入sd卡中  } else {//默认系统路径//利用Activity实例打开流文件得到一个写入流fos = MainActivity.instance.openFileOutput("save.yc", Context.MODE_PRIVATE);}//将写入流封装在数据写入流中dos = new DataOutputStream(fos);//写入一个int类型(将圆形所在格子的下标写入流文件中)
                dos.writeInt(creentTileIndex);} catch (FileNotFoundException e) {// TODO Auto-generated catch block
                e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch block
                e.printStackTrace();} finally {//即使保存时发生异常,也要关闭流try {if (fos != null)fos.close();if (dos != null)dos.close();} catch (IOException e) {// TODO Auto-generated catch block
                    e.printStackTrace();}}//下键读取游戏状态} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {boolean isHaveSDCard = false;// 从SDcard中读取数据// 试探终端是否有sdcard! 并且探测SDCard是否处于被移除的状态if (Environment.getExternalStorageState() != null && !Environment.getExternalStorageState().equals("removed")) { Log.v("yc", "读取,有SD卡");isHaveSDCard = true;}try {if (isHaveSDCard) {File path = new File("/sdcard/yc");// 创建目录  File f = new File("/sdcard/yc/save.yc");// 创建文件  if (!path.exists()) {// 目录存在返回truereturn false;} else {if (!f.exists()) {// 文件存在返回true  return false;}}fis = new FileInputStream(f);// 将数据存入sd卡中  } else {if (MainActivity.instance.openFileInput("save.yc") != null) {//利用Activity实例打开流文件得到一个读入流fis = MainActivity.instance.openFileInput("save.yc");}}//将读入流封装在数据读入流中dis = new DataInputStream(fis);//读出一个Int类型赋值与圆形所在格子的下标creentTileIndex = dis.readInt();} catch (FileNotFoundException e) {// TODO Auto-generated catch block
                e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch block
                e.printStackTrace();} finally {//即使读取时发生异常,也要关闭流try {if (fis != null)fis.close();if (dis != null)dis.close();} catch (IOException e) {// TODO Auto-generated catch block
                    e.printStackTrace();}}} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {if (creentTileIndex > 0) {creentTileIndex -= 1;}} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {if (creentTileIndex < 8) {creentTileIndex += 1;}}return super.onKeyDown(keyCode, event);}
View Code

 

3.SQLite 轻量级数据库

此方式也适合游戏的保存和使用,不仅可以保存较大的数据,而且可以将自己的数据存储到文件系统或者数据库当中,如SQLite数据库,也能将数据保存到SDcard 中。

SQLite是一款轻量级数据库,它的设计目的是嵌入式,而且它占用的资源非常少,在嵌入式设备中,只需要几百KB!

SQLite的特性:
轻量级:使用 SQLite 只需要带一个动态库,就可以享受它的全部功能,而且那个动态库的尺寸想当小。
独立性:SQLite 数据库的核心引擎不需要依赖第三方软件,也不需要所谓的“安装”。
隔离性:SQLite 数据库中所有的信息(比如表、视图、触发器等)都包含在一个文件夹内,方便管理和维护。
跨平台:SQLite 目前支持大部分操作系统,不至电脑操作系统更在众多的手机系统也是能够运行,比如:Android。
多语言接口:SQLite 数据库支持多语言编程接口。
安全性:SQLite 数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一数据库读取数据,但只能有一个可以写入数据.

优点:1.能存储较多的数据。2.能将数据库文件存放到SD卡中!


什么是 SQLiteDatabase?

     一个 SQLiteDatabase 的实例代表了一个SQLite 的数据库,通过SQLiteDatabase 实例的一些方法,我们可以执行SQL 语句,对数        据库进行增、删、查、改的操作。需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的。

 
什么是 SQLiteOpenHelper ?

     根据这名字,我们可以看出这个类是一个辅助类。这个类主要生成一个数据库,并对数据库的版本进行管理。当在程序当中调用这个类的方法getWritableDatabase(),或者getReadableDatabase()方法的时候,如果当时没有数据,那么Android 系统就会自动生成一个数据库。SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的3 个函数

什么是 ContentValues 类?

     ContentValues 类和Hashmap/Hashtable 比较类似,它也是负责存储一些名值对,但是它存储的名值对当中的名是一个String 类型,而值都是基本类型。
 
什么是 Cursor ?

     Cursor 在Android 当中是一个非常有用的接口,通过Cursor 我们可以对从数据库查询出来的结果集进行随机的读写访问。

同样利用前面 SharedPreference 的例子,去除 SharedPreference 存储部分,改用SQLite进行保存。

① 首先新建一个类MySQLiteOpenHelper继承SQLiteOpenHelper;写一个构造,重写两个函数:

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;public class MySQLiteOpenHelper extends SQLiteOpenHelper {public final static int VERSION = 1;// 版本号public final static String TABLE_NAME = "INDEXINFO";// 表名public final static String Param1 = "ID";// 后面ContentProvider使用public final static String Param2 = "TITLEINDEX";public final static String DATABASE_NAME = "yc.db";public MySQLiteOpenHelper(Context context) {// 在Android 中创建和打开一个数据库都可以使用openOrCreateDatabase 方法来实现,// 因为它会自动去检测是否存在这个数据库,如果存在则打开,不过不存在则创建一个数据库;// 创建成功则返回一个 SQLiteDatabase对象,否则抛出异常FileNotFoundException。// 下面是来创建一个名为"DATABASE_NAME"的数据库,并返回一个SQLiteDatabase对象super(context, DATABASE_NAME, null, VERSION);}/*** 在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表;*/@Overridepublic void onCreate(SQLiteDatabase db) {// SQL语句,这里是创建一个表明为yc的表,有2个参数ID和TITLEINDEX,其中ID为主键,自增。具体说明如下:// CREATE TABLE 创建一张表 然后后面是表名// 然后表的列,第一个是id 方便操作数据,int类型// PRIMARY KEY 是指主键 这是一个int型,用于唯一的标识一行;// AUTOINCREMENT 表示数据库会为每条记录的key加一,确保记录的唯一性;// 最后加入一列文本 int类型参数TITLEINDEX
String str_sql = "CREATE TABLE " + TABLE_NAME + "(" + Param1+ " INTEGER PRIMARY KEY AUTOINCREMENT," + Param2+ "  INTEGER );";// execSQL()方法是执行一句sql语句
        db.execSQL(str_sql);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// 一般默认情况下,当我们插入 数据库就立即更新// 当数据库需要升级的时候,Android 系统会主动的调用这个方法。// 一般我们在这个方法里边删除数据表,并建立新的数据表,// 当然是否还需要做其他的操作,完全取决于游戏需求。Log.v("y_Hint", "onUpgrade");}}
View Code

② 在MySurfaceView类中创建对应的实例

private MySQLiteOpenHelper myOpenHelper;// 创建一个继承SQLiteOpenHelper类实例
private SQLiteDatabase db;

③ 在SurfaceView初始化函数中实例一个数据库辅助器

// 实例一个数据库辅助器
myOpenHelper = new MySQLiteOpenHelper(MainActivity.instance);

④ 最后修改按键监听事件,其实只需要修改其中“保存”和“读取”操作,代码如下:

    /*** 按键事件监听*/@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {db = myOpenHelper.getWritableDatabase(); // 实例数据库// 上键保存游戏状态if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {// ---------------------- SQL语句删除--------------String DELETE_DATA = "DELETE FROM " + MySQLiteOpenHelper.TABLE_NAME;db.execSQL(DELETE_DATA);// ---------------------- SQL语句插入--------------String INSERT_DATA = "INSERT INTO " + MySQLiteOpenHelper.TABLE_NAME+ "(" + MySQLiteOpenHelper.Param2 + ") values ("+ creentTileIndex + ")";db.execSQL(INSERT_DATA);// 下键读取游戏状态} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {// 数据查询Cursor cur = db.rawQuery("SELECT * FROM "+ MySQLiteOpenHelper.TABLE_NAME, null);if (cur != null) {cur.moveToNext();creentTileIndex = cur.getInt(1);}} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {if (creentTileIndex > 0) {creentTileIndex -= 1;}} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {if (creentTileIndex < 8) {creentTileIndex += 1;}}return super.onKeyDown(keyCode, event);}

说明:

在Android中查询数据是通过Cursor类来实现的,当我们使用SQLiteDatabase.query()方法时,会得到一个Cursor对象,Cursor指向的就是每一条数据。它提供了很多有关查询的方法,具体方法如下:

 以下是方法和说明:

 move    以当前的位置为参考,将Cursor移动到指定的位置,成功返回true, 失败返回false

 moveToPosition 将Cursor移动到指定的位置,成功返回true,失败返回false

 moveToNext  将Cursor向前移动一个位置,成功返回true,失败返回false

 moveToLast    将Cursor向后移动一个位置,成功返回true,失败返回 false。

 movetoFirst  将Cursor移动到第一行,成功返回true,失败返回false

 isBeforeFirst     返回Cursor是否指向第一项数据之前

 isAfterLast  返回Cursor是否指向最后一项数据之后

 isClosed          返回Cursor是否关闭

 isFirst    返回Cursor是否指向第一项数据

 isLast       返回Cursor是否指向最后一项数据

 isNull       返回指定位置的值是否为null

 getCount     返回总的数据项数

 getInt          返回当前行中指定的索引数据

 

本文地址:http://www.cnblogs.com/yc-755909659/p/4223999.html 

PS:本文由J灬叶小超原创,如有转载请注明出处,谢谢!

转载于:https://www.cnblogs.com/yc-755909659/p/4223999.html


http://chatgpt.dhexx.cn/article/1mXhnS4u.shtml

相关文章

Demo06-GUI编程

GUI编程 1. 简介 GUI的核心技术&#xff1a;Swing AWT 2. AWT 2.1 Awt 介绍 awt 指抽象的窗口工具&#xff0c;包含了很多类和接口&#xff0c;用于GUI编程&#xff1a;图形用户界面编程元素&#xff1a;窗口&#xff0c;按钮&#xff0c;文本框java.awt [外链图片转存失…

【Java基础】语法基础

本文适合有编程基础或是需要Java语言复习的家人们食用~ 一、Java语言介绍 本篇文章使用的JDK版本是1.8&#xff08;即JDK 8&#xff09;Java语言是运行在JVM上的&#xff0c;有了JVM&#xff0c;Java语言得以在不同操作系统上运行垃圾回收机制&#xff1a;Java语言提供了一种…

编程算法集锦

编程算法集锦 一、分治法1.分治法介绍2.归并排序3.快速排序4.中值问题 二、贪心法1.贪心法2.最小生成树Kruskal算法3.Huffman编码4.单源点最短路径 三、回溯法1.回溯法-n皇后问题2.子集和数 四、动态规划1.数塔问题2.最长公共子序列3.求序列-2 11 -4 13 -5 -2的最大字段和4.求最…

富文本的使用 NSMutableAttributedString

文章内容大纲 1、NSMutableAttributedString的基本使用2、NSMutableAttributedString的简易封装3、使用开源代码GOBMarkupPaser处理富文本4、UITextKit简介5、编程思想的相关思考 前言 富文本使用案例&#xff1a; 这里我自己也用了富文本实现了简单的却也是常用的例子&#x…

iOS 开发 富文本

http://www.itnose.net/detail/6177538.html 文章内容大纲 1、NSMutableAttributedString的基本使用2、NSMutableAttributedString的简易封装3、使用开源代码GOBMarkupPaser处理富文本4、UITextKit简介5、编程思想的相关思考 前言 富文本使用案例&#xff1a; 这里我自己也用…

软工第三次作业-结对编程

结对项目-最长英语单词链 哈哈&#xff0c;这次记住了&#xff0c;来&#xff0c;初始化&#xff01; 项目内容这个作业属于哪个课程2023年北航敏捷软件工程社区这个作业的要求在哪里结对项目-最长英语单词链我在这个课程的目标是学习软件开发的原则、方法&#xff0c;并对敏捷…

Python基础编程习题

警察局抓了a&#xff0c;b&#xff0c;c&#xff0c;d四名偷窃嫌疑犯&#xff0c;其中只有一人是小偷。审问中 a说&#xff1a;“我不是小偷。” b说&#xff1a;“c是小偷。” c说&#xff1a;“小偷肯定是d。” d说&#xff1a;“c在冤枉人。” 现在已经知道四个人中三人说的…

四面体的表面积_如何求正四面体的体积和表面积?

当正四面体的棱长为a时&#xff0c;体积&#xff1a;√2a/12&#xff0c;表面积√3a^2。 解答过程如下&#xff1a; 正四面体是由四个全等的正三角形所组成的几何体。它有四个面、四个顶点、六条棱。每个二面角均为7032’&#xff0c;有四个三面角&#xff0c;每个三面角的面角…

空间四面体的面积、体积运算

基于C#窗体应用程序。通过添加控件&#xff08;Button、Label、TextBox&#xff09;来实现相应的功能。 目录 一、界面设计 二、编写代码 1、计算体积 2、计算面积 三、编译调试 四、实现效果 一、界面设计 二、编写代码 1、计算体积 double A1, A2, A3, A4, value; A…

四面体体积求法

四面体&#xff08;三棱锥&#xff09;体积 &#xff1a; 设 有&#xff1a; 不过这是有向的。如果知道那四个顶点&#xff0c;用这个公式即可求出体积。 如果不知道四点仅知道6条边长&#xff0c;就得用下面的方法——欧拉四面体公式 写成行列式&#xff1a; 那么有&…

matlab 四面体体积

计算方法&#xff1a; 已知四面体顶点坐标分别为 (x1,y1,z1)&#xff0c; (x2,y2,z2)&#xff0c; (x3,y3,z3)&#xff0c; (x4,y4,z4)&#xff0c; 可以通过如下两种方法求四面体体积&#xff1a; 1. 利用向量的混和积 过一顶点的三向量设为a&#xff0c;b&#xff0c;…

C++:使用类方法根据四点计算四面体体积

一个四面体有四个点&#xff0c;分别为a (x1, y1, z1), b (x2, y2, z2), c (x3, y3, z3), 以及d (x4, y4, z4)&#xff0c;计算该四面体的体积。 &#xff08;1&#xff09;四面体计算公式 &#xff08;2&#xff09;三维空间的两点的点乘 &#xff0…

【HDU1411】四面体的体积公式

1.题目链接。题目大意&#xff1a;就是给出一个四面体的六条边&#xff0c;求出这个四面体的体积。 2.这个&#xff0c;如果知道坐标是很好解决的&#xff0c;假设我们知道的是坐标&#xff1a; 体积就是混合积的六分之一。&#xff08;什么&#xff1f;x,y,z是啥&#xff1f;…

tomcat7安装版的详细步骤

如图&#xff1a;直接双击tomcat安装包进入安装流程。 出现安装界面&#xff0c;点击“next”下一步继续。 在这个界面点击i aggress &#xff0c;“我同意”继续下一步。 当出现这个界面的时候&#xff0c;不要做任何修改&#xff0c;直接点击下一步继续。 在这个界面…

tomcat7.0安装及配置教程(win10)

一、前言 Tomcat 服务器是一个开源的轻量级Web应用服务器&#xff0c;在中小型系统和并发量小的场合下被普遍使用&#xff0c;是开发和调试Servlet、JSP 程序的首选。 二、安装前准备 1.确保安装过jdk&#xff0c;安装过可跳过。 如果没有安装可以参考本人另外写的博文win1…

Tomcat7.0的安装及配置

本篇文章集合网上的零散经验加上2次实践整合&#xff0c;主要为大家提供如何正确使用Tomcat方法。本人安装的是7.0.77版本。 所需软件&#xff1a; JDK6.0/7.0 Tomcat 7.0 步骤&#xff1a; 1.安装JDK&#xff0c;配置好环境变量&#xff1a;JAVA_HOME、Classpath、Path 2…

win10安装tomcat7的安装与配置【详细教程】

1、tomcat传送门&#xff0c;群文件自取&#xff1b;群号&#xff1a;708072830 2、下载解压之后&#xff0c;先安装好tomcat 第一步&#xff1a; 在Tomcat bin路径下 找到 startup.bat 双击 打开&#xff0c;闪退表示 安装或者配置 失败&#xff1b;如下&#xff1b;界面不…

Tomcat7.0/8.0 详细安装配置

Tomcat 7.0 、Tomcat8.0 详细安装配置图解,以及UTF-8编码配置 注意:安装配置tomcat7.0及以上,需要先安装JDK1.7及以上才能支持。 1、先下载tomcat压缩包 Tomcat 7 :http://tomcat.apache.org/download-70.cgi Tomcat 8 : http://tomcat.apache.org/download-80.cgi …

tomcat7.0安装

下载地址 tomcat7.0下载地址 https://tomcat.apache.org/download-70.cgi 同事推荐别安装最新版&#xff0c;说是不稳定&#xff01; 安装前说明 安装tomcat之前一定要有jdk。 下载包分为安装版与免安装版&#xff0c;我使用的是安装版&#xff0c;安装完成后不需要配置环…

CentOS8-Tomcat7安装并设置开机自启动

CentOS8-Tomcat7安装并设置开机自启动 1、安装 将压缩包文件apache-tomcat-7.0.57.tar.gz利用Xftp 6工具上传到/usr/local中并解压&#xff08;为了以后可能会安装多个Tomcat,我将解压后的文件移动到了新建目录tomcat-cluster下并重命名&#xff09;&#xff1a; tar -xvf a…