结合ai开发简单的增删改查app
我在做一个app,内容是学生信息的增删改查,学生信息包括姓名,性别,学号。用sqllite,Android studio开发,现在有一些问题
你在开发一个基于SQLite的学生信息管理应用时可能会遇到以下几个常见问题及解决方案,我帮你分步骤梳理:
🔧 1. 数据库创建与升级问题
java
复制
public class DBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "students.db";
private static final int DB_VERSION = 1;
// 创建表SQL
private static final String CREATE_TABLE =
"CREATE TABLE students (_id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "name TEXT NOT NULL, "
+ "gender INTEGER DEFAULT 0, " // 0-男 1-女
+ "student_id TEXT UNIQUE);"; // 学号唯一
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS students");
onCreate(db);
}
}
📝 2. CRUD操作常见问题
java
复制
// 插入数据示例
public long addStudent(Student student) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", student.getName());
values.put("gender", student.getGender());
values.put("student_id", student.getStudentId());
long id = db.insert("students", null, values);
db.close();
return id;
}
// 查询所有学生
public List
List
String selectQuery = "SELECT * FROM students";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.moveToFirst()) {
do {
Student student = new Student();
student.setId(cursor.getInt(0));
student.setName(cursor.getString(1));
student.setGender(cursor.getInt(2));
student.setStudentId(cursor.getString(3));
students.add(student);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
return students;
}
🚫 3. 唯一学号处理
java
复制
try {
long result = db.insertOrThrow("students", null, values);
} catch (SQLiteConstraintException e) {
// 处理学号重复
Log.e("DB", "学号已存在");
return -1;
}
🔄 4. 列表刷新问题
java
复制
// 在Activity中
private void refreshList() {
List
adapter.updateList(students);
adapter.notifyDataSetChanged();
}
// 自定义Adapter
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
private List
public void updateList(List<Student> newList) {
mData = newList;
}
}
🎨 5. 性别输入优化建议
xml
复制
<RadioButton
android:id="@+id/radioMale"
android:text="男"
android:checked="true"/>
<RadioButton
android:id="@+id/radioFemale"
android:text="女"/>
运行 HTML
🔍 6. 数据库调试技巧
使用Android Studio的Database Inspector:
运行应用后点击 View > Tool Windows > App Inspection
选择正在运行的应用进程
打开数据库文件进行实时查看和修改
💡 最佳实践建议:
使用ORM框架(如Room)简化数据库操作
所有数据库操作在子线程执行
使用LiveData观察数据变化
对学号字段添加唯一索引
使用事务处理批量操作
遇到具体问题时,可以重点告诉我以下信息:
完整的错误日志
相关代码片段
你期望的效果与实际效果的差异
是否使用了第三方库
这样我可以更精准地帮你定位问题原因!
给出DBHelper类内容包括创建学生表,增加删除修改查找操作
以下是一个完整的 DBHelper 类实现,包含数据库创建和完整的 CRUD 操作:
java
复制
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
public class DBHelper extends SQLiteOpenHelper {
// 数据库信息
private static final String DATABASE_NAME = "StudentDB";
private static final int DATABASE_VERSION = 1;
// 表结构
public static final String TABLE_STUDENTS = "students";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_GENDER = "gender";
public static final String COLUMN_STUDENT_ID = "student_id";
// 创建表SQL
private static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_STUDENTS + "("
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_NAME + " TEXT NOT NULL, "
+ COLUMN_GENDER + " INTEGER DEFAULT 0, " // 0-男 1-女
+ COLUMN_STUDENT_ID + " TEXT UNIQUE NOT NULL);"; // 学号唯一约束
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_STUDENTS);
onCreate(db);
}
// 添加学生
public long addStudent(Student student) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_NAME, student.getName());
values.put(COLUMN_GENDER, student.getGender());
values.put(COLUMN_STUDENT_ID, student.getStudentId());
long result = db.insert(TABLE_STUDENTS, null, values);
db.close();
return result; // 返回插入的行ID,-1表示失败
}
// 获取所有学生
public List<Student> getAllStudents() {
List<Student> students = new ArrayList<>();
String query = "SELECT * FROM " + TABLE_STUDENTS;
try (SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(query, null)) {
if (cursor.moveToFirst()) {
do {
Student student = new Student();
student.setId(cursor.getInt(0));
student.setName(cursor.getString(1));
student.setGender(cursor.getInt(2));
student.setStudentId(cursor.getString(3));
students.add(student);
} while (cursor.moveToNext());
}
}
return students;
}
// 根据ID更新学生信息
public int updateStudent(Student student) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_NAME, student.getName());
values.put(COLUMN_GENDER, student.getGender());
values.put(COLUMN_STUDENT_ID, student.getStudentId());
return db.update(TABLE_STUDENTS,
values,
COLUMN_ID + " = ?",
new String[]{String.valueOf(student.getId())});
}
// 根据学号删除学生
public int deleteStudentByStudentId(String studentId) {
SQLiteDatabase db = this.getWritableDatabase();
return db.delete(TABLE_STUDENTS,
COLUMN_STUDENT_ID + " = ?",
new String[]{studentId});
}
// 根据学号查询学生
public Student getStudentByStudentId(String studentId) {
SQLiteDatabase db = this.getReadableDatabase();
try (Cursor cursor = db.query(TABLE_STUDENTS,
new String[]{COLUMN_ID, COLUMN_NAME, COLUMN_GENDER, COLUMN_STUDENT_ID},
COLUMN_STUDENT_ID + " = ?",
new String[]{studentId},
null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
Student student = new Student();
student.setId(cursor.getInt(0));
student.setName(cursor.getString(1));
student.setGender(cursor.getInt(2));
student.setStudentId(cursor.getString(3));
return student;
}
}
return null;
}
// 根据姓名模糊查询
public List<Student> searchStudentsByName(String name) {
List<Student> students = new ArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_STUDENTS,
new String[]{COLUMN_ID, COLUMN_NAME, COLUMN_GENDER, COLUMN_STUDENT_ID},
COLUMN_NAME + " LIKE ?",
new String[]{"%" + name + "%"},
null, null, null);
try {
if (cursor.moveToFirst()) {
do {
Student student = new Student();
student.setId(cursor.getInt(0));
student.setName(cursor.getString(1));
student.setGender(cursor.getInt(2));
student.setStudentId(cursor.getString(3));
students.add(student);
} while (cursor.moveToNext());
}
} finally {
cursor.close();
}
return students;
}
}
配套的 Student 模型类:
java
复制
public class Student {
private int id;
private String name;
private int gender; // 0-男 1-女
private String studentId;
// 构造函数、getter和setter
public Student() {}
public Student(String name, int gender, String studentId) {
this.name = name;
this.gender = gender;
this.studentId = studentId;
}
// 自动生成所有getter和setter...
}
关键功能说明:
数据库创建:
自动创建包含自增ID、姓名、性别、学号的表
学号字段有唯一约束(不可重复)
CRUD操作:
增:addStudent() 处理插入操作
删:deleteStudentByStudentId() 根据学号删除
改:updateStudent() 根据ID更新信息
查:
getAllStudents() 获取全部数据
getStudentByStudentId() 精确查询
searchStudentsByName() 模糊查询
最佳实践:
使用 try-with-resources 自动管理Cursor
所有数据库操作后自动关闭连接
使用参数化查询防止SQL注入
统一的主键管理(_id字段)
使用示例:
java
复制
// 初始化
DBHelper dbHelper = new DBHelper(context);
// 添加学生
Student newStudent = new Student("张三", 0, "20230001");
dbHelper.addStudent(newStudent);
// 查询所有学生
List
// 修改学生信息
Student target = dbHelper.getStudentByStudentId("20230001");
target.setName("李四");
dbHelper.updateStudent(target);
// 删除学生
dbHelper.deleteStudentByStudentId("20230001");
注意要点:
建议在子线程执行数据库操作
学号唯一性由数据库约束保证,插入重复学号会返回-1
更新操作需要先获取完整Student对象
模糊查询使用LIKE和%通配符
生产环境建议添加事务支持
现在我有一个addactivity和对应的layout文件,怎么样通过通过addactivity调用添加学生功能呢?
好的,用户现在有一个AddActivity和对应的布局文件,想知道如何在这个活动中调用之前提供的DBHelper类来添加学生信息。首先,我需要理解用户的需求。他们可能已经完成了数据库操作的封装,现在需要在前端界面中实现添加学生的功能。
首先,用户需要在AddActivity中获取用户输入的姓名、性别和学号。布局文件中应该有对应的输入控件,比如EditText和RadioButton。需要检查用户是否正确绑定了这些控件,并确保它们的ID正确无误。
接下来,用户需要在AddActivity中实例化DBHelper类。这时候要注意数据库操作的线程问题,因为直接在主线程中进行数据库操作可能会导致应用无响应。虽然示例代码可能没有提到,但应该提醒用户使用异步任务或线程来处理数据库操作,尤其是在实际应用中。
然后,用户需要处理添加按钮的点击事件。在点击事件中,需要获取输入的数据并进行验证。比如,检查学号是否为空,是否符合格式,以及是否已经存在。这里可能需要调用DBHelper中的方法来检查学号是否重复,或者依赖数据库的唯一约束来处理异常。
另外,性别输入通常使用RadioGroup和RadioButton,用户需要确定如何获取选中的性别值。可能需要将RadioButton的选择转换为DBHelper中定义的性别代码,比如0代表男,1代表女。
在数据验证通过后,调用DBHelper的addStudent方法插入数据,并根据返回值判断是否成功。插入成功后,可能需要跳转回主界面或显示成功提示,同时清空输入框以便下次输入。
同时,需要考虑异常处理,比如学号重复时的提示,或者输入无效时的错误反馈。用户可能希望在这些情况下显示Toast或Snackbar来通知用户。
还要注意资源释放,比如在Activity销毁时关闭数据库连接,但DBHelper中的方法已经调用了db.close(),所以可能不需要额外处理。不过,最好确认DBHelper的实现是否正确管理了数据库连接。
最后,可能需要指导用户如何在AndroidManifest.xml中注册AddActivity,并确保从其他活动(如主活动)正确跳转到AddActivity,比如通过Intent和startActivityForResult(如果需要返回结果)。
总结步骤:布局控件绑定、获取输入数据、验证数据、调用DBHelper插入数据、处理结果、导航或反馈。同时,提醒用户注意线程管理和输入验证的重要性,确保应用的健壮性和用户体验。
以下是实现添加学生功能的完整步骤,我们从布局文件到Activity逻辑分步骤说明:
第一步:创建添加学生界面(activity_add.xml)
xml
复制
<!-- 姓名输入 -->
<EditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入姓名"
android:inputType="textPersonName"/>
<!-- 性别选择 -->
<RadioGroup
android:id="@+id/rgGender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="8dp">
<RadioButton
android:id="@+id/rbMale"
android:text="男"
android:checked="true"/>
<RadioButton
android:id="@+id/rbFemale"
android:text="女"/>
</RadioGroup>
<!-- 学号输入 -->
<EditText
android:id="@+id/etStudentId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入学号"
android:inputType="number"
android:layout_marginTop="8dp"/>
<!-- 提交按钮 -->
<Button
android:id="@+id/btnSubmit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="添加学生"
android:layout_marginTop="16dp"/>
运行 HTML
第二步:实现AddActivity逻辑
java
复制
public class AddActivity extends AppCompatActivity {
private EditText etName, etStudentId;
private RadioGroup rgGender;
private DBHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add);
// 初始化数据库帮助类
dbHelper = new DBHelper(this);
// 绑定控件
etName = findViewById(R.id.etName);
etStudentId = findViewById(R.id.etStudentId);
rgGender = findViewById(R.id.rgGender);
Button btnSubmit = findViewById(R.id.btnSubmit);
// 设置提交按钮点击事件
btnSubmit.setOnClickListener(v -> addStudent());
}
private void addStudent() {
// 获取输入值
String name = etName.getText().toString().trim();
String studentId = etStudentId.getText().toString().trim();
int gender = getSelectedGender();
// 输入验证
if (name.isEmpty() || studentId.isEmpty()) {
Toast.makeText(this, "请填写完整信息", Toast.LENGTH_SHORT).show();
return;
}
// 创建学生对象
Student student = new Student(name, gender, studentId);
// 执行数据库操作(实际开发应在子线程执行)
long result = dbHelper.addStudent(student);
// 处理结果
if (result != -1) {
Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
clearInputs();
} else {
Toast.makeText(this, "添加失败(学号可能重复)", Toast.LENGTH_SHORT).show();
}
}
// 获取选择的性别
private int getSelectedGender() {
int selectedId = rgGender.getCheckedRadioButtonId();
if (selectedId == R.id.rbMale) {
return 0; // 男
} else if (selectedId == R.id.rbFemale) {
return 1; // 女
}
return 0; // 默认值
}
// 清空输入框
private void clearInputs() {
etName.setText("");
etStudentId.setText("");
rgGender.check(R.id.rbMale); // 重置性别选择
}
@Override
protected void onDestroy() {
dbHelper.close(); // 释放数据库资源
super.onDestroy();
}
}
第三步:从主界面跳转到添加页面(示例)
在MainActivity中添加跳转逻辑:
java
复制
// 在MainActivity的onCreate中
Button btnAdd = findViewById(R.id.btnAdd); // 假设存在添加按钮
btnAdd.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, AddActivity.class);
startActivity(intent);
});
关键实现细节说明:
输入验证:
检查姓名和学号是否为空
学号唯一性由数据库约束自动检查
性别默认选择男(通过RadioGroup默认选中项)
线程处理:
示例代码直接在主线程操作(仅用于演示)
实际开发建议使用以下方式:
java
复制
new Thread(() -> {
long result = dbHelper.addStudent(student);
runOnUiThread(() -> {
// 在这里更新UI
});
}).start();
数据反馈:
成功时清空输入框
失败时提示可能原因
使用Toast进行简单反馈
资源管理:
在Activity销毁时关闭数据库连接
使用try-with-resources管理Cursor(已在DBHelper中实现)
效果增强建议:
输入验证增强:
java
复制
// 添加学号格式验证(示例:要求8位数字)
if (!studentId.matches("\d{8}")) {
etStudentId.setError("学号必须为8位数字");
return;
}
加载动画:
java
复制
// 添加时显示进度条
ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("添加中...");
progressDialog.show();
// 操作完成后
progressDialog.dismiss();
返回结果:
java
复制
// 在AddActivity中添加:
setResult(RESULT_OK);
finish();
// 在MainActivity中接收:
startActivityForResult(intent, ADD_REQUEST_CODE);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ADD_REQUEST_CODE && resultCode == RESULT_OK) {
refreshStudentList(); // 刷新列表
}
}
常见问题处理:
学号重复错误:
在插入前先查询学号是否存在:
java
复制
if (dbHelper.getStudentByStudentId(studentId) != null) {
Toast.makeText(this, "学号已存在", Toast.LENGTH_SHORT).show();
return;
}
输入法关闭:
java
复制
// 在按钮点击时隐藏键盘
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(etStudentId.getWindowToken(), 0);
空指针防护:
java
复制
// 在获取输入值时添加空判断
if (etName.getText() == null || etStudentId.getText() == null) return;
通过以上实现,您已经可以完成从界面到数据库的完整添加流程。实际开发中建议结合ViewModel和LiveData实现更规范的架构。
非常好!同理我有一个DeleteActivity和对应的activity_delete.xml文件,怎么样通过通过DeleteActivity调用s删除学生功能呢
以下是实现删除功能的完整实现方案,与添加功能保持相同的代码风格和架构:
第一步:创建删除界面(activity_delete.xml)
xml
复制
<!-- 学号输入 -->
<EditText
android:id="@+id/etStudentId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入要删除的学号"
android:inputType="number"
android:maxLines="1"/>
<!-- 双功能按钮布局 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="16dp">
<Button
android:id="@+id/btnSearch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="查询学生"
android:layout_marginEnd="8dp"/>
<Button
android:id="@+id/btnDelete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="确认删除"
android:enabled="false"
android:textColor="@color/white"
android:backgroundTint="@color/colorDanger"/>
</LinearLayout>
<!-- 学生信息展示 -->
<TextView
android:id="@+id/tvStudentInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"
android:textSize="16sp"
android:lineSpacingExtra="4dp"/>
运行 HTML
第二步:实现DeleteActivity逻辑
java
复制
public class DeleteActivity extends AppCompatActivity {
private EditText etStudentId;
private TextView tvStudentInfo;
private Button btnDelete;
private DBHelper dbHelper;
private Student targetStudent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_delete);
dbHelper = new DBHelper(this);
etStudentId = findViewById(R.id.etStudentId);
tvStudentInfo = findViewById(R.id.tvStudentInfo);
btnDelete = findViewById(R.id.btnDelete);
Button btnSearch = findViewById(R.id.btnSearch);
// 查询按钮点击
btnSearch.setOnClickListener(v -> searchStudent());
// 删除按钮点击
btnDelete.setOnClickListener(v -> deleteStudent());
}
private void searchStudent() {
String studentId = etStudentId.getText().toString().trim();
if (studentId.isEmpty()) {
showError("请输入学号");
return;
}
// 查询学生(建议实际使用子线程)
new Thread(() -> {
targetStudent = dbHelper.getStudentByStudentId(studentId);
runOnUiThread(() -> {
if (targetStudent != null) {
showStudentInfo(targetStudent);
btnDelete.setEnabled(true);
} else {
showError("未找到该学号的学生");
btnDelete.setEnabled(false);
}
});
}).start();
}
private void deleteStudent() {
if (targetStudent == null) return;
new Thread(() -> {
int result = dbHelper.deleteStudentByStudentId(targetStudent.getStudentId());
runOnUiThread(() -> {
if (result > 0) {
Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
resetUI();
} else {
Toast.makeText(this, "删除失败", Toast.LENGTH_SHORT).show();
}
});
}).start();
}
private void showStudentInfo(Student student) {
String info = String.format("找到学生:\n姓名:%s\n性别:%s\n学号:%s",
student.getName(),
student.getGender() == 0 ? "男" : "女",
student.getStudentId());
tvStudentInfo.setText(info);
tvStudentInfo.setVisibility(View.VISIBLE);
}
private void showError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
tvStudentInfo.setVisibility(View.GONE);
}
private void resetUI() {
etStudentId.setText("");
tvStudentInfo.setVisibility(View.GONE);
btnDelete.setEnabled(false);
targetStudent = null;
}
@Override
protected void onDestroy() {
dbHelper.close();
super.onDestroy();
}
}
第三步:增强功能实现(关键逻辑说明)
双阶段操作设计:
第一阶段(查询验证):通过学号查询学生是否存在
第二阶段(确认删除):显示学生详细信息后启用删除按钮
线程安全处理:
java
复制
// 使用AsyncTask实现后台操作(替代方案)
private class DeleteTask extends AsyncTask<Void, Void, Integer> {
@Override
protected Integer doInBackground(Void... voids) {
return dbHelper.deleteStudentByStudentId(targetStudent.getStudentId());
}
@Override
protected void onPostExecute(Integer result) {
// 处理UI更新
}
}
输入验证增强:
java
复制
// 在searchStudent()中添加学号格式验证
if (!studentId.matches("\d{8}")) {
etStudentId.setError("学号必须为8位数字");
return;
}
动画效果增强:
xml
复制
运行 HTML
java
复制
// 显示学生信息时添加动画
tvStudentInfo.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in));
第四步:从主界面跳转(与添加功能一致)
java
复制
// 在MainActivity中添加
Button btnDelete = findViewById(R.id.btnDelete);
btnDelete.setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, DeleteActivity.class);
startActivityForResult(intent, DELETE_REQUEST_CODE);
});
// 处理返回结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DELETE_REQUEST_CODE && resultCode == RESULT_OK) {
refreshStudentList(); // 刷新列表的方法
}
}
关键安全措施
防止误操作:
java
复制
// 添加确认对话框
private void showDeleteConfirmation() {
new AlertDialog.Builder(this)
.setTitle("确认删除")
.setMessage("确定要删除该学生吗?")
.setPositiveButton("删除", (dialog, which) -> deleteStudent())
.setNegativeButton("取消", null)
.show();
}
数据备份机制:
java
复制
// 在删除前创建备份
private void createBackup(Student student) {
ContentValues backup = new ContentValues();
backup.put("name", student.getName());
backup.put("gender", student.getGender());
backup.put("student_id", student.getStudentId());
dbHelper.getWritableDatabase().insert("backup_students", null, backup);
}
操作日志记录:
java
复制
// 记录删除操作
private void logDeletion(String studentId) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
String timestamp = sdf.format(new Date());
SharedPreferences logPrefs = getSharedPreferences("delete_log", MODE_PRIVATE);
logPrefs.edit()
.putString(timestamp, "删除学号:" + studentId)
.apply();
}
扩展功能建议
批量删除功能:
java
复制
// 在DBHelper中添加
public int deleteStudents(List
SQLiteDatabase db = this.getWritableDatabase();
try {
db.beginTransaction();
for (String id : studentIds) {
db.delete(TABLE_STUDENTS, COLUMN_STUDENT_ID + " = ?", new String[]{id});
}
db.setTransactionSuccessful();
return studentIds.size();
} finally {
db.endTransaction();
db.close();
}
}
回收站功能:
sql
复制
-- 修改数据库结构
ALTER TABLE students ADD COLUMN is_deleted INTEGER DEFAULT 0;
java
复制
// 改为逻辑删除
public int softDelete(String studentId) {
ContentValues values = new ContentValues();
values.put("is_deleted", 1);
return db.update(TABLE_STUDENTS, values, COLUMN_STUDENT_ID + " = ?", new String[]{studentId});
}
撤销删除功能:
java
复制
// 在DBHelper中添加
public int restoreStudent(String studentId) {
ContentValues values = new ContentValues();
values.put("is_deleted", 0);
return db.update(TABLE_STUDENTS, values, COLUMN_STUDENT_ID + " = ?", new String[]{studentId});
}
通过以上实现,您将获得一个安全、可靠且用户体验良好的删除功能模块。实际开发中可以根据需求选择实现扩展功能,建议优先实现确认对话框和操作日志记录等安全措施。
非常好太棒了,那同理UpdateActivity和对应的activity_update.xml该怎么写呢
以下是实现更新功能的完整方案,采用与添加/删除功能一致的架构模式:
第一步:创建更新界面(activity_update.xml)
xml
复制
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 搜索区域 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/etSearchId"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="输入要修改的学号"
android:inputType="number"/>
<Button
android:id="@+id/btnSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="查询"/>
</LinearLayout>
<!-- 信息展示区域(初始隐藏) -->
<LinearLayout
android:id="@+id/llEditArea"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:layout_marginTop="16dp">
<!-- 姓名编辑 -->
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="姓名">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- 性别选择 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="性别"
android:textSize="14sp"
android:layout_marginTop="8dp"/>
<RadioGroup
android:id="@+id/rgGender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rbMale"
android:text="男"/>
<RadioButton
android:id="@+id/rbFemale"
android:text="女"/>
</RadioGroup>
<!-- 新学号输入 -->
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="新学号"
android:layout_marginTop="8dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etNewStudentId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- 更新按钮 -->
<Button
android:id="@+id/btnUpdate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="确认更新"
android:enabled="false"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
</LinearLayout>
<!-- 状态提示 -->
<TextView
android:id="@+id/tvStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:textSize="14sp"/>
</LinearLayout>
运行 HTML
第二步:实现UpdateActivity核心逻辑
java
复制
public class UpdateActivity extends AppCompatActivity {
private DBHelper dbHelper;
private Student currentStudent;
// 视图组件
private EditText etSearchId, etName, etNewStudentId;
private RadioGroup rgGender;
private LinearLayout llEditArea;
private Button btnUpdate;
private TextView tvStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update);
initViews();
dbHelper = new DBHelper(this);
}
private void initViews() {
etSearchId = findViewById(R.id.etSearchId);
etName = findViewById(R.id.etName);
etNewStudentId = findViewById(R.id.etNewStudentId);
rgGender = findViewById(R.id.rgGender);
llEditArea = findViewById(R.id.llEditArea);
btnUpdate = findViewById(R.id.btnUpdate);
tvStatus = findViewById(R.id.tvStatus);
// 查询按钮点击
findViewById(R.id.btnSearch).setOnClickListener(v -> searchStudent());
// 更新按钮点击
btnUpdate.setOnClickListener(v -> showConfirmDialog());
}
private void searchStudent() {
String searchId = etSearchId.getText().toString().trim();
if (!validateSearchInput(searchId)) return;
new Thread(() -> {
currentStudent = dbHelper.getStudentByStudentId(searchId);
runOnUiThread(() -> {
if (currentStudent != null) {
displayStudentInfo(currentStudent);
llEditArea.setVisibility(View.VISIBLE);
btnUpdate.setEnabled(true);
tvStatus.setText("找到学生:" + currentStudent.getName());
} else {
llEditArea.setVisibility(View.GONE);
btnUpdate.setEnabled(false);
tvStatus.setText("未找到该学号的学生");
}
});
}).start();
}
private void displayStudentInfo(Student student) {
etName.setText(student.getName());
etNewStudentId.setText(student.getStudentId());
// 设置性别选择
if (student.getGender() == 0) {
rgGender.check(R.id.rbMale);
} else {
rgGender.check(R.id.rbFemale);
}
}
private void showConfirmDialog() {
new AlertDialog.Builder(this)
.setTitle("确认修改")
.setMessage("确定要更新该学生信息吗?")
.setPositiveButton("确定", (dialog, which) -> updateStudent())
.setNegativeButton("取消", null)
.show();
}
private void updateStudent() {
if (!validateUpdateInput()) return;
new Thread(() -> {
// 准备更新数据
Student updatedStudent = new Student(
etName.getText().toString().trim(),
rgGender.getCheckedRadioButtonId() == R.id.rbMale ? 0 : 1,
etNewStudentId.getText().toString().trim()
);
updatedStudent.setId(currentStudent.getId());
// 检查学号是否冲突
if (!currentStudent.getStudentId().equals(updatedStudent.getStudentId())) {
Student existStudent = dbHelper.getStudentByStudentId(updatedStudent.getStudentId());
if (existStudent != null) {
runOnUiThread(() -> {
etNewStudentId.setError("学号已存在");
tvStatus.setText("学号冲突,更新失败");
});
return;
}
}
// 执行更新
int result = dbHelper.updateStudent(updatedStudent);
runOnUiThread(() -> {
if (result > 0) {
tvStatus.setText("更新成功");
currentStudent = updatedStudent; // 更新当前对象
etSearchId.setText(updatedStudent.getStudentId()); // 自动更新搜索条件
} else {
tvStatus.setText("更新失败");
}
});
}).start();
}
// 输入验证方法
private boolean validateSearchInput(String searchId) {
if (searchId.isEmpty()) {
etSearchId.setError("请输入学号");
return false;
}
if (!searchId.matches("\\d{8}")) {
etSearchId.setError("学号必须为8位数字");
return false;
}
return true;
}
private boolean validateUpdateInput() {
String newName = etName.getText().toString().trim();
String newStudentId = etNewStudentId.getText().toString().trim();
if (newName.isEmpty()) {
etName.setError("姓名不能为空");
return false;
}
if (newStudentId.isEmpty()) {
etNewStudentId.setError("学号不能为空");
return false;
}
if (!newStudentId.matches("\\d{8}")) {
etNewStudentId.setError("学号必须为8位数字");
return false;
}
return true;
}
@Override
protected void onDestroy() {
dbHelper.close();
super.onDestroy();
}
}
第三步:增强功能实现说明
智能学号冲突检测:
java
复制
// 在updateStudent()中添加:
if (!originalId.equals(newId)) {
if (dbHelper.isStudentIdExist(newId)) {
showError("新学号已被其他学生使用");
return;
}
}
数据变更提示:
java
复制
// 添加文本监听实时检测修改
private void setupTextWatchers() {
TextWatcher watcher = new TextWatcher() {
public void afterTextChanged(Editable s) {
checkDataChanged();
}
// 其他回调方法...
};
etName.addTextChangedListener(watcher);
etNewStudentId.addTextChangedListener(watcher);
}
private void checkDataChanged() {
boolean isChanged = !etName.getText().toString().equals(currentStudent.getName()) ||
!etNewStudentId.getText().toString().equals(currentStudent.getStudentId()) ||
(rgGender.getCheckedRadioButtonId() == R.id.rbMale ? 0 : 1) != currentStudent.getGender();
btnUpdate.setEnabled(isChanged);
}
历史记录功能:
java
复制
// 在DBHelper中添加:
public void logUpdate(Student oldData, Student newData) {
ContentValues log = new ContentValues();
log.put("old_name", oldData.getName());
log.put("new_name", newData.getName());
log.put("update_time", System.currentTimeMillis());
db.insert("update_logs", null, log);
}
版本控制支持:
sql
复制
-- 创建历史记录表
CREATE TABLE student_history (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
student_id TEXT NOT NULL,
operation INTEGER, -- 0创建 1更新 2删除
content TEXT, -- JSON格式数据快照
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
第四步:扩展功能建议
修改记录查看:
java
复制
// 添加查看历史记录按钮
findViewById(R.id.btnHistory).setOnClickListener(v -> {
Intent intent = new Intent(this, HistoryActivity.class);
intent.putExtra("student_id", currentStudent.getStudentId());
startActivity(intent);
});
数据对比显示:
java
复制
// 在更新确认对话框显示修改对比
String diffInfo = "修改内容:\n" +
"姓名:" + currentStudent.getName() + " → " + etName.getText() + "\n" +
"学号:" + currentStudent.getStudentId() + " → " + etNewStudentId.getText();
new AlertDialog.Builder(this)
.setTitle("确认修改")
.setMessage(diffInfo)
.setPositiveButton("确认", /* ... */)
.show();
字段级修改控制:
java
复制
// 对敏感字段(如学号)添加二次验证
private void validateStudentIdChange() {
if (!etNewStudentId.getText().equals(currentStudent.getStudentId())) {
new AlertDialog.Builder(this)
.setTitle("重要变更提示")
.setMessage("修改学号会影响关联数据,确定要继续吗?")
.setPositiveButton("继续修改", null)
.setNegativeButton("取消修改", (d, w) -> resetStudentIdField())
.show();
}
}
自动填充建议:
java
复制
// 实现姓名自动补全
AutoCompleteTextView actvName = findViewById(R.id.actvName);
List
ArrayAdapter
android.R.layout.simple_dropdown_item_1line, nameSuggestions);
actvName.setAdapter(adapter);
最佳实践建议
使用数据绑定:
xml
复制
android {
dataBinding {
enabled = true
}
}
运行 HTML
采用MVVM架构:
java
复制
// 创建UpdateViewModel
public class UpdateViewModel extends ViewModel {
private MutableLiveData
public void loadStudent(String studentId) {
// 从数据库加载数据
}
public LiveData<Student> getStudent() {
return currentStudent;
}
}
添加单元测试:
java
复制
@RunWith(AndroidJUnit4.class)
public class UpdateTest {
@Test
public void testValidUpdate() {
// 测试正常更新流程
}
@Test
public void testDuplicateIdUpdate() {
// 测试学号冲突情况
}
}
国际化支持:
xml
复制
运行 HTML
以上实现方案提供了完整的更新功能实现,包含以下关键特性:
分阶段的查询→编辑→确认流程
智能化的输入验证和冲突检测
友好的用户反馈机制
扩展性强的基础架构
符合Material Design的设计规范
实际开发中可根据具体需求选择实现扩展功能,建议优先实现数据版本控制和操作日志功能,这对后续的审计和数据恢复非常重要。那查找功能SelectActivity和对应的activity_update.xml的功能如何实现,查找结果可以单独放在一个xml文件
以下是查找功能的完整实现方案,包含分页显示和多种查询方式:
第一步:创建布局文件
- 搜索界面 (activity_search.xml)
xml
复制
<!-- 搜索条件输入 -->
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入学号或姓名">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etKeyword"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- 搜索类型选择 -->
<RadioGroup
android:id="@+id/rgSearchType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="8dp">
<RadioButton
android:id="@+id/rbAll"
android:text="全部"
android:checked="true"/>
<RadioButton
android:id="@+id/rbStudentId"
android:text="按学号"/>
<RadioButton
android:id="@+id/rbName"
android:text="按姓名"/>
</RadioGroup>
<!-- 搜索按钮 -->
<Button
android:id="@+id/btnSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始搜索"
android:layout_marginTop="8dp"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"/>
<!-- 结果列表 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvResults"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:scrollbars="vertical"/>
<!-- 加载状态 -->
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
运行 HTML
2. 结果项布局 (item_student.xml)
xml
复制
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvStudentId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"/>
<TextView
android:id="@+id/tvGender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
运行 HTML
第二步:实现RecyclerView适配器
java
复制
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
private List
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(Student student);
}
public StudentAdapter(List<Student> students, OnItemClickListener listener) {
this.students = students;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_student, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Student student = students.get(position);
holder.tvStudentId.setText("学号:" + student.getStudentId());
holder.tvName.setText("姓名:" + student.getName());
holder.tvGender.setText("性别:" + (student.getGender() == 0 ? "男" : "女"));
holder.itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onItemClick(student);
}
});
}
@Override
public int getItemCount() {
return students.size();
}
public void updateData(List<Student> newStudents) {
students = newStudents;
notifyDataSetChanged();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvStudentId, tvName, tvGender;
ViewHolder(View itemView) {
super(itemView);
tvStudentId = itemView.findViewById(R.id.tvStudentId);
tvName = itemView.findViewById(R.id.tvName);
tvGender = itemView.findViewById(R.id.tvGender);
}
}
}
第三步:实现SearchActivity
java
复制
public class SearchActivity extends AppCompatActivity {
private DBHelper dbHelper;
private StudentAdapter adapter;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
dbHelper = new DBHelper(this);
progressBar = findViewById(R.id.progressBar);
RecyclerView recyclerView = findViewById(R.id.rvResults);
// 初始化RecyclerView
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new StudentAdapter(new ArrayList<>(), this::showDetailDialog);
recyclerView.setAdapter(adapter);
setupSearch();
}
private void setupSearch() {
EditText etKeyword = findViewById(R.id.etKeyword);
RadioGroup rgSearchType = findViewById(R.id.rgSearchType);
Button btnSearch = findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(v -> {
String keyword = etKeyword.getText().toString().trim();
int searchType = getSearchType(rgSearchType);
if (validateInput(keyword, searchType)) {
performSearch(keyword, searchType);
}
});
}
private int getSearchType(RadioGroup radioGroup) {
switch (radioGroup.getCheckedRadioButtonId()) {
case R.id.rbStudentId: return 1;
case R.id.rbName: return 2;
default: return 0; // 全部
}
}
private boolean validateInput(String keyword, int searchType) {
if (searchType != 0 && keyword.isEmpty()) {
Toast.makeText(this, "请输入搜索关键词", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
private void performSearch(String keyword, int searchType) {
progressBar.setVisibility(View.VISIBLE);
new Thread(() -> {
List<Student> results = new ArrayList<>();
switch (searchType) {
case 0: // 全部
results = dbHelper.getAllStudents();
break;
case 1: // 学号
Student student = dbHelper.getStudentByStudentId(keyword);
if (student != null) results.add(student);
break;
case 2: // 姓名
results = dbHelper.searchStudentsByName(keyword);
break;
}
runOnUiThread(() -> {
progressBar.setVisibility(View.GONE);
adapter.updateData(results);
if (results.isEmpty()) {
Toast.makeText(this, "未找到匹配结果", Toast.LENGTH_SHORT).show();
}
});
}).start();
}
private void showDetailDialog(Student student) {
new AlertDialog.Builder(this)
.setTitle("详细信息")
.setMessage(formatStudentInfo(student))
.setPositiveButton("关闭", null)
.show();
}
private String formatStudentInfo(Student student) {
return String.format(
"学号:%s\n姓名:%s\n性别:%s",
student.getStudentId(),
student.getName(),
student.getGender() == 0 ? "男" : "女"
);
}
@Override
protected void onDestroy() {
dbHelper.close();
super.onDestroy();
}
}
开发过程