Android Studio 学习记录-数据库

目录

SQL的基本语法

1.数据定义语言

2.数据操纵语言

数据库管理器 SQLiteDatabase

数据库帮助器 SQLiteOpenHelper

优化记住密码功能


        本文介绍Android的数据库存储方式-SQLite的使用方法,包括:SQLite用到了哪些SQL语法,如何使用数据库管理器操纵SQLite,如何使用数据库帮助器简化数据库操作,以及如何利用SQLite改进登录页面的记住密码功能。

SQL的基本语法

        SQL本质上是一种编程语言,它的学名叫作“结构化查询语言”(全称为Structured Query Language,简称SQL).不过SQL语言并非通用的编程语言,它专用于数据库的访问和处理,更像是一种操作命令,所以常说SQL语句而不说SQL代码。标准的SQL语句分为3类:数据定义、数据操纵和数据控制。但不同的数据库往往有自己的实现。

        SQLite是一种小巧的嵌入式数据库,使用方便、开发简单。如同MySQL、Oracle那样,SQLite也采用SQL语句管理数据,由于它属于轻型数据库,不涉及复杂的数据控制操作,因此App开发只用到数据定义和数据操纵两类SQL语句。此外,SQLite的SQL语法与通用的SQL语法略有不同,接下来介绍的两类SQL语法全部基于SQLite。

1.数据定义语言

        数据定义语言(全称Data Definition Language,简称DDL)描述了怎样变更数据实体的框架结构。就SQLite而言,DDL语言主要包括3种操作:创建表格、删除表格、修改表结构,分别说明如下。

        (1)创建表格

        表格的创建动作由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语法与其他数据库的SQL语法有所出入,相关的注意点说明见下:

  1. SQL语句不区分大小写,无论是create与table这类关键词,还是表格名称、字段名称,都不区分大小写。唯一区分大小写的是被单引号括起来的字符串值。
  2. 为避免重复建表,应加上IF NOTEXISTS关键词,例如CREATE TABLE IF NOTEXISTS 表格名称······
  3.  SQLite支持整型INTEGER、长整型LONG、字符串VARCHAR、浮点数FLOAT,但不支持布尔类型。布尔类型的数据要使用整型保存,如果直接保存布尔数据,在入库时SQLite会自动将它转为0或1,其中0表示false,1表示true。
  4. 建表时需要唯一标识字段,它的字段名为_id.创建新表都要加上该字段定义,例如_idINTEGER PRIMARY KEY AUTOINCREMENT NOT NULL。

        (2)删除表格

        删除表格表格的删除动作由drop命令完成,格式为“DROP TABLE IF EXISTS 表格名称;”。下面是删除用户信息表的SQL语句例子:

DROP TABLE IF EXISTS user_info;

        (3)修改表结构

        表格的修改动作由alter命令完成,格式为“ALTER TABLE 表格名称 修改操作;”。不过SQLite只支持增加字段,不支持修改字段,也不支持删除字段。对于字段增加操作,需要在alter之后补充add命令,具体格式如“ALTER TABLE 表格名称 ADD COLUMN 字段名称 字段类型;”。下面是给用户信息表增加手机号字段的SQL语句例子:

ALTER TABLE user_info ADD COLUMN phone VARCHAR;
注        意
SQLite的alter命令每次只能添加一列字段,若要添加多列,就得分多次添加。

2.数据操纵语言

        数据操纵语言(全称Data Manipulation Language,简称DML)描述了怎样处理数据实体的内部记录。表格记录的操作类型包括添加、删除、修改、查询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 查询条件;”,其中查询条件的表达式形如“字段名=字段值”,多个字段的条件交集通过“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='张三';

        查询操作除了比较字段值条件之外,常常需要对查询结果排序,此时要在查询条件后面添加排序条件,对应的表达式为“ORDER BY 字段名ASC或者DESC”,意指对查询结果按照某个字段排序,其中ASC代表升序,DESC代表降序。下面是查询记录并对结果排序的SQL语句例子:

SELECT * FROM user_info ORDER BY age ASC;

