目錄
SQL的基本語法
1.數(shù)據(jù)定義語言
2.數(shù)據(jù)操縱語言
數(shù)據(jù)庫管理器 SQLiteDatabase
數(shù)據(jù)庫幫助器 SQLiteOpenHelper
優(yōu)化記住密碼功能
????????本文介紹Android的數(shù)據(jù)庫存儲方式-SQLite的使用方法,包括:SQLite用到了哪些SQL語法,如何使用數(shù)據(jù)庫管理器操縱SQLite,如何使用數(shù)據(jù)庫幫助器簡化數(shù)據(jù)庫操作,以及如何利用SQLite改進登錄頁面的記住密碼功能。
SQL的基本語法
????????SQL本質(zhì)上是一種編程語言,它的學(xué)名叫作“結(jié)構(gòu)化查詢語言”(全稱為Structured Query Language,簡稱SQL).不過SQL語言并非通用的編程語言,它專用于數(shù)據(jù)庫的訪問和處理,更像是一種操作命令,所以常說SQL語句而不說SQL代碼。標(biāo)準(zhǔn)的SQL語句分為3類:數(shù)據(jù)定義、數(shù)據(jù)操縱和數(shù)據(jù)控制。但不同的數(shù)據(jù)庫往往有自己的實現(xiàn)。
????????SQLite是一種小巧的嵌入式數(shù)據(jù)庫,使用方便、開發(fā)簡單。如同MySQL、Oracle那樣,SQLite也采用SQL語句管理數(shù)據(jù),由于它屬于輕型數(shù)據(jù)庫,不涉及復(fù)雜的數(shù)據(jù)控制操作,因此App開發(fā)只用到數(shù)據(jù)定義和數(shù)據(jù)操縱兩類SQL語句。此外,SQLite的SQL語法與通用的SQL語法略有不同,接下來介紹的兩類SQL語法全部基于SQLite。
1.數(shù)據(jù)定義語言
????????數(shù)據(jù)定義語言(全稱Data Definition Language,簡稱DDL)描述了怎樣變更數(shù)據(jù)實體的框架結(jié)構(gòu)。就SQLite而言,DDL語言主要包括3種操作:創(chuàng)建表格、刪除表格、修改表結(jié)構(gòu),分別說明如下。
????????(1)創(chuàng)建表格
????????表格的創(chuàng)建動作由create命令完成,格式為“CREATE TABLE IF NOT EXISTS 表格名稱(以逗號分隔的各字段定義);”。以用戶信息表為例,它的建表語句如下:
CREATE TABLE IF NOT EXISTS user_info (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR NOT NULL,age INTEGER NOT NULL,
height LONG NOT NULL,weight FLOAT NOT NULL,
married INTEGER NOT NULL, update_time VARCHAR NOT NULL) ;
????????上面的SQL語法與其他數(shù)據(jù)庫的SQL語法有所出入,相關(guān)的注意點說明見下:
- SQL語句不區(qū)分大小寫,無論是create與table這類關(guān)鍵詞,還是表格名稱、字段名稱,都不區(qū)分大小寫。唯一區(qū)分大小寫的是被單引號括起來的字符串值。
- 為避免重復(fù)建表,應(yīng)加上IF NOTEXISTS關(guān)鍵詞,例如CREATE TABLE IF NOTEXISTS 表格名稱······
- ?SQLite支持整型INTEGER、長整型LONG、字符串VARCHAR、浮點數(shù)FLOAT,但不支持布爾類型。布爾類型的數(shù)據(jù)要使用整型保存,如果直接保存布爾數(shù)據(jù),在入庫時SQLite會自動將它轉(zhuǎn)為0或1,其中0表示false,1表示true。
- 建表時需要唯一標(biāo)識字段,它的字段名為_id.創(chuàng)建新表都要加上該字段定義,例如_idINTEGER PRIMARY KEY AUTOINCREMENT NOT NULL。
????????(2)刪除表格
????????刪除表格表格的刪除動作由drop命令完成,格式為“DROP TABLE IF EXISTS 表格名稱;”。下面是刪除用戶信息表的SQL語句例子:
DROP TABLE IF EXISTS user_info;
????????(3)修改表結(jié)構(gòu)
????????表格的修改動作由alter命令完成,格式為“ALTER TABLE 表格名稱 修改操作;”。不過SQLite只支持增加字段,不支持修改字段,也不支持刪除字段。對于字段增加操作,需要在alter之后補充add命令,具體格式如“ALTER TABLE 表格名稱?ADD COLUMN 字段名稱?字段類型;”。下面是給用戶信息表增加手機號字段的SQL語句例子:
ALTER TABLE user_info ADD COLUMN phone VARCHAR;
注????????意 |
---|
SQLite的alter命令每次只能添加一列字段,若要添加多列,就得分多次添加。 |
2.數(shù)據(jù)操縱語言
????????數(shù)據(jù)操縱語言(全稱Data Manipulation Language,簡稱DML)描述了怎樣處理數(shù)據(jù)實體的內(nèi)部記錄。表格記錄的操作類型包括添加、刪除、修改、查詢4類,分別說明如下:
????????(1)添加記錄
????????記錄的添加動作由insert命令完成,格式為“INSERT INTO 表格名稱(以逗號分隔的字段名列表)VALUES(以逗號分隔的字段值列表);”。下面是往用戶信息表插入一條記錄的SQL語句例子:
INSERT INTO user info (name,age,height,weight,married,update time)
VALUES ('張三',20,170,50,0,'20200504');
????????(2)刪除記錄
????????記錄的刪除動作由delete命令完成,格式為“DELETE FROM 表格名稱?WHERE?查詢條件;”,其中查詢條件的表達(dá)式形如“字段名=字段值”,多個字段的條件交集通過“AND”連接,條件并集通過“OR”連接。下面是從用戶信息表刪除指定記錄的SQL語句例子:
DELETE FROM user_info WHERE name='張三';
????????(3)修改記錄
????????記錄的修改動作由update命令完成,格式為“UPDATE 表格名稱 SET 字段名=字段值 WHERE 查詢條件;”。下面是對用戶信息表更新指定記錄的SQL語句例子:
UPDATE user_info SET married=1 WHERE name='張三';
????????(4)查詢記錄
????????記錄的查詢動作由select命令完成,格式為“SELECT 以逗號分隔的字段名列表 FROM 表格名稱 WHERE 查詢條件;”。如果字段名列表填星號(*),則表示查詢該表的所有字段。下面是從用戶信息表查詢指定記錄的SQL語句例子:
SELECT name FROM user_info WHERE name='張三';
????????查詢操作除了比較字段值條件之外,常常需要對查詢結(jié)果排序,此時要在查詢條件后面添加排序條件,對應(yīng)的表達(dá)式為“ORDER BY 字段名ASC或者DESC”,意指對查詢結(jié)果按照某個字段排序,其中ASC代表升序,DESC代表降序。下面是查詢記錄并對結(jié)果排序的SQL語句例子:
SELECT * FROM user_info ORDER BY age ASC;
數(shù)據(jù)庫管理器 SQLiteDatabase
????????SQL語句畢竟只是SQL命令,若要在Java代碼中操縱SQLite,還需專門的工具類。SQLiteDatabase便是Android提供的SQLite數(shù)據(jù)庫管理器,開發(fā)者可以在活動頁面代碼中調(diào)用openOrCreateDatabase方法獲取數(shù)據(jù)庫實例,參考代碼如下:
public class SQLiteDatabase extends AppCompatActivity implements View.OnClickListener {
private TextView tv_database; // 聲明一個文本視圖對象
private String mDatabaseName; // 包含完整路徑的數(shù)據(jù)庫名稱
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_database);
tv_database = findViewById(R.id.tv_database);
findViewById(R.id.btn_database_create).setOnClickListener(this);
findViewById(R.id.btn_database_delete).setOnClickListener(this);
// 生成一個測試數(shù)據(jù)庫的完整路徑
mDatabaseName = getFilesDir() + "/test.db";
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_database_create) {
// 創(chuàng)建或打開數(shù)據(jù)庫。數(shù)據(jù)庫如果不存在就創(chuàng)建它,如果存在就打開它
android.database.sqlite.SQLiteDatabase db = openOrCreateDatabase(mDatabaseName, Context.MODE_PRIVATE, null);
String desc = String.format("數(shù)據(jù)庫%s創(chuàng)建%s", db.getPath(), (db!=null)?"成功":"失敗");
tv_database.setText(desc);
} else if (v.getId() == R.id.btn_database_delete) {
boolean result = deleteDatabase(mDatabaseName); // 刪除數(shù)據(jù)庫
String desc = String.format("數(shù)據(jù)庫%s刪除%s", mDatabaseName, result?"成功":"失敗");
tv_database.setText(desc);
}
}
}
????????首次運行測試App,調(diào)用openOrCreateDatabase方法會自動創(chuàng)建數(shù)據(jù)庫,并返回該數(shù)據(jù)庫的管理器實例,創(chuàng)建結(jié)果如圖所示。
?????????獲得數(shù)據(jù)庫實例之后,就能對該數(shù)據(jù)庫開展各項操作了。數(shù)據(jù)庫管理器SQLiteDatabase提供了若干操作數(shù)據(jù)表的API,常用的方法有3類,列舉如下:
????????1.管理類,用于數(shù)據(jù)庫層面的操作
- openDatabase:打開指定路徑的數(shù)據(jù)庫。
- isOpen:判斷數(shù)據(jù)庫是否已打開。
- close:關(guān)閉數(shù)據(jù)庫。
- getVersion:獲取數(shù)據(jù)庫的版本號。
- setVersion:設(shè)置數(shù)據(jù)庫的版本號。
????????2.事務(wù)類,用于事務(wù)層面的操作
- beginTransaction:開始事務(wù)。
- setTransactionSuccessful:設(shè)置事務(wù)的成功標(biāo)志。
- endTransaction:結(jié)束事務(wù)。執(zhí)行本方法時,系統(tǒng)會判斷之前是否調(diào)用了
setTransactionSuccessful方法,如果之前已調(diào)用該方法就提交事務(wù),如果沒有調(diào)用該方法就回滾事務(wù)。
????????3.數(shù)據(jù)處理類,用于數(shù)據(jù)表層面的操作
- execSQL:執(zhí)行拼接好的SQL控制語句。一般用于建表、刪表、變更表結(jié)構(gòu)。
- delete:刪除符合條件的記錄。
- update:更新符合條件的記錄信息。
- insert:插入一條記錄。
- query:執(zhí)行查詢操作,并返回結(jié)果集的游標(biāo)。
- rawQuery:執(zhí)行拼接好的SQL查詢語句,并返回結(jié)果集的游標(biāo)。
????????在實際開發(fā)中,經(jīng)常用到的是查詢語句,建議先寫好查詢操作的select語句,再調(diào)用rawQuery方法執(zhí)行查詢語句。
數(shù)據(jù)庫幫助器 SQLiteOpenHelper
????????由于SQLiteDatabase存在局限性,一不小心就會重復(fù)打開數(shù)據(jù)庫,處理數(shù)據(jù)庫的升級也不方便,因此Android提供了數(shù)據(jù)庫幫助器SQLiteOpenHelper,幫助開發(fā)者合理使用SQLite。
????????SQLiteOpenHelper的具體使用步驟如下:
????????1.新建一個繼承自SQLiteOpenHelper的數(shù)據(jù)庫操作類,按提示重寫onCreate和onUpgrade兩個方法。其中,onCreate方法只在第一次打開數(shù)據(jù)庫時執(zhí)行,在此可以創(chuàng)建表結(jié)構(gòu);而onUpgrade方法在數(shù)據(jù)庫版本升高時執(zhí)行,在此可以根據(jù)新舊版本號變更表結(jié)構(gòu)。
????????2.為保證數(shù)據(jù)庫的安全使用,需要封裝幾個必要方法,包括獲取單例對象、打開數(shù)據(jù)庫連接、關(guān)閉數(shù)據(jù)庫連接,說明如下:
- 獲取單例對象:確保在App運行過程中數(shù)據(jù)庫只會打開一次,避免重復(fù)打開引起錯誤。
- ·打開數(shù)據(jù)庫連接:SQLite有鎖機制,即讀鎖和寫鎖的處理,故而數(shù)據(jù)庫連接也分兩種,讀連接可
調(diào)用getReadableDatabase 方法獲得,寫連接可調(diào)用getWritableDatabase方法獲得。 - 關(guān)閉數(shù)據(jù)庫連接:數(shù)據(jù)庫操作完畢,調(diào)用數(shù)據(jù)庫實例的close方法關(guān)閉連接。
? ? ? ? 3.提供對表記錄增加、刪除、修改、查詢的操作方法。
????????能被SQLite直接使用的數(shù)據(jù)結(jié)構(gòu)是ContentValues類,它類似于映射Map,也提供了put和get方法存取鍵值對。區(qū)別之處在于:ContentValues的鍵只能是字符串,不能是其他類型。ContentValues主要用于增加記錄和更新記錄,對應(yīng)數(shù)據(jù)庫的insert和lupdate方法。
????????記錄的查詢操作用到了游標(biāo)類Cursor,調(diào)用query和rawQuery方法返回的都是Cursor對象,若要獲取全部的查詢結(jié)果,則需根據(jù)游標(biāo)的指示一條一條遍歷結(jié)果集合。Cursor的常用方法可分為3類,說明如下:
????????1.游標(biāo)控制類方法,用于指定游標(biāo)的狀態(tài)
- close:關(guān)閉游標(biāo)。
- isClosed:判斷游標(biāo)是否關(guān)閉
- isFirst:判斷游標(biāo)是否在開頭。
- isLast:判斷游標(biāo)是否在末尾。
? ? ? ? 2.游標(biāo)移動類方法,把游標(biāo)移動到指定位置
- moveToFirst:移動游標(biāo)到開頭。
- moveToLast:移動游標(biāo)到末尾。
- moveToNext:移動游標(biāo)到下一條記錄。
- moveToPrevious:移動游標(biāo)到上一條記錄。
- move:往后移動游標(biāo)若干條記錄。
- moveToPosition:移動游標(biāo)到指定位置的記錄。
? ? ? ? 3.獲取記錄類方法,可獲取記錄的數(shù)量、類型以及取值
- getCount:獲取結(jié)果記錄的數(shù)量。
- getInt:獲取指定字段的整型值。
- getLong:獲取指定字段的長整型值。
- getFloat:獲取指定字段的浮點數(shù)值。
- getString:獲取指定字段的字符串值。
- getType:獲取指定字段的字段類型。
????????鑒于數(shù)據(jù)庫操作的特殊性,不方便單獨演示某個功能,接下來從創(chuàng)建數(shù)據(jù)庫開始介紹,完整演示一下數(shù)據(jù)庫的讀寫操作。用戶注冊信息的演示頁面包括兩個,分別是記錄保存頁面和記錄讀取頁面,其中記錄保存頁面通過insert方法向數(shù)據(jù)庫添加用戶信息。
????????先打開記錄保存頁面,依次錄入信息并將兩個用戶的注冊信息保存至數(shù)據(jù)庫。再打開記錄讀取頁面,從數(shù)據(jù)庫讀取用戶注冊信息并展示在頁面上。
?????????上述演示頁面主要用到了數(shù)據(jù)庫記錄的添加、查詢和刪除操作,對應(yīng)的數(shù)據(jù)庫幫助器關(guān)鍵代碼如下所示,尤其關(guān)注里面的insert、delete、update和query方法:
package com.example.helloandroid.database;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.example.helloandroid.bean.UserInfo;
import java.util.ArrayList;
import java.util.List;
@SuppressLint("DefaultLocale")
public class UserDBHelper extends SQLiteOpenHelper {
private static final String TAG = "UserDBHelper";
private static final String DB_NAME = "user.db"; // 數(shù)據(jù)庫的名稱
private static final int DB_VERSION = 1; // 數(shù)據(jù)庫的版本號
private static UserDBHelper mHelper = null; // 數(shù)據(jù)庫幫助器的實例
private SQLiteDatabase mDB = null; // 數(shù)據(jù)庫的實例
public static final String TABLE_NAME = "user_info"; // 表的名稱
private UserDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
private UserDBHelper(Context context, int version) {
super(context, DB_NAME, null, version);
}
// 利用單例模式獲取數(shù)據(jù)庫幫助器的唯一實例
public static UserDBHelper getInstance(Context context, int version) {
if (version > 0 && mHelper == null) {
mHelper = new UserDBHelper(context, version);
} else if (mHelper == null) {
mHelper = new UserDBHelper(context);
}
return mHelper;
}
// 打開數(shù)據(jù)庫的讀連接
public SQLiteDatabase openReadLink() {
if (mDB == null || !mDB.isOpen()) {
mDB = mHelper.getReadableDatabase();
}
return mDB;
}
// 打開數(shù)據(jù)庫的寫連接
public SQLiteDatabase openWriteLink() {
if (mDB == null || !mDB.isOpen()) {
mDB = mHelper.getWritableDatabase();
}
return mDB;
}
// 關(guān)閉數(shù)據(jù)庫連接
public void closeLink() {
if (mDB != null && mDB.isOpen()) {
mDB.close();
mDB = null;
}
}
// 創(chuàng)建數(shù)據(jù)庫,執(zhí)行建表語句
public void onCreate(SQLiteDatabase db) {
Log.d(TAG, "onCreate");
String drop_sql = "DROP TABLE IF EXISTS " + TABLE_NAME + ";";
Log.d(TAG, "drop_sql:" + drop_sql);
db.execSQL(drop_sql);
String create_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ "name VARCHAR NOT NULL," + "age INTEGER NOT NULL,"
+ "height INTEGER NOT NULL," + "weight FLOAT NOT NULL,"
+ "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL"
//演示數(shù)據(jù)庫升級時要先把下面這行注釋
+ ",phone VARCHAR" + ",password VARCHAR"
+ ");";
Log.d(TAG, "create_sql:" + create_sql);
db.execSQL(create_sql); // 執(zhí)行完整的SQL語句
}
// 升級數(shù)據(jù)庫,執(zhí)行表結(jié)構(gòu)變更語句
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.d(TAG, "onUpgrade oldVersion=" + oldVersion + ", newVersion=" + newVersion);
if (newVersion > 1) {
//Android的ALTER命令不支持一次添加多列,只能分多次添加
String alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "phone VARCHAR;";
Log.d(TAG, "alter_sql:" + alter_sql);
db.execSQL(alter_sql);
alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "password VARCHAR;";
Log.d(TAG, "alter_sql:" + alter_sql);
db.execSQL(alter_sql); // 執(zhí)行完整的SQL語句
}
}
// 根據(jù)指定條件刪除表記錄
public int delete(String condition) {
// 執(zhí)行刪除記錄動作,該語句返回刪除記錄的數(shù)目
return mDB.delete(TABLE_NAME, condition, null);
}
// 刪除該表的所有記錄
public int deleteAll() {
// 執(zhí)行刪除記錄動作,該語句返回刪除記錄的數(shù)目
return mDB.delete(TABLE_NAME, "1=1", null);
}
// 往該表添加一條記錄
public long insert(UserInfo info) {
List<UserInfo> infoList = new ArrayList<UserInfo>();
infoList.add(info);
return insert(infoList);
}
// 往該表添加多條記錄
public long insert(List<UserInfo> infoList) {
long result = -1;
for (int i = 0; i < infoList.size(); i++) {
UserInfo info = infoList.get(i);
List<UserInfo> tempList = new ArrayList<UserInfo>();
// 如果存在同名記錄,則更新記錄
// 注意條件語句的等號后面要用單引號括起來
if (info.name != null && info.name.length() > 0) {
String condition = String.format("name='%s'", info.name);
tempList = query(condition);
if (tempList.size() > 0) {
update(info, condition);
result = tempList.get(0).rowid;
continue;
}
}
// 如果存在同樣的手機號碼,則更新記錄
if (info.phone != null && info.phone.length() > 0) {
String condition = String.format("phone='%s'", info.phone);
tempList = query(condition);
if (tempList.size() > 0) {
update(info, condition);
result = tempList.get(0).rowid;
continue;
}
}
// 不存在唯一性重復(fù)的記錄,則插入新記錄
ContentValues cv = new ContentValues();
cv.put("name", info.name);
cv.put("age", info.age);
cv.put("height", info.height);
cv.put("weight", info.weight);
cv.put("married", info.married);
cv.put("update_time", info.update_time);
cv.put("phone", info.phone);
cv.put("password", info.password);
// 執(zhí)行插入記錄動作,該語句返回插入記錄的行號
result = mDB.insert(TABLE_NAME, "", cv);
if (result == -1) { // 添加成功則返回行號,添加失敗則返回-1
return result;
}
}
return result;
}
// 根據(jù)條件更新指定的表記錄
public int update(UserInfo info, String condition) {
ContentValues cv = new ContentValues();
cv.put("name", info.name);
cv.put("age", info.age);
cv.put("height", info.height);
cv.put("weight", info.weight);
cv.put("married", info.married);
cv.put("update_time", info.update_time);
cv.put("phone", info.phone);
cv.put("password", info.password);
// 執(zhí)行更新記錄動作,該語句返回更新的記錄數(shù)量
return mDB.update(TABLE_NAME, cv, condition, null);
}
public int update(UserInfo info) {
// 執(zhí)行更新記錄動作,該語句返回更新的記錄數(shù)量
return update(info, "rowid=" + info.rowid);
}
// 根據(jù)指定條件查詢記錄,并返回結(jié)果數(shù)據(jù)列表
public List<UserInfo> query(String condition) {
String sql = String.format("select rowid,_id,name,age,height," +
"weight,married,update_time,phone,password " +
"from %s where %s;", TABLE_NAME, condition);
Log.d(TAG, "query sql: " + sql);
List<UserInfo> infoList = new ArrayList<UserInfo>();
// 執(zhí)行記錄查詢動作,該語句返回結(jié)果集的游標(biāo)
Cursor cursor = mDB.rawQuery(sql, null);
// 循環(huán)取出游標(biāo)指向的每條記錄
while (cursor.moveToNext()) {
UserInfo info = new UserInfo();
info.rowid = cursor.getLong(0); // 取出長整型數(shù)
info.xuhao = cursor.getInt(1); // 取出整型數(shù)
info.name = cursor.getString(2); // 取出字符串
info.age = cursor.getInt(3); // 取出整型數(shù)
info.height = cursor.getLong(4); // 取出長整型數(shù)
info.weight = cursor.getFloat(5); // 取出浮點數(shù)
//SQLite沒有布爾型,用0表示false,用1表示true
info.married = (cursor.getInt(6) == 0) ? false : true;
info.update_time = cursor.getString(7); // 取出字符串
info.phone = cursor.getString(8); // 取出字符串
info.password = cursor.getString(9); // 取出字符串
infoList.add(info);
}
cursor.close(); // 查詢完畢,關(guān)閉數(shù)據(jù)庫游標(biāo)
return infoList;
}
// 根據(jù)手機號碼查詢指定記錄
public UserInfo queryByPhone(String phone) {
UserInfo info = null;
List<UserInfo> infoList = query(String.format("phone='%s'", phone));
if (infoList.size() > 0) { // 存在該號碼的登錄信息
info = infoList.get(0);
}
return info;
}
}
數(shù)據(jù)寫入Java代碼:
package com.example.helloandroid;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import com.example.helloandroid.database.UserDBHelper;
import com.example.helloandroid.bean.UserInfo;
import com.example.helloandroid.DateUtil;
import util.ToastUtil;
public class SQLiteOpenHelper extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private UserDBHelper mHelper; // 聲明一個用戶數(shù)據(jù)庫幫助器的對象
private android.widget.EditText et_name; // 聲明一個編輯框?qū)ο? private android.widget.EditText et_age; // 聲明一個編輯框?qū)ο? private android.widget.EditText et_height; // 聲明一個編輯框?qū)ο? private EditText et_weight; // 聲明一個編輯框?qū)ο? private boolean isMarried = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_open_helper);
et_name = findViewById(R.id.et_name);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
CheckBox ck_married = findViewById(R.id.ck_married);
ck_married.setOnCheckedChangeListener(this);
findViewById(R.id.btn_save).setOnClickListener(this);
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
isMarried = isChecked;
}
@Override
protected void onStart() {
super.onStart();
// 獲得數(shù)據(jù)庫幫助器的實例
mHelper = UserDBHelper.getInstance(this, 1);
mHelper.openWriteLink(); // 打開數(shù)據(jù)庫幫助器的寫連接
}
@Override
protected void onStop() {
super.onStop();
mHelper.closeLink(); // 關(guān)閉數(shù)據(jù)庫連接
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_save) {
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
if (TextUtils.isEmpty(name)) {
ToastUtil.show(this, "請先填寫姓名");
return;
} else if (TextUtils.isEmpty(age)) {
ToastUtil.show(this, "請先填寫年齡");
return;
} else if (TextUtils.isEmpty(height)) {
ToastUtil.show(this, "請先填寫身高");
return;
} else if (TextUtils.isEmpty(weight)) {
ToastUtil.show(this, "請先填寫體重");
return;
}
// 以下聲明一個用戶信息對象,并填寫它的各字段值
UserInfo info = new UserInfo();
info.name = name;
info.age = Integer.parseInt(age);
info.height = Long.parseLong(height);
info.weight = Float.parseFloat(weight);
info.married = isMarried;
info.update_time = DateUtil.getNowDateTime("yyyy-MM-dd HH:mm:ss");
mHelper.insert(info); // 執(zhí)行數(shù)據(jù)庫幫助器的插入操作
ToastUtil.show(this, "數(shù)據(jù)已寫入SQLite數(shù)據(jù)庫");
}
}
}
優(yōu)化記住密碼功能
????????真正的記住密碼功能應(yīng)當(dāng)是這樣的:先輸入手機號碼,然后根據(jù)手機號碼匹配保存的密碼,一個手機號碼對應(yīng)一個密碼,從而實現(xiàn)具體手機號碼的密碼記憶功能。
????????現(xiàn)在運用數(shù)據(jù)庫技術(shù)分條存儲各用戶的登錄信息,并支持根據(jù)手機號查找登錄信息,從而同時記住多個手機號的密碼。具體的改造主要有下列3點:
????????(1)聲明一個數(shù)據(jù)庫的幫助器對象,然后在活動頁面的onResume方法中打開數(shù)據(jù)庫連接,在onPasue方法中關(guān)閉數(shù)據(jù)庫連接,示例代碼如下:
private UserDBHelper mHelper; //聲明一個用戶數(shù)據(jù)庫的幫助器對象
@Override
protected void onResume() {
super.onResume();
mHelper = UserDBHelper.getInstance(this, 1); // 獲得用戶數(shù)據(jù)庫幫助器的實例
mHelper.openWriteLink(); // 恢復(fù)頁面,則打開數(shù)據(jù)庫連接
}
@Override
protected void onPause() {
super.onPause();
mHelper.closeLink(); //暫停頁面,則關(guān)閉數(shù)據(jù)庫連接
}
????????(2)登錄成功時,如果用戶勾選了“記住密碼”復(fù)選框,就將手機號碼及其密碼保存至數(shù)據(jù)庫。也就是在loginSuccess方法中增加如下代碼:
// 如果勾選了“記住密碼”,則把手機號碼和密碼保存為數(shù)據(jù)庫的用戶表記錄
if (isRemember) {
UserInfo info = new UserInfo(); // 創(chuàng)建一個用戶信息對象
info.phone = et_phone.getText().toString();
info.password = et_password.getText().toString();
info.update_time = DateUtil.getNowDateTime("yyyy-MM-dd HH:mm:ss");
mHelper.insert(info); // 往用戶數(shù)據(jù)庫添加登錄成功的用戶信息
}
????????(3)再次打開登錄頁面,用戶輸入手機號后點擊密碼框時,App根據(jù)手機號到數(shù)據(jù)庫查找登錄信息,并將記錄結(jié)果中的密碼填入密碼框。其中根據(jù)手機號碼查找登錄信息,要求在幫助器代碼中添加以下方法,用于找到指定手機的登錄密碼:
// 根據(jù)手機號碼查詢指定記錄
public UserInfo queryByPhone(String phone) {
UserInfo info = null;
List<UserInfo> infoList = query(String.format("phone='%s'", phone));
if (infoList.size() > 0) { // 存在該號碼的登錄信息
info = infoList.get(0);
}
return info;
}
????????此外,上面第3點的點擊密碼框觸發(fā)查詢操作,用到了編輯框的焦點變更事件。就本案例而言,光標(biāo)切到密碼框觸發(fā)焦點變更事件,具體處理邏輯要求重寫監(jiān)聽器的onFocusChange方法,重寫后的方法代碼如下:
@Override
public void onFocusChange(View v, boolean hasFocus) {
String phone = et_phone.getText().toString();
// 判斷是否是密碼編輯框發(fā)生焦點變化
if (v.getId() == R.id.et_password) {
// 用戶已輸入手機號碼,且密碼框獲得焦點
if (phone.length() > 0 && hasFocus) {
// 根據(jù)手機號碼到數(shù)據(jù)庫中查詢用戶記錄
UserInfo info = mHelper.queryByPhone(phone);
if (info != null) {
// 找到用戶記錄,則自動在密碼框中填寫該用戶的密碼
et_password.setText(info.password);
}
}
}
}
????????重新運行測試App,先打開登錄頁面,勾選“記住密碼”復(fù)選框,并確保本次登錄成功。然后再次進入登錄頁面,輸入手機號碼后光標(biāo)還停留在手機框。接著點擊密碼框,光標(biāo)隨之跳轉(zhuǎn)到密碼框,此時密碼框自動填入了該號碼對應(yīng)的密碼串。由效果圖可見,這次實現(xiàn)了真正意義上的記住密碼功能。文章來源:http://www.zghlxwxcb.cn/news/detail-465887.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-465887.html
到了這里,關(guān)于Android Studio 學(xué)習(xí)記錄-數(shù)據(jù)庫的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!