数据定义语言
创建表格
语法:CREATE TABLE IF NOT EXISTS 表格名称(以逗号分隔的各字段定义)
[示例]
CREATE TABLE IF NOT EXISTS user_info(
_id INTERGER 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
);
注意:
1.SQL语句不区分大小写,唯一区分大小写的是被单引号库括起来的字符串值
2.为了避免重复建表,应该加上IF NOT EXISTS 关键字
3.SQLite支持整型INTEGER,长整型LONG、字符串VARCHAR、浮点数FLOAT,但不支持布尔类型,布尔类型的数据要用整型保存,如果直接保存不而数据,在入库时SQLite会自动将它转为0或者1,其中0表示false,1表示true
4.建表时需要唯一标识字段,它的字段名为_id.创建新表都要加上该字段定义。例如_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
删除表格
语法:drop table if exists 表格名称
drop table if exists user_info;
修改表结构
语法:alter table 表格名称 修改操作
特别需要注意的是,SQLite只支持增加字段,不支持修改字段,也不支持删除字段。
对于字段增加操作,语法为
字段增加操作语法:alter table 表格名称 add column 字段名称 字段类型
alter table user_info add column phone varchar;
并且,SQLite的alter语句每次只能添加一列字段,若要添加多列,需要分多次添加。
数据操作语言
添加记录
语法:insert into 表格名称(以逗号分隔的字段名列表) values (以逗号分割的字段值列表)
insert into user_info (name,age,height,weight,married,update_time) values('张三',20,170,50,0,'20200504');
删除记录
语法:delete from 表格名称 where 查询条件
delete from user_info where name = '张三';
修改记录
语法:update 表格名称 set 字段名=字段值 where 查询条件
update user_info set married = 1 where name = '张三';
查询记录
语法:select 以逗号分隔的字段名列表或星号 from 表格名称 where 查询条件 order by 字段名 asc或desc;
select * from user_info order by age asc;
使用Java代码操作数据库
若要在Java代码中操作SQLite,需要有专门的工具类。SQLiteDatabase表示Android提供的SQLite的数据库管理器,开发者可以在活动页面代码调用openOrCreateDatabase方法来获取数据库实例。
创建和删除数据库
首先先来创建一个名为activity_database.xml的文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_database_create"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="创建数据库"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_database_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="删除数据库"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
效果大致如图:
其次,创建一个名为DatabaseActivity.java的文件来操作数据库。
package com.example.sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class DatabaseActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_database;
private String mDatabaseName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_database);
tv_database = findViewById(R.id.tv_database);
findViewById(R.id.btn_database_create).setOnClickListener(this);
findViewById(R.id.btn_database_delete).setOnClickListener(this);
// 生成一个测试数据库的完整路径
mDatabaseName = getFilesDir() + "/test.db";
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_database_create) {
// 创建或打开数据库。数据库如果不存在就创建它,如果存在就打开它
SQLiteDatabase db = openOrCreateDatabase(mDatabaseName, Context.MODE_PRIVATE, null);
String desc = String.format("数据库%s创建%s", db.getPath(), (db!=null)?"成功":"失败");
tv_database.setText(desc);
} else if (v.getId() == R.id.btn_database_delete) {
boolean result = deleteDatabase(mDatabaseName); // 删除数据库
String desc = String.format("数据库%s删除%s", mDatabaseName, result?"成功":"失败");
tv_database.setText(desc);
}
}
}
获得数据库实例之后,就可以对数据库开展各种操作了。
SQLiteDatabase提供了若干操作数据表的API,常见的方法有三类:
管理类 | 用于数据库层面的操作 |
---|---|
openDatabase | 打开指定路径的数据库 |
isOpen | 判断数据库是否已经打开 |
close | 关闭数据库 |
getVersion | 获取数据库的版本号 |
setVersion | 设置数据库的版本号 |
管理类 | 用于数据库层面的操作 |
---|---|
openDatabase | 打开指定路径的数据库 |
isOpen | 判断数据库是否已经打开 |
close | 关闭数据库 |
getVersion | 获取数据库的版本号 |
setVersion | 设置数据库的版本号 |
数据处理类 | 用于数据表层面的操作 |
---|---|
execSQL | 执行拼接好的SQL语句。一般用于建表、删表、变更表结构 |
delete | 删除符合条件的记录 |
update | 更新符合条件的记录信息 |
insert | 插入一条记录 |
query | 执行查询操作,并返回结果集的游标 |
rawQuery | 执行拼接好的SQL语句,并返回结果集游标 |
数据库帮助器
由于SQLiteDatabase存在局限性,一不小心就会重复打开数据库,处理数据库的升级也不方便。因此Android提供了数据库帮助器SQLiteOpenHelper,帮助开发者合理利用SQLite。
SQLiteHelper的具体使用步骤如下:
步骤一:新建一个继承自SQLiteOpenHelper的数据库操作类,按照提示重写onCreate和onUpdate两个方法。其中onCreate只在第一次打开数据库时执行,在此可以创建表结构;而onUpgrade方法在数据库版本高时执行,在此可以根据新旧版本号变更表结构。
步骤二:为保证数据库安全使用,需要封装几个必要的方法,包括获取单例对象、打开数据库连接、关闭数据库连接,说明如下:
- 获取单例对象:确保在App运行过程中数据库只会打开一次,避免重复打开引起错误。
- 打开数据库连接:SQLite又锁机制,即读锁和写锁的处理;故而数据库连接也分为两种,读链接可以直接调用getReadableDatabase方法获得,写连接可以调用getWritableDatabase获得
- 关闭数据库连接:数据库操作完毕,调用数据库实例的close方法关闭连接。
步骤三:提供对表记录增加、删除、修改和查询的操作方法。
能被SQLite直接使用的数据结构是ContentValues类,它类似于映射Map,也提供了put和get方法存取键值对。区别之处在于:ContentValues的键只能是字符串,不能是其他类型。ContentValues主要用于增加记录和更新记录,对应数据库的insert和update方法。
记录的查询操作用到了游标类Cursor,调用query和rawQuery方法返回的都是Cursor对象,若要获取全部的查询结果,则需要根据游标的指示一条一条地遍历结果集合。Cursor的常用方法可分为3类。说明如下:
游标
游标控制类方法 | 用于指定游标的状态 |
---|---|
close | 关闭游标 |
isClosed | 判断游标是否关闭 |
isFirst | 判断游标是否在开头 |
isLast | 判断游标是否在末尾 |
游标移动类方法 | 把游标移动到指定位置 |
---|---|
moveToFirst | 移动游标到开头 |
moveToLast | 移动到游标末尾 |
moveToNext | 移动游标到下一条记录 |
moveToPrevious | 移动游标到上一条记录 |
move | 往后移动游标若干条记录 |
moevToPosition | 移动游标到指定位置的记录 |
获取记录类方法 | 可获取记录的数量、类型以及取值 |
---|---|
getCount | 获取结果记录的数量 |
getInt | 获取指定字段的整数值 |
getLong | 获取指定字段的长整型值 |
getFloat | 获取指定字段的浮点数值 |
getString | 获取指定字段的字符串值 |
getType | 获取指定字段的字段类型 |
接下来,将通过具体实例来进一步加深理解。
案例分析
写入数据库
上图为数据保存界面,用户需要输入相关信息后,点击保存到数据库即可。
具体代码activity_sqlite_write.xml如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="姓名:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_name"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入姓名"
android:inputType="text"
android:maxLength="12"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="年龄:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_age"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入年龄"
android:inputType="number"
android:maxLength="2"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_height"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="身高:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_height"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_height"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入身高"
android:inputType="number"
android:maxLength="3"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_weight"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="体重:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_weight"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_weight"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入体重"
android:inputType="numberDecimal"
android:maxLength="5"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<CheckBox
android:id="@+id/ck_married"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:checked="false"
android:text="已婚"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存到数据库"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
在此之前,先附上一些界面修饰的基本代码。
editext_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/shape_edit_focus"/>
<item android:drawable="@drawable/shape_edit_normal"/>
</selector>
shape_edit_focus.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#ff0000ff" />
<corners android:radius="5dp" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
</shape>
shape_edit_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#ffaaaaaa" />
<corners android:radius="5dp" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
</shape>
shape_oval_red.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#ffaaaaaa" />
<corners android:radius="5dp" />
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp" />
</shape>
接着,就是书写写入数据库页面的活动类了。
在书写正式代码之前,需要准备编写一些工具类,便于直接调用
ToastUtil.java
package com.example.sqlite.util;
import android.content.Context;
import android.widget.Toast;
public class ToastUtil {
public static void show(Context ctx, String desc) {
Toast.makeText(ctx, desc, Toast.LENGTH_SHORT).show();
}
}
DateUtil.java
import android.annotation.SuppressLint;
import android.text.TextUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@SuppressLint("SimpleDateFormat")
public class DateUtil {
// 获取当前的日期时间
public static String getNowDateTime(String formatStr) {
String format = formatStr;
if (TextUtils.isEmpty(format)) {
format = "yyyyMMddHHmmss";
}
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(new Date());
}
// 获取当前的时间
public static String getNowTime() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
return sdf.format(new Date());
}
// 获取当前的时间(精确到毫秒)
public static String getNowTimeDetail() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
return sdf.format(new Date());
}
}
封装数据的UserInfo.java
//用户信息
public class UserInfo {
public long rowid; // 行号
public int xuhao; // 序号
public String name; // 姓名
public int age; // 年龄
public long height; // 身高
public float weight; // 体重
public boolean married; // 婚否
public String update_time; // 更新时间
public String phone; // 手机号
public String password; // 密码
public UserInfo() {
rowid = 0L;
xuhao = 0;
name = "";
age = 0;
height = 0L;
weight = 0.0f;
married = false;
update_time = "";
phone = "";
password = "";
}
}
SQLiteWriteActivity.java
package com.example.sqlite;
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 androidx.appcompat.app.AppCompatActivity;
import com.example.sqlite.bean.UserInfo;
import com.example.sqlite.database.UserDBHelper;
import com.example.sqlite.util.DateUtil;
import com.example.sqlite.util.ToastUtil;
public class SQLiteWriteActivity extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private UserDBHelper mHelper; // 声明一个用户数据库帮助器的对象
private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private boolean isMarried = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_write);
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();
// 获得数据库帮助器的实例
mHelper = UserDBHelper.getInstance(this, 1);
mHelper.openWriteLink(); // 打开数据库帮助器的写连接
}
@Override
protected void onStop() {
super.onStop();
mHelper.closeLink(); // 关闭数据库连接
}
@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); // 执行数据库帮助器的插入操作
ToastUtil.show(this, "数据已写入SQLite数据库");
}
}
}
数据库帮助器UserDBHelper的封装
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.chapter06.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"; // 数据库的名称
private static final int DB_VERSION = 1; // 数据库的版本号
private static UserDBHelper mHelper = null; // 数据库帮助器的实例
private SQLiteDatabase mDB = null; // 数据库的实例
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);
}
// 利用单例模式获取数据库帮助器的唯一实例
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;
}
// 打开数据库的读连接
public SQLiteDatabase openReadLink() {
if (mDB == null || !mDB.isOpen()) {
mDB = mHelper.getReadableDatabase();
}
return mDB;
}
// 打开数据库的写连接
public SQLiteDatabase openWriteLink() {
if (mDB == null || !mDB.isOpen()) {
mDB = mHelper.getWritableDatabase();
}
return mDB;
}
// 关闭数据库连接
public void closeLink() {
if (mDB != null && mDB.isOpen()) {
mDB.close();
mDB = null;
}
}
// 创建数据库,执行建表语句
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"
//演示数据库升级时要先把下面这行注释
+ ",phone VARCHAR" + ",password VARCHAR"
+ ");";
Log.d(TAG, "create_sql:" + create_sql);
db.execSQL(create_sql); // 执行完整的SQL语句
}
// 升级数据库,执行表结构变更语句
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); // 执行完整的SQL语句
}
}
// 根据指定条件删除表记录
public int delete(String condition) {
// 执行删除记录动作,该语句返回删除记录的数目
return mDB.delete(TABLE_NAME, condition, null);
}
// 删除该表的所有记录
public int deleteAll() {
// 执行删除记录动作,该语句返回删除记录的数目
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;
}
}
// 不存在唯一性重复的记录,则插入新记录
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);
// 执行插入记录动作,该语句返回插入记录的行号
result = mDB.insert(TABLE_NAME, "", cv);
if (result == -1) { // 添加成功则返回行号,添加失败则返回-1
return result;
}
}
return result;
}
// 根据条件更新指定的表记录
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);
// 执行更新记录动作,该语句返回更新的记录数量
return mDB.update(TABLE_NAME, cv, condition, null);
}
public int update(UserInfo info) {
// 执行更新记录动作,该语句返回更新的记录数量
return update(info, "rowid=" + info.rowid);
}
// 根据指定条件查询记录,并返回结果数据列表
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>();
// 执行记录查询动作,该语句返回结果集的游标
Cursor cursor = mDB.rawQuery(sql, null);
// 循环取出游标指向的每条记录
while (cursor.moveToNext()) {
UserInfo info = new UserInfo();
info.rowid = cursor.getLong(0); // 取出长整型数
info.xuhao = cursor.getInt(1); // 取出整型数
info.name = cursor.getString(2); // 取出字符串
info.age = cursor.getInt(3); // 取出整型数
info.height = cursor.getLong(4); // 取出长整型数
info.weight = cursor.getFloat(5); // 取出浮点数
//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(); // 查询完毕,关闭数据库游标
return infoList;
}
// 根据手机号码查询指定记录
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;
}
}
读取数据库
activity_sqlite_read.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除所有记录"
android:textColor="@color/black"
android:textSize="17sp" />
<TextView
android:id="@+id/tv_sqlite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
SQLReadActivity.java
package com.example.chapter06;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.chapter06.bean.UserInfo;
import com.example.chapter06.database.UserDBHelper;
import com.example.chapter06.util.ToastUtil;
import java.util.List;
@SuppressLint("DefaultLocale")
public class SQLiteReadActivity extends AppCompatActivity implements View.OnClickListener {
private UserDBHelper mHelper; // 声明一个用户数据库帮助器的对象
private TextView tv_sqlite;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_read);
tv_sqlite = findViewById(R.id.tv_sqlite);
findViewById(R.id.btn_delete).setOnClickListener(this);
}
@Override
protected void onStart() {
super.onStart();
// 获得数据库帮助器的实例
mHelper = UserDBHelper.getInstance(this, 1);
mHelper.openReadLink(); // 打开数据库帮助器的读连接
readSQLite(); // 读取数据库中保存的所有用户记录
}
@Override
protected void onStop() {
super.onStop();
mHelper.closeLink(); // 关闭数据库连接
}
// 读取数据库中保存的所有用户记录
private void readSQLite() {
if (mHelper == null) {
ToastUtil.show(this, "数据库连接为空");
return;
}
// 执行数据库帮助器的查询操作
List<UserInfo> userList = mHelper.query("1=1");
String desc = String.format("数据库查询到%d条记录,详情如下:", userList.size());
for (int i = 0; i < userList.size(); i++) {
UserInfo info = userList.get(i);
desc = String.format("%s\n第%d条记录信息如下:", desc, i + 1);
desc = String.format("%s\n 姓名为%s", desc, info.name);
desc = String.format("%s\n 年龄为%d", desc, info.age);
desc = String.format("%s\n 身高为%d", desc, info.height);
desc = String.format("%s\n 体重为%f", desc, info.weight);
desc = String.format("%s\n 婚否为%b", desc, info.married);
desc = String.format("%s\n 更新时间为%s", desc, info.update_time);
}
if (userList.size() <= 0) {
desc = "数据库查询到的记录为空";
}
tv_sqlite.setText(desc);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_delete) {
mHelper.closeLink(); // 关闭数据库连接
mHelper.openWriteLink(); // 打开数据库帮助器的写连接
mHelper.deleteAll(); // 删除所有记录
mHelper.closeLink(); // 关闭数据库连接
mHelper.openReadLink(); // 打开数据库帮助器的读连接
readSQLite(); // 读取数据库中保存的所有用户记录
ToastUtil.show(this, "已删除所有记录");
}
}
}
LoginSQLiteActivity.java
package com.example.chapter06;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.chapter06.bean.UserInfo;
import com.example.chapter06.database.UserDBHelper;
import com.example.chapter06.util.DateUtil;
import com.example.chapter06.util.ViewUtil;
import java.util.Random;
@SuppressLint("DefaultLocale")
public class LoginSQLiteActivity extends AppCompatActivity implements View.OnClickListener, OnFocusChangeListener {
private RadioGroup rg_login; // 声明一个单选组对象
private RadioButton rb_password; // 声明一个单选按钮对象
private RadioButton rb_verifycode; // 声明一个单选按钮对象
private EditText et_phone; // 声明一个编辑框对象
private TextView tv_password; // 声明一个文本视图对象
private EditText et_password; // 声明一个编辑框对象
private Button btn_forget; // 声明一个按钮控件对象
private CheckBox ck_remember; // 声明一个复选框对象
private int mRequestCode = 0; // 跳转页面时的请求代码
private boolean isRemember = false; // 是否记住密码
private String mPassword = "111111"; // 默认密码
private String mVerifyCode; // 验证码
private UserDBHelper mHelper; // 声明一个用户数据库的帮助器对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_sqlite);
rg_login = findViewById(R.id.rg_login);
rb_password = findViewById(R.id.rb_password);
rb_verifycode = findViewById(R.id.rb_verifycode);
et_phone = findViewById(R.id.et_phone);
tv_password = findViewById(R.id.tv_password);
et_password = findViewById(R.id.et_password);
btn_forget = findViewById(R.id.btn_forget);
ck_remember = findViewById(R.id.ck_remember);
// 给rg_login设置单选监听器
rg_login.setOnCheckedChangeListener(new RadioListener());
// 给ck_remember设置勾选监听器
ck_remember.setOnCheckedChangeListener(new CheckListener());
// 给et_phone添加文本变更监听器
et_phone.addTextChangedListener(new HideTextWatcher(et_phone, 11));
// 给et_password添加文本变更监听器
et_password.addTextChangedListener(new HideTextWatcher(et_password, 6));
btn_forget.setOnClickListener(this);
findViewById(R.id.btn_login).setOnClickListener(this);
// 给密码编辑框注册一个焦点变化监听器,一旦焦点发生变化,就触发监听器的onFocusChange方法
et_password.setOnFocusChangeListener(this);
}
// 定义登录方式的单选监听器
private class RadioListener implements RadioGroup.OnCheckedChangeListener {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.rb_password) { // 选择了密码登录
tv_password.setText("登录密码:");
et_password.setHint("请输入密码");
btn_forget.setText("忘记密码");
ck_remember.setVisibility(View.VISIBLE);
} else if (checkedId == R.id.rb_verifycode) { // 选择了验证码登录
tv_password.setText(" 验证码:");
et_password.setHint("请输入验证码");
btn_forget.setText("获取验证码");
ck_remember.setVisibility(View.GONE);
}
}
}
// 定义是否记住密码的勾选监听器
private class CheckListener implements CompoundButton.OnCheckedChangeListener {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.getId() == R.id.ck_remember) {
isRemember = isChecked;
}
}
}
// 定义一个编辑框监听器,在输入文本达到指定长度时自动隐藏输入法
private class HideTextWatcher implements TextWatcher {
private EditText mView; // 声明一个编辑框对象
private int mMaxLength; // 声明一个最大长度变量
public HideTextWatcher(EditText v, int maxLength) {
super();
mView = v;
mMaxLength = maxLength;
}
// 在编辑框的输入文本变化前触发
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
// 在编辑框的输入文本变化时触发
public void onTextChanged(CharSequence s, int start, int before, int count) {}
// 在编辑框的输入文本变化后触发
public void afterTextChanged(Editable s) {
String str = s.toString(); // 获得已输入的文本字符串
// 输入文本达到11位(如手机号码),或者达到6位(如登录密码)时关闭输入法
if ((str.length() == 11 && mMaxLength == 11)
|| (str.length() == 6 && mMaxLength == 6)) {
ViewUtil.hideOneInputMethod(LoginSQLiteActivity.this, mView); // 隐藏输入法软键盘
}
}
}
@Override
public void onClick(View v) {
String phone = et_phone.getText().toString();
if (v.getId() == R.id.btn_forget) { // 点击了“忘记密码”按钮
if (phone.length() < 11) { // 手机号码不足11位
Toast.makeText(this, "请输入正确的手机号", Toast.LENGTH_SHORT).show();
return;
}
if (rb_password.isChecked()) { // 选择了密码方式校验,此时要跳到找回密码页面
// 以下携带手机号码跳转到找回密码页面
Intent intent = new Intent(this, LoginForgetActivity.class);
intent.putExtra("phone", phone);
startActivityForResult(intent, mRequestCode); // 携带意图返回上一个页面
} else if (rb_verifycode.isChecked()) { // 选择了验证码方式校验,此时要生成六位随机数字验证码
// 生成六位随机数字的验证码
mVerifyCode = String.format("%06d", new Random().nextInt(999999));
// 以下弹出提醒对话框,提示用户记住六位验证码数字
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("请记住验证码");
builder.setMessage("手机号" + phone + ",本次验证码是" + mVerifyCode + ",请输入验证码");
builder.setPositiveButton("好的", null);
AlertDialog alert = builder.create();
alert.show(); // 显示提醒对话框
}
} else if (v.getId() == R.id.btn_login) { // 点击了“登录”按钮
if (phone.length() < 11) { // 手机号码不足11位
Toast.makeText(this, "请输入正确的手机号", Toast.LENGTH_SHORT).show();
return;
}
if (rb_password.isChecked()) { // 密码方式校验
if (!et_password.getText().toString().equals(mPassword)) {
Toast.makeText(this, "请输入正确的密码", Toast.LENGTH_SHORT).show();
} else { // 密码校验通过
loginSuccess(); // 提示用户登录成功
}
} else if (rb_verifycode.isChecked()) { // 验证码方式校验
if (!et_password.getText().toString().equals(mVerifyCode)) {
Toast.makeText(this, "请输入正确的验证码", Toast.LENGTH_SHORT).show();
} else { // 验证码校验通过
loginSuccess(); // 提示用户登录成功
}
}
}
}
// 从下一个页面携带参数返回当前页面时触发
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == mRequestCode && data != null) {
// 用户密码已改为新密码,故更新密码变量
mPassword = data.getStringExtra("new_password");
}
}
// 从修改密码页面返回登录页面,要清空密码的输入框
@Override
protected void onRestart() {
super.onRestart();
et_password.setText("");
}
@Override
protected void onResume() {
super.onResume();
mHelper = UserDBHelper.getInstance(this, 1); // 获得用户数据库帮助器的实例
mHelper.openWriteLink(); // 恢复页面,则打开数据库连接
}
@Override
protected void onPause() {
super.onPause();
mHelper.closeLink(); // 暂停页面,则关闭数据库连接
}
// 校验通过,登录成功
private void loginSuccess() {
String desc = String.format("您的手机号码是%s,恭喜你通过登录验证,点击“确定”按钮返回上个页面",
et_phone.getText().toString());
// 以下弹出提醒对话框,提示用户登录成功
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("登录成功");
builder.setMessage(desc);
builder.setPositiveButton("确定返回", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish(); // 结束当前的活动页面
}
});
builder.setNegativeButton("我再看看", null);
AlertDialog alert = builder.create();
alert.show();
// 如果勾选了“记住密码”,则把手机号码和密码保存为数据库的用户表记录
if (isRemember) {
UserInfo info = new UserInfo(); // 创建一个用户信息对象
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); // 往用户数据库添加登录成功的用户信息
}
}
//下面这个函数为自动填充密码的逻辑
// 焦点变更事件的处理方法,hasFocus表示当前控件是否获得焦点。
// 为什么光标进入密码框事件不选onClick?因为要点两下才会触发onClick动作(第一下是切换焦点动作)
@Override
public void onFocusChange(View v, boolean hasFocus) {
String phone = et_phone.getText().toString();
// 判断是否是密码编辑框发生焦点变化
if (v.getId() == R.id.et_password) {
// 用户已输入手机号码,且密码框获得焦点
if (phone.length() > 0 && hasFocus) {
// 根据手机号码到数据库中查询用户记录
UserInfo info = mHelper.queryByPhone(phone);
if (info != null) {
// 找到用户记录,则自动在密码框中填写该用户的密码
et_password.setText(info.password);
}
}
}
}
}