数据库管理器 SQLiteDatabase

        SQL语句毕竟只是SQL命令,若要在Java代码中操纵SQLite,还需专门的工具类。SQLiteDatabase便是Android提供的SQLite数据库管理器,开发者可以在活动页面代码中调用openOrCreateDatabase方法获取数据库实例,参考代码如下:

public class SQLiteDatabase 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_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);
        // 生成一个测试数据库的完整路径
        mDatabaseName = getFilesDir() + "/test.db";
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_database_create) {
            // 创建或打开数据库。数据库如果不存在就创建它,如果存在就打开它
            android.database.sqlite.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);
        }
    }
}

        首次运行测试App,调用openOrCreateDatabase方法会自动创建数据库,并返回该数据库的管理器实例,创建结果如图所示。

         获得数据库实例之后,就能对该数据库开展各项操作了。数据库管理器SQLiteDatabase提供了若干操作数据表的API,常用的方法有3类,列举如下:

        1.管理类,用于数据库层面的操作

  • openDatabase:打开指定路径的数据库。
  • isOpen:判断数据库是否已打开。
  • close:关闭数据库。
  • getVersion:获取数据库的版本号。
  • setVersion:设置数据库的版本号。

        2.事务类,用于事务层面的操作

  • beginTransaction:开始事务。
  • setTransactionSuccessful:设置事务的成功标志。
  • endTransaction:结束事务。执行本方法时,系统会判断之前是否调用了
    setTransactionSuccessful方法,如果之前已调用该方法就提交事务,如果没有调用该方法就回滚事务。

        3.数据处理类,用于数据表层面的操作

  • execSQL:执行拼接好的SQL控制语句。一般用于建表、删表、变更表结构。
  • delete:删除符合条件的记录。
  • update:更新符合条件的记录信息。
  • insert:插入一条记录。
  • query:执行查询操作,并返回结果集的游标。
  • rawQuery:执行拼接好的SQL查询语句,并返回结果集的游标。

        在实际开发中,经常用到的是查询语句,建议先写好查询操作的select语句,再调用rawQuery方法执行查询语句。

数据库帮助器 SQLiteOpenHelper

        由于SQLiteDatabase存在局限性,一不小心就会重复打开数据库,处理数据库的升级也不方便,因此Android提供了数据库帮助器SQLiteOpenHelper,帮助开发者合理使用SQLite。

        SQLiteOpenHelper的具体使用步骤如下:

        1.新建一个继承自SQLiteOpenHelper的数据库操作类,按提示重写onCreate和onUpgrade两个方法。其中,onCreate方法只在第一次打开数据库时执行,在此可以创建表结构;而onUpgrade方法在数据库版本升高时执行,在此可以根据新旧版本号变更表结构。

        2.为保证数据库的安全使用,需要封装几个必要方法,包括获取单例对象、打开数据库连接、关闭数据库连接,说明如下:

  • 获取单例对象:确保在App运行过程中数据库只会打开一次,避免重复打开引起错误。
  • ·打开数据库连接:SQLite有锁机制,即读锁和写锁的处理,故而数据库连接也分两种,读连接可
    调用getReadableDatabase 方法获得,写连接可调用getWritableDatabase方法获得。
  • 关闭数据库连接:数据库操作完毕,调用数据库实例的close方法关闭连接。

        3.提供对表记录增加、删除、修改、查询的操作方法。

        能被SQLite直接使用的数据结构是ContentValues类,它类似于映射Map,也提供了put和get方法存取键值对。区别之处在于:ContentValues的键只能是字符串,不能是其他类型。ContentValues主要用于增加记录和更新记录,对应数据库的insert和lupdate方法。

        记录的查询操作用到了游标类Cursor,调用query和rawQuery方法返回的都是Cursor对象,若要获取全部的查询结果,则需根据游标的指示一条一条遍历结果集合。Cursor的常用方法可分为3类,说明如下:

        1.游标控制类方法,用于指定游标的状态

  • close:关闭游标。
  • isClosed:判断游标是否关闭
  • isFirst:判断游标是否在开头。
  • isLast:判断游标是否在末尾。

        2.游标移动类方法,把游标移动到指定位置

  • moveToFirst:移动游标到开头。
  • moveToLast:移动游标到末尾。
  • moveToNext:移动游标到下一条记录。
  • moveToPrevious:移动游标到上一条记录。
  • move:往后移动游标若干条记录。
  • moveToPosition:移动游标到指定位置的记录。

        3.获取记录类方法,可获取记录的数量、类型以及取值

  • getCount:获取结果记录的数量。
  • getInt:获取指定字段的整型值。
  • getLong:获取指定字段的长整型值。
  • getFloat:获取指定字段的浮点数值。
  • getString:获取指定字段的字符串值。
  • getType:获取指定字段的字段类型。

        鉴于数据库操作的特殊性,不方便单独演示某个功能,接下来从创建数据库开始介绍,完整演示一下数据库的读写操作。用户注册信息的演示页面包括两个,分别是记录保存页面和记录读取页面,其中记录保存页面通过insert方法向数据库添加用户信息。

        先打开记录保存页面,依次录入信息并将两个用户的注册信息保存至数据库。再打开记录读取页面,从数据库读取用户注册信息并展示在页面上。

         上述演示页面主要用到了数据库记录的添加、查询和删除操作,对应的数据库帮助器关键代码如下所示,尤其关注里面的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"; // 数据库的名称
    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;
    }

}

