Java Android:在RecyclerView中拖放项目时,如何有效地更新SQLite数据库?
我有这个待办事项列表应用程序,其中“任务”可以包含自己的“子任务”列表。问题是,每当这些“任务”通过拖放重新排列时,有时会给我一些奇怪的行为,比如在单击它们以显示其子列表时崩溃 以下是拖放侦听器的代码:Java Android:在RecyclerView中拖放项目时,如何有效地更新SQLite数据库?,java,android,sqlite,android-asynctask,background,Java,Android,Sqlite,Android Asynctask,Background,我有这个待办事项列表应用程序,其中“任务”可以包含自己的“子任务”列表。问题是,每当这些“任务”通过拖放重新排列时,有时会给我一些奇怪的行为,比如在单击它们以显示其子列表时崩溃 以下是拖放侦听器的代码: private void attachItemTouchHelperToAdapter() { ItemTouchHelper.SimpleCallback touchCallback = new ItemTouchHelper.SimpleCallback(It
private void attachItemTouchHelperToAdapter() {
ItemTouchHelper.SimpleCallback touchCallback =
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
final int fromPosition = viewHolder.getAdapterPosition();
final int toPosition = target.getAdapterPosition();
rearrangeTasks(fromPosition, toPosition);
// Call background thread to update database.
new UpdateDatabaseTask(mTaskList).execute();
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Task taskToDelete = mTaskAdapter.mTasks.get(position);
TaskLab.get(getActivity()).deleteTask(taskToDelete);
updateUI(position);
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchCallback);
itemTouchHelper.attachToRecyclerView(mTaskRecyclerView);
}
重新排列onMove中调用的列表的代码:
private void rearrangeTasks(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mTaskList, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mTaskList, i, i - 1);
}
}
mTaskAdapter.notifyItemMoved(fromPosition, toPosition);
}
调用数据库帮助程序函数,以使用重新排列的列表更新整个表:
public void updateDatabase(List<Task> tasks) {
mDatabase.delete(TaskTable.NAME, null, null);
for (Task task : tasks) {
addTask(task);
}
}
public void updateDatabase(列出任务){
mDatabase.delete(TaskTable.NAME,null,null);
for(任务:任务){
添加任务(task);
}
}
这里的问题是,有时,当快速拖放“任务”时,AsyncTask更新数据库的速度不够快,因此当我试图在数据库完成更新之前对其执行函数时,会出现奇怪的行为
<> P> > SQLite数据库更新的更好方法是什么?<代码> RealReVIEW < /代码>是拖放的? < P>这个问题太宽泛,无法回答,因为在解决您的问题时,您可以考虑几种解决方案。我现在可以想到两个,下面我将简要介绍一下 解决方案#1
可以考虑调用数据库方法直接更新表,而不必通过<代码> AycCastase调用它。在数据库表中注册内容观察者,以便每次更新表时,
RecyclerView
都会随之更新,而无需调用notifyDataSetChanged()
。示例实现应该如下所示
public class Task {
public int taskId;
public int taskPosition;
}
public void updateDatabase(Task taskToBeUpdated, int newPosition) {
// You need two update queries.
// Update task_table set task_position += task_position + 1 where task_position >= newPosition;
// Update task_table set task_position = newPosition where taskId = taskTobeUpdated.taskId
}
像这样声明数据库表的URI
public static final Uri DB_TABLE_TASKS_URI = Uri
.parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TASKS);
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new SQLiteCursorLoader(getActivity()) {
@Override
public Cursor loadInBackground() {
Cursor cursor;
cursor = TasksLab.getTasks();
// Register the cursor with the content observer
this.registerContentObserver(cursor, DBConstants.DB_TABLE_TASKS_URI);
return cursor;
}
};
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Set the cursor in the adapter of your RecyclerView here.
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
// Do not forget to destroy the loader when you are destroying the view.
@Override
public void onDestroy() {
getLoaderManager().destroyLoader(TASKS_QUERY_LOADER);
super.onDestroyView();
}
现在,在您的updateDatabase
函数中,您需要调用此notify函数,在任务表发生更改或更新时通知正在收听此内容的观察者
public static void updateDatabase(List<Task> taskList) {
// .. Your other implementation goes here
context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TASKS_URI, null);
}
现在,您需要在TasksActivity
中实现LoaderCallbacks
的功能。一个示例实现应该是这样的
public static final Uri DB_TABLE_TASKS_URI = Uri
.parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TASKS);
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new SQLiteCursorLoader(getActivity()) {
@Override
public Cursor loadInBackground() {
Cursor cursor;
cursor = TasksLab.getTasks();
// Register the cursor with the content observer
this.registerContentObserver(cursor, DBConstants.DB_TABLE_TASKS_URI);
return cursor;
}
};
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Set the cursor in the adapter of your RecyclerView here.
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
// Do not forget to destroy the loader when you are destroying the view.
@Override
public void onDestroy() {
getLoaderManager().destroyLoader(TASKS_QUERY_LOADER);
super.onDestroyView();
}
现在,当您离开TaskActivity
时,您需要将更改保存到数据库中
@Override
public void onDestroy() {
updateDatabaseFromTheList();
super.onDestroyView();
}
@Override
public void onPause() {
updateDatabaseFromTheList();
super.onDestroyView();
}
并在onResume
函数中每次从数据库加载任务
@Override
public void onResume() {
super.onResume();
updateRecyclerViewFromDatabase();
}
更新
基于上述评论中解决方案1所需的澄清
“我建议只传递被拖动或删除的任务id
更新,以便表立即得到更新。“。我该怎么办
这样做
我不确定您现在是如何更新任务表的。然而,我可以想到一些我自己的实现。只需将要更新的任务传递给它在任务表中的位置。因此,伪实现可能如下所示
public class Task {
public int taskId;
public int taskPosition;
}
public void updateDatabase(Task taskToBeUpdated, int newPosition) {
// You need two update queries.
// Update task_table set task_position += task_position + 1 where task_position >= newPosition;
// Update task_table set task_position = newPosition where taskId = taskTobeUpdated.taskId
}
希望这能有所帮助。感谢您付出这么多努力来回答这个问题。您说过,“我建议只传递被拖动或更新的任务id,以便表立即得到更新。”。我该怎么做?