一、SharedPreferences
- SharedPreferences是Android的一个轻量级存储工具,采用的存储结构是Key-value的键值对方式。
共享参数的存储介质是符合XMK规范的配置文件。保存路径是:/data/data/应用包名/shared_prefs/文件名.xml。
使用场景:
简单且孤立的数据。若是复杂且相互间有关的数据,则要保存再数据库中。
文本形式的数据。若是二进制数据,则要保存在文件中。
需要持久化存储的数据。在App推出后再次启动时,之前保存的数据仍然有效。
实际开发中,共享参数经常存储的数据有App的个性化配置信息、用户使用App的行为信息、临时需要保存的片段信息等。
实战:
布局:
layout/activity_share_write.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"
android:orientation="vertical"
tools:context=".ShareWriteActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="姓名:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入姓名" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="年龄:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入年龄" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="身高:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入身高" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="体重:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_weight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入体重" />
</LinearLayout>
<CheckBox
android:id="@+id/cb_married"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已婚" />
<Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存到共享参数"
/>
</LinearLayout>
相关选择器:
drawable-v24/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>
drawable-v24/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="#0000ff" />
<!-- 指定了形状四个圆角的半径-->
<corners android:radius="5dp" />
<!-- 指定形状四个方向的间距-->
<padding android:bottom="2dp" android:left="2dp" android:right="2dp" android:top="2dp"/>
</shape>
drawable-v24/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="#aaaaaa" />
<!-- 指定了形状四个圆角的半径-->
<corners android:radius="5dp" />
<!-- 指定形状四个方向的间距-->
<padding android:bottom="2dp" android:left="2dp" android:right="2dp" android:top="2dp"/>
</shape>
业务代码:
public class ShareWriteActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private CheckBox cb_married;
private Button btn_save;
private SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_share_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);
btn_save = findViewById(R.id.btn_save);
cb_married = findViewById(R.id.cb_married);
btn_save.setOnClickListener(this);
// config 为文件名,MODE_PRIVATE 私有模式
sharedPreferences = getSharedPreferences("config", Context.MODE_PRIVATE);
// 推出程序后(结束),重新进入读取数据
reload();
}
private void reload() {
String name = sharedPreferences.getString("name", null);
if (name != null) {
et_name.setText(name);
}
int age = sharedPreferences.getInt("age", 0);
if (age != 0) {
et_age.setText(String.valueOf(age));
}
float height = sharedPreferences.getFloat("height", 0f);
if(height != 0f) {
et_height.setText(String.valueOf(height));
}
float weight = sharedPreferences.getFloat("weight", 0f);
if (weight != 0f) {
et_weight.setText(String.valueOf(weight));
}
boolean married = sharedPreferences.getBoolean("married", false);
cb_married.setChecked(married);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case 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();
SharedPreferences.Editor edit = sharedPreferences.edit();
edit.putString("name", name);
edit.putInt("age", Integer.parseInt(age));
edit.putFloat("height", Float.parseFloat(height));
edit.putFloat("weight", Float.parseFloat(weight));
edit.putBoolean("married", cb_married.isChecked());
edit.commit();
break;
}
}
}
效果展示:
二、数据库SQLite
数据库管理器SQLiteDatabase
SQLiteDatabase 是SQLite的数据库管理类,它提供了若干操作数据表的API,常用方法有3类:
1、管理类,用于数据库层面的操作
openDatabase:打开指定路径的数据库
isOpen:判断数据库是否已打开。
close:关闭数据库。
getVersion:获取数据库的版本号。
setVersion:设置数据库的版本号。
2、事务类
beginTransaction:开始事务
setTransactionSuccessful: 设置事务的成功标志
endTransaction: 结束事务
3、数据处理类,用于数据表层面的操作
execSQL:执行数据表层面的操作
delete: 删除符合条件的记录
update: 更新符合条件的记录
insert: 插入一条记录
query: 执行查询操作,返回结果集的游标
rawQuery: 执行拼接好的SQL查询语句,返回结果集的游标
数据库操作
布局文件
<?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"
android:orientation="vertical"
tools:context=".DatabaseActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="5dp"
>
<Button
android:id="@+id/btn_create_database"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="创建数据库"
/>
<Button
android:id="@+id/btn_drop_database"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="删除数据库"
/>
</LinearLayout>
<TextView
android:id="@+id/tv_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
业务代码:
public class DatabaseActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_create_database;
private Button btn_drop_database;
private TextView tv_show;
private String mDatabaseName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_database);
btn_create_database = findViewById(R.id.btn_create_database);
btn_drop_database = findViewById(R.id.btn_drop_database);
tv_show = findViewById(R.id.tv_show);
btn_create_database.setOnClickListener(this);
btn_drop_database.setOnClickListener(this);
// 生成一个测试数据库的完整路径
mDatabaseName = getFilesDir() + "/test.db";
}
@Override
public void onClick(View v) {
String desc = null;
switch (v.getId()) {
case R.id.btn_create_database:
// 创建或者打开数据库。数据库如果不存在就创建它,如果存在就打开它
SQLiteDatabase sqLiteDatabase = openOrCreateDatabase(mDatabaseName, Context.MODE_PRIVATE, null);
desc = String.format("数据库%s创建%s", sqLiteDatabase.getPath(), (sqLiteDatabase != null) ? "成功" : "失败");
tv_show.setText(desc);
break;
case R.id.btn_drop_database:
// 删除数据库
boolean result = deleteDatabase(mDatabaseName);
desc = String.format("数据库%s创建%s", mDatabaseName, result ? "成功" : "失败");
tv_show.setText(desc);
break;
}
}
}
效果展示:
数据库帮助器SQLiteOpenHelper
是Android提供的数据库辅助工具,用于指导开发者进行SQLite的合理使用
SQLiteOpenHelper的具体使用步骤:
- 新建一个继承自SQLiteOpenHelper的数据库操作类,提示重写onCreate和onUpgrade两个方法。
封装保证数据库安全的必要方法。
提供对表记录进行增加、删除、修改、擦汗寻的操作方法。
1、增删改查
布局文件:
<?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"
android:orientation="vertical"
tools:context=".ShareWriteActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="姓名:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入姓名" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="年龄:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入年龄" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="身高:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入身高" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="体重:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_weight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入体重" />
</LinearLayout>
<CheckBox
android:id="@+id/cb_married"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已婚" />
<Button
android:id="@+id/btn_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添 加"
/>
<Button
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删 除"
/>
<Button
android:id="@+id/btn_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="修 改"
/>
<Button
android:id="@+id/btn_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="查 询"
/>
</LinearLayout>
选择器:drawable-v24/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>
实体类:
public class User {
private int id; // 序号
private String name; // 姓名
private int age; // 年龄
private long height; // 身高
private float weight; // 体重
private boolean married; // 婚否
public User() {
}
public User(String name, int age, long height, float weight, boolean married) {
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
this.married = married;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public long getHeight() {
return height;
}
public void setHeight(long height) {
this.height = height;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", height=" + height +
", weight=" + weight +
", married=" + married +
'}';
}
}
数据库帮助类:
package com.example.datesaved.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;
import com.example.datesaved.bean.User;
import java.util.ArrayList;
import java.util.List;
/**
* @author liuzhihao
* @create 2023-03-11 0:09
*
*
* 操作步骤:
* 新建一个继承自SQLiteOpenHelper的数据库操作类,提示重写onCreate和onUpgrade两个方法。
* 封装保证数据库安全的必要方法。
* 提供对表记录进行增加、删除、修改、查询的操作方法。
*/
public class UserDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "user.db";
private static final String TABLE_NAME = "user";
private static final int DB_VERSION = 1;
private static UserDBHelper mHelper = null;
private SQLiteDatabase mRDB = null; // 读操作
private SQLiteDatabase mWDB = null; // 写操作
private UserDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
// (懒汉式 线程不安全)利用单例模式获取数据库帮助器的唯一实例
public static UserDBHelper getInstance(Context context) {
if (mHelper == null) {
mHelper = new UserDBHelper(context);
}
return mHelper;
}
// 打开数据库的读链接
public SQLiteDatabase openReadLink() {
if (mRDB == null || !mRDB.isOpen()) {
mRDB = mHelper.getReadableDatabase();
}
return mRDB;
}
// 打开数据库的写链接
public SQLiteDatabase openWriteLink() {
if (mWDB == null || !mWDB.isOpen()) {
mWDB = mHelper.getReadableDatabase();
}
return mWDB;
}
// 关闭数据库链接
public void closeLink() {
if (mRDB != null && mRDB.isOpen()) {
mRDB.close();
mRDB = null;
}
if (mWDB != null && mWDB.isOpen()) {
mWDB.close();
mWDB = null;
}
}
public long insert(User user) {
ContentValues values = new ContentValues(); // map
values.put("name", user.getName());
values.put("age", user.getAge());
values.put("height", user.getHeight());
values.put("weight", user.getWeight());
values.put("married", user.isMarried());
// 执行插入记录,该语句返回插入记录的行号
// 第二个参数的作用:
// 如果第三个参数values为null或者元素个数为0,由于insert()方法要求必须添加一条除了主键之外其他字段为null值的记录,
// 为了满足SQL语法的需要,insert语句必须给定一个字段名,如:insert into person(name) values (NULL),
// 如果不给顶字段名,insert语句就成了这样:insert into person() values(),显然这不满足标准SQL的语法,
// 如果第三个参数values不为null并且元素的个数大于0,可以把第二个参数设置为null
return mWDB.insert(TABLE_NAME, null, values);
}
public long deleteByName(String name) {
// 删除所有
// mWDB.delete(TABLE_NAME, "1 = 1", null);
return mWDB.delete(TABLE_NAME, "name = ?", new String[]{name});
}
public long update(User user) {
ContentValues values = new ContentValues();
values.put("name", user.getName());
values.put("age", user.getAge());
values.put("height", user.getHeight());
values.put("weight", user.getWeight());
values.put("married", user.isMarried());
return mWDB.update(TABLE_NAME, values, "name = ?", new String[]{user.getName()});
}
public List<User> queryAll() {
List<User> list = new ArrayList<>();
// 返回一个游标:一行一行读取每一行的记录
Cursor cursor = mRDB.query(TABLE_NAME, null, null, null, null, null, null);
while (cursor.moveToNext()) {
User user = new User();
user.setName(cursor.getString(1));
user.setAge(cursor.getInt(2));
user.setHeight(cursor.getLong(3));
user.setWeight(cursor.getFloat(4));
// SQL没有布尔型,用0表示false,用1表示true
user.setMarried(cursor.getInt(5) != 0);
list.add(user);
}
return list;
}
public List<User> queryByName(String name) {
List<User> list = new ArrayList<>();
Cursor cursor = mRDB.query(TABLE_NAME, null, "name = ?", new String[]{name}, null, null, null);
while (cursor.moveToNext()) {
User user = new User();
user.setName(cursor.getString(1));
user.setAge(cursor.getInt(2));
user.setHeight(cursor.getLong(3));
user.setWeight(cursor.getFloat(4));
user.setMarried(cursor.getInt(5) != 0);
list.add(user);
}
return list;
}
/**
* 创建数据库,执行建表语句
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
" _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);";
db.execSQL(sql);
}
// 数据库版本更新
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
工具类:
package com.example.datesaved.utils;
import android.content.Context;
import android.widget.Toast;
/**
* @author liuzhihao
* @create 2023-03-12 0:16
*/
public class ToastUtil {
public static void show(Context context, String desc) {
Toast.makeText(context, desc, Toast.LENGTH_SHORT).show();
}
}
业务代码:
package com.example.datesaved;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
import com.example.datesaved.bean.User;
import com.example.datesaved.database.UserDBHelper;
import com.example.datesaved.utils.ToastUtil;
import java.util.List;
public class SQLiteHelperActivity extends AppCompatActivity implements View.OnClickListener, View.OnFocusChangeListener {
private static final String TAG = "liuzhihao";
private Button btn_add;
private Button btn_delete;
private Button btn_update;
private Button btn_query;
private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private CheckBox cb_married;
private UserDBHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_helper);
btn_add = findViewById(R.id.btn_add);
btn_delete = findViewById(R.id.btn_delete);
btn_update = findViewById(R.id.btn_update);
btn_query = findViewById(R.id.btn_query);
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);
cb_married = findViewById(R.id.cb_married);
btn_add.setOnClickListener(this);
btn_delete.setOnClickListener(this);
btn_update.setOnClickListener(this);
btn_query.setOnClickListener(this);
// 焦点变更
et_name.setOnFocusChangeListener(this);
et_age.setOnFocusChangeListener(this);
et_height.setOnFocusChangeListener(this);
et_weight.setOnFocusChangeListener(this);
// 文本内容变更
et_name.addTextChangedListener(new SelfWatcher());
et_age.addTextChangedListener(new SelfWatcher());
et_height.addTextChangedListener(new SelfWatcher());
et_weight.addTextChangedListener(new SelfWatcher());
}
@Override
protected void onStart() {
super.onStart();
// 获得数据库帮助器的实例
dbHelper = UserDBHelper.getInstance(this);
// 打开数据库帮助器的读写连接
dbHelper.openWriteLink();
dbHelper.openReadLink();
}
@Override
protected void onStop() {
super.onStop();
// 关闭数据库连接
dbHelper.closeLink();
}
@Override
public void onClick(View v) {
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
User user = null;
switch (v.getId()) {
case R.id.btn_add:
if(TextUtils.isEmpty(age) || TextUtils.isEmpty(height) || TextUtils.isEmpty(weight)) {
ToastUtil.show(this, "年龄/身高/体重不能为空");
return;
}
user = new User(name, Integer.parseInt(age), Long.parseLong(height), Float.parseFloat(weight), cb_married.isChecked());
if (dbHelper.insert(user) > 0) {
ToastUtil.show(this, "添加成功");
}
break;
case R.id.btn_delete:
if (dbHelper.deleteByName(name) > 0) {
ToastUtil.show(this, "删除成功");
}
break;
case R.id.btn_update:
user = new User(name, Integer.parseInt(age), Long.parseLong(height), Float.parseFloat(weight), cb_married.isChecked());
if (dbHelper.update(user) > 0) {
ToastUtil.show(this, "修改成功");
}
break;
case R.id.btn_query:
// List<User> users = dbHelper.queryAll();
List<User> users = dbHelper.queryByName(name);
for (User u : users) {
Log.d(TAG, "name : " + u.getName());
Log.d(TAG, "age : " + u.getAge());
Log.d(TAG, "height : " + u.getHeight());
Log.d(TAG, "weight : " + u.getWeight());
Log.d(TAG, "isMarried : " + (u.isMarried() ? "已婚" : "未婚"));
}
break;
}
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
String name = et_name.getText().toString();
if (TextUtils.isEmpty(name)) {
et_name.requestFocus();
ToastUtil.show(this, "请输入正确的姓名");
}
}
}
private class SelfWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
}
}
}
2、事务管理
public long insert(User user) {
ContentValues values = new ContentValues(); // map
values.put("name", user.getName());
values.put("age", user.getAge());
values.put("height", user.getHeight());
values.put("weight", user.getWeight());
values.put("married", user.isMarried());
// 执行插入记录,该语句返回插入记录的行号
// 第二个参数的作用:
// 如果第三个参数values为null或者元素个数为0,由于insert()方法要求必须添加一条除了主键之外其他字段为null值的记录,
// 为了满足SQL语法的需要,insert语句必须给定一个字段名,如:insert into person(name) values (NULL),
// 如果不给顶字段名,insert语句就成了这样:insert into person() values(),显然这不满足标准SQL的语法,
// 如果第三个参数values不为null并且元素的个数大于0,可以把第二个参数设置为null
// 事务操作
// beginTransaction:开始事务
// setTransactionSuccessful: 设置事务的成功标志
// endTransaction: 结束事务
try {
mWDB.beginTransaction();
mWDB.insert(TABLE_NAME, null, values);
// 异常
int i = 10 / 0;
mWDB.insert(TABLE_NAME, null, values);
mWDB.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
mWDB.endTransaction();
}
return 1;
// return mWDB.insert(TABLE_NAME, null, values);
}
3、数据库版本升级
// 数据库版本更新时执行(下次会比较版本号,不相等则执行此方法)
// 比如:新增字段,修改表结构
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN phone VARCHAR;";
db.execSQL(sql);
sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN password VARCHAR;";
db.execSQL(sql);
}
三、存储卡文件操作
3.1 私有存储空间与公共存储空间 和 在存储卡上读写文本文件
Android把外部存储分成了两块区域,一块时所有应用均可访问的公共空间,另一块时只有应用自己才可访问的私有空间。
布局文件:
<?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"
android:orientation="vertical"
tools:context=".ShareWriteActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="姓名:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入姓名" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="年龄:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入年龄" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="身高:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入身高" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:text="体重:"
android:textSize="15sp" />
<EditText
android:id="@+id/et_weight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/editext_selector"
android:hint="请输入体重" />
</LinearLayout>
<CheckBox
android:id="@+id/cb_married"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已婚" />
<Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保 存"
/>
<Button
android:id="@+id/btn_read"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="读 取"
/>
<TextView
android:id="@+id/tv_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
选择器:
drawable-v24/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>
业务代码:
public class FileWriteActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "liuzhihao";
private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private CheckBox cb_married;
private Button btn_save;
private Button btn_read;
private TextView tv_txt;
private String path;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_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);
cb_married = findViewById(R.id.cb_married);
btn_save = findViewById(R.id.btn_save);
btn_read = findViewById(R.id.btn_read);
tv_txt = findViewById(R.id.tv_txt);
btn_save.setOnClickListener(this);
btn_read.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case 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();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("姓名:").append(name);
stringBuilder.append("\n年龄:").append(age);
stringBuilder.append("\n身高:").append(height);
stringBuilder.append("\n体重:").append(weight);
stringBuilder.append("\n婚否:").append(cb_married.isChecked() ? "是" : "否");
String fileName = System.currentTimeMillis() + ".txt"; // 文件名称
String directory = null;
// 外部存储的私有空间
directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();///storage/emulated/0/Android/data/com.example.datesaved/files/Download/
// 外部存储的公共空间
directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString(); // 需要在manifests中加权限 /storage/emulated/0/Download
// 内部存储私有空间
directory = getFilesDir().toString();// /data/user/0/com.example.datesaved/files/
path = directory + File.separatorChar + fileName; // File.separatorChar = "/";斜杠
Log.d(TAG, "onClick_save: path = " + path);
FileUtil.saveText(path, stringBuilder.toString());
ToastUtil.show(this, "保存成功");
break;
case R.id.btn_read:
tv_txt.setText(FileUtil.openText(path));
break;
}
}
}
工具类:
public class ToastUtil {
public static void show(Context context, String desc) {
Toast.makeText(context, desc, Toast.LENGTH_SHORT).show();
}
}
public class FileUtil {
// 把字符串保存到指定路径的文本文件
public static void saveText(String path, String txt) {
BufferedWriter os = null;
try {
os = new BufferedWriter(new FileWriter(path));
os.write(txt);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.2 在存储卡上读写图片文件
Android的位图工具是Bitmap,App读写Bitmap可以使用性能更好的BufferedOutputStream和BufferedInputStream。
Android还提供了BitmapFactory工具用于读取各种来源的图片,相关方法如下:
- decodeResource: 该方法可以从资源文件中读取图片信息。
- decodeFile:该方法可将指定路径的图片读取到Bitmap对象。
- decodeStream: 该方法从输入流中读取位图数据。
布局文件:
<?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"
android:orientation="vertical"
tools:context=".ImageWriteActivity">
<Button
android:id="@+id/btn_saveimg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存图片"
/>
<Button
android:id="@+id/btn_readimg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取图片"
/>
<ImageView
android:id="@+id/iv_content"
android:layout_width="match_parent"
android:layout_height="400dp"
android:scaleType="fitCenter"
/>
</LinearLayout>
业务代码:
public class ImageWriteActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "liuzhihao";
private Button btn_saveimg;
private Button btn_readimg;
private ImageView iv_content;
private String directory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_write);
btn_saveimg = findViewById(R.id.btn_saveimg);
btn_readimg = findViewById(R.id.btn_readimg);
iv_content = findViewById(R.id.iv_content);
btn_saveimg.setOnClickListener(this);
btn_readimg.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_saveimg:
String filename = System.currentTimeMillis() + ".jpeg";
// 获取当前app的私有下载目录
directory = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + File.separatorChar + filename;
Log.d(TAG, "directory : " + directory);
// 从指定资源文件中获取位图对象
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.libai);
// 把位图对象保存为图片文件
FileUtil.saveImage(directory, bitmap);
ToastUtil.show(this, "保存图片成功");
break;
case R.id.btn_readimg:
// 第一种方式
if (directory != null) {
Bitmap image = FileUtil.openImage(directory);
iv_content.setImageBitmap(image);
}
// 第二种方式
// Bitmap image = BitmapFactory.decodeFile(directory);
// iv_content.setImageBitmap(image);
// 第三种方式:直接调用setImageURI方法,设置图像视图的路径对象
// iv_content.setImageURI(Uri.parse(directory));
break;
}
}
}
工具类:
public class FileUtil {
// 把字符串保存到指定路径的文本文件
public static void saveText(String path, String txt) {
BufferedWriter os = null;
try {
os = new BufferedWriter(new FileWriter(path));
os.write(txt);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 从指定路径的文本文件中读取内容字符串
public static String openText(String path) {
BufferedReader is = null;
StringBuilder stringBuilder = new StringBuilder();
try {
is = new BufferedReader(new FileReader(path));
String line = null;
while ((line = is.readLine()) != null) {
stringBuilder.append(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return stringBuilder.toString();
}
// 把位图数据保存到指定路径
public static void saveImage(String directory, Bitmap bitmap) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(directory);
// 把位图数据压缩到文件输出流中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 从指定路径的图片我呢见中读取位图数据
public static Bitmap openImage(String directory) {
Bitmap bitmap = null;
FileInputStream fileInputStream = null;
Log.d("liuzhihao", "openImage: directory = " + directory);
try {
fileInputStream = new FileInputStream(directory);
bitmap = BitmapFactory.decodeStream(fileInputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bitmap;
}
}