数据写入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; // 声明一个用户数据库帮助器的对象
    private android.widget.EditText et_name; // 声明一个编辑框对象
    private android.widget.EditText et_age; // 声明一个编辑框对象
    private android.widget.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_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();
        // 获得数据库帮助器的实例
        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数据库");
        }
    }
}

优化记住密码功能

        真正的记住密码功能应当是这样的:先输入手机号码,然后根据手机号码匹配保存的密码,一个手机号码对应一个密码,从而实现具体手机号码的密码记忆功能。

        现在运用数据库技术分条存储各用户的登录信息,并支持根据手机号查找登录信息,从而同时记住多个手机号的密码。具体的改造主要有下列3点:

        (1)声明一个数据库的帮助器对象,然后在活动页面的onResume方法中打开数据库连接,在onPasue方法中关闭数据库连接,示例代码如下:

    private UserDBHelper mHelper; //声明一个用户数据库的帮助器对象

@Override
    protected void onResume() {
        super.onResume();
        mHelper = UserDBHelper.getInstance(this, 1); // 获得用户数据库帮助器的实例
        mHelper.openWriteLink(); // 恢复页面,则打开数据库连接
    }

    @Override
    protected void onPause() {
        super.onPause();
        mHelper.closeLink(); //暂停页面,则关闭数据库连接
    }

        (2)登录成功时,如果用户勾选了“记住密码”复选框,就将手机号码及其密码保存至数据库。也就是在loginSuccess方法中增加如下代码:

        // 如果勾选了“记住密码”,则把手机号码和密码保存为数据库的用户表记录
        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); // 往用户数据库添加登录成功的用户信息
        }

        (3)再次打开登录页面,用户输入手机号后点击密码框时,App根据手机号到数据库查找登录信息,并将记录结果中的密码填入密码框。其中根据手机号码查找登录信息,要求在帮助器代码中添加以下方法,用于找到指定手机的登录密码:

    // 根据手机号码查询指定记录
    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点的点击密码框触发查询操作,用到了编辑框的焦点变更事件。就本案例而言,光标切到密码框触发焦点变更事件,具体处理逻辑要求重写监听器的onFocusChange方法,重写后的方法代码如下:

    @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);
                }
            }
        }
    }

        重新运行测试App,先打开登录页面,勾选“记住密码”复选框,并确保本次登录成功。然后再次进入登录页面,输入手机号码后光标还停留在手机框。接着点击密码框,光标随之跳转到密码框,此时密码框自动填入了该号码对应的密码串。由效果图可见,这次实现了真正意义上的记住密码功能。

猜你喜欢

转载自blog.csdn.net/demon_dog/article/details/129298814
今日推荐