结合ai开发简单的增删改查app

news/2025/3/13 21:01:09/文章来源:https://www.cnblogs.com/mlo9i/p/18770960

我在做一个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 getAllStudents() {
List students = new ArrayList<>();
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 students = dbHelper.getAllStudents();
adapter.updateList(students);
adapter.notifyDataSetChanged();
}

// 自定义Adapter
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {
private List mData;

public void updateList(List<Student> newList) {mData = newList;
}

}
🎨 5. 性别输入优化建议

xml
复制

<RadioButtonandroid:id="@+id/radioMale"android:text="男"android:checked="true"/><RadioButtonandroid: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 allStudents = dbHelper.getAllStudents();

// 修改学生信息
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
复制

<!-- 姓名输入 -->
<EditTextandroid:id="@+id/etName"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入姓名"android:inputType="textPersonName"/><!-- 性别选择 -->
<RadioGroupandroid:id="@+id/rgGender"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_marginTop="8dp"><RadioButtonandroid:id="@+id/rbMale"android:text="男"android:checked="true"/><RadioButtonandroid:id="@+id/rbFemale"android:text="女"/>
</RadioGroup><!-- 学号输入 -->
<EditTextandroid:id="@+id/etStudentId"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入学号"android:inputType="number"android:layout_marginTop="8dp"/><!-- 提交按钮 -->
<Buttonandroid: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
复制

<!-- 学号输入 -->
<EditTextandroid:id="@+id/etStudentId"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入要删除的学号"android:inputType="number"android:maxLines="1"/><!-- 双功能按钮布局 -->
<LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_marginTop="16dp"><Buttonandroid:id="@+id/btnSearch"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="查询学生"android:layout_marginEnd="8dp"/><Buttonandroid: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><!-- 学生信息展示 -->
<TextViewandroid: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 studentIds) {
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
复制

<LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><!-- 搜索区域 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><EditTextandroid:id="@+id/etSearchId"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:hint="输入要修改的学号"android:inputType="number"/><Buttonandroid:id="@+id/btnSearch"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="8dp"android:text="查询"/></LinearLayout><!-- 信息展示区域(初始隐藏) --><LinearLayoutandroid: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.TextInputLayoutstyle="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="姓名"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/etName"android:layout_width="match_parent"android:layout_height="wrap_content"android:maxLines="1"/></com.google.android.material.textfield.TextInputLayout><!-- 性别选择 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="性别"android:textSize="14sp"android:layout_marginTop="8dp"/><RadioGroupandroid:id="@+id/rgGender"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/rbMale"android:text="男"/><RadioButtonandroid:id="@+id/rbFemale"android:text="女"/></RadioGroup><!-- 新学号输入 --><com.google.android.material.textfield.TextInputLayoutstyle="@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.TextInputEditTextandroid:id="@+id/etNewStudentId"android:layout_width="match_parent"android:layout_height="wrap_content"android:inputType="number"/></com.google.android.material.textfield.TextInputLayout><!-- 更新按钮 --><Buttonandroid: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><!-- 状态提示 --><TextViewandroid: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 nameSuggestions = dbHelper.getAllNames();
ArrayAdapter adapter = new ArrayAdapter<>(this,
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 currentStudent = new 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
复制

更新成功

Update Success
运行 HTML
以上实现方案提供了完整的更新功能实现,包含以下关键特性:

分阶段的查询→编辑→确认流程

智能化的输入验证和冲突检测

友好的用户反馈机制

扩展性强的基础架构

符合Material Design的设计规范

实际开发中可根据具体需求选择实现扩展功能,建议优先实现数据版本控制和操作日志功能,这对后续的审计和数据恢复非常重要。那查找功能SelectActivity和对应的activity_update.xml的功能如何实现,查找结果可以单独放在一个xml文件
以下是查找功能的完整实现方案,包含分页显示和多种查询方式:

第一步:创建布局文件

  1. 搜索界面 (activity_search.xml)

xml
复制

<!-- 搜索条件输入 -->
<com.google.android.material.textfield.TextInputLayoutstyle="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="输入学号或姓名"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/etKeyword"android:layout_width="match_parent"android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout><!-- 搜索类型选择 -->
<RadioGroupandroid:id="@+id/rgSearchType"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:layout_marginTop="8dp"><RadioButtonandroid:id="@+id/rbAll"android:text="全部"android:checked="true"/><RadioButtonandroid:id="@+id/rbStudentId"android:text="按学号"/><RadioButtonandroid:id="@+id/rbName"android:text="按姓名"/>
</RadioGroup><!-- 搜索按钮 -->
<Buttonandroid: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.RecyclerViewandroid:id="@+id/rvResults"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="16dp"android:scrollbars="vertical"/><!-- 加载状态 -->
<ProgressBarandroid: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">

<LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:padding="16dp"><TextViewandroid:id="@+id/tvStudentId"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16sp"android:textStyle="bold"/><TextViewandroid:id="@+id/tvName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="4dp"/><TextViewandroid: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 students;
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);// 初始化RecyclerViewrecyclerView.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();
}

}
开发过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/898364.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于pyqt5的开发环境搭建

1、安装python 3.11.5 2、升级pip python -m pip install --upgrade pip 3、安装PyQt5相关包 pip install PyQt5 pip install PyQt5-tools 4、配置pycharm------ 参考:https://blog.csdn.net/Pan_peter/article/details/130606896您的资助是我最大的动力! 金额随意,欢迎来赏!…

荣耀magic 7 pro 连接三星watch6

1.安装三星智能穿戴app(自己download文件夹的)。然后打开三星智能穿戴app时自动下载wear os。 2、打开wear os,允许所有权限,打开三星智能穿戴进行手表连接。在手机中会自动安装以下三个软件: com.samsung.wearable.watch6plugin com.samsung.wearable.watchfaceplugin co…

【博主亲测可用】草图大师SketchUp Pro 2025特别版(SU最新版)

软件介绍 SketchUp Pro 2025 是一款专为建筑设计、室内设计、景观规划和游戏开发等领域打造的3D建模软件。作为最新版本,它在保留易用性和灵活性的基础上,引入了多项创新功能,显著提升了设计效率和可视化效果。以下是其核心亮点:新增功能环境面板 新增360图像背景、天空穹顶…

回文自动机

回文自动机 PAM 是一种处理回文串的工具,每个节点都表示一个本质不同的回文串。 PAM 由两棵树组成,分别表示奇数长度的回文串与偶数长度的回文串。称两棵树的根为奇根和偶根,分别表示长度为 \(-1, 0\) 的字符串,不表示任何实际的字符串,仅作为初始状态。 定义:\(fail\) 指…

python 实验一(20241227曹鹏泰)

学号 20241227 《Python程序设计》实验1报告 课程:《Python程序设计》 班级: 2412 姓名: 曹鹏泰 学号:20241227 实验教师:王志强 实验日期:2025年3月13日 必修/选修: 公选课 1.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能;(编写书中的程序,并进行…

Leading Robots | 贪心 单调栈

题意:机器人比赛跑步,知道了初始位置,和加速度,求有多少机器人能成为领头羊(在某一刻跑到第一名,不能并列) 思路:因为知道了加速度,所以很容易知道,当加速度最快的成为了领头羊,其他机器人就没机会了,并且,当一个机器人在初始位置和加速度都比前面的机器人小的话,…

鸿蒙用户首选项数据持久化

用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取的时候,能够快速从内存中获取数据,当需要持久化时可…

基于 .NET Blazor 开源、低代码、易扩展的插件开发框架

前言 今天大姚给大家分享一个基于 .NET Blazor 开源的轻量级、跨平台、低代码、易扩展的插件开发框架:Known。 项目介绍 Known 是一个基于 Blazor 的轻量级、跨平台、低代码、易扩展的插件开发框架。它旨在帮助开发者快速构建和扩展应用程序,提供了一整套工具和组件,从前端到…

unity Terrain绘制树时提示所选树没有LOD组解决方法

个人理解LOD组是一种unity优化资源显示的方法,为了节省资源, 如果刷绘的预制件没有LOD组,刷多了无论远近都是显示状态,就很卡解决方法:给需要绘制的树预制件添加LOD组 组件 根据不同阶段放入不同显示的模型或者网格,预制件添加后记得保存应用,这样就可以在Terrain绘制树了 时间…

什么是自然语言的概率分布

自然语言的概率分布,是指在自然语言处理(NLP)中,对语言单位(如单词、短语、句子等)出现的概率进行建模和描述的方式。 它反映了语言使用的统计规律,即某些语言单位比其他单位更常出现。 核心思想: 自然语言并非随机的字符组合,而是遵循一定的统计规律。有些单词、短语…

GESP-Lv8总结(202409)

GESP C++ 八级 2024 年 09 月 题目链接 错题 ( T1 ) 下面关于C++类和对象的说法,错误的是 A. 类的析构函数可以为虚函数 B. 类的构造函数不可以为虚函数 C. class中成员的默认访问权限为private D. struct中成员的默认访问权限为private \(\color{red}{根本不会:)}\) 这道题有…

20242934 2024-2025-2 《网络攻防实践》第3周作业

1.实验内容 实验一:网络嗅探:网络嗅探利用计算机网络接口截获其他计算机数据报文,以监听数据流中包含的有效信息。由于捕获到的数据报文是经过封包处理的二进制数据,故还要结合网络协议分析技术。 使用tcpdump开源软件对在本机上访问www.tianya.cn网站过程进行嗅探,回答问…