Android:在 RecyclerView 中拖放项目时如何有效更新 SQLite 数据库?
Posted
技术标签:
【中文标题】Android:在 RecyclerView 中拖放项目时如何有效更新 SQLite 数据库?【英文标题】:Android: How do I effectively update the SQLite database when dragging and dropping items in the RecyclerView? 【发布时间】:2018-07-26 23:35:16 【问题描述】:我有这个待办事项列表应用程序,其中“任务”可以包含他们自己的“子任务”列表。问题是,每当这些“任务”通过拖放重新排列时,有时会出现奇怪的行为,例如单击它们以显示其子列表时崩溃。
这是拖放监听器的代码:
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);
调用更新sqlite数据库的AsyncTask
:
private class UpdateDatabaseTask extends AsyncTask<Void, Void, Void>
private List<Task> mTaskList;
public UpdateDatabaseTask(List<Task> taskList)
mTaskList = taskList;
@Override
protected Void doInBackground(Void... voids)
TaskLab.get(getActivity()).updateDatabase(mTaskList);
return null;
@Override
protected void onPostExecute(Void aVoid)
调用数据库辅助函数以使用重新排列的列表更新整个表:
public void updateDatabase(List<Task> tasks)
mDatabase.delete(TaskTable.NAME, null, null);
for (Task task : tasks)
addTask(task);
这里的问题是,有时,当快速拖放“任务”时,AsyncTask 更新数据库的速度不够快,因此当我尝试在它完成更新之前对其执行功能时,会出现奇怪的行为。
在 RecyclerView
拖放时更新 SQLite 数据库的更好方法是什么?
【问题讨论】:
【参考方案1】:这个问题太宽泛,无法回答,因为您可以考虑多种解决方案来解决您的问题。我现在可以想到两个,在这里我将简要描述它们。
解决方案 #1
您可以考虑直接调用更新表的数据库方法,而不是通过AsyncTask
调用它。在您的数据库表中注册一个内容观察者,以便每次更新表时,RecyclerView
都会随之更新,而无需调用 notifyDataSetChanged()
。示例实现应如下所示。
像这样声明数据库表的 URI。
public static final Uri DB_TABLE_TASKS_URI = Uri
.parse("sqlite://" + Constants.ApplicationPackage + "/" + DB_TABLE_TASKS);
现在在您的updateDatabase
函数中,当您的任务表发生更改或更新时,您需要调用此通知函数来通知监听此内容的观察者。
public static void updateDatabase(List<Task> taskList)
// .. Your other implementation goes here
context.getContentResolver().notifyChange(DBConstants.DB_TABLE_TASKS_URI, null);
现在使用LoaderCallbacks
在RecyclerView
中显示sqlite 表中的任务。
public class TasksActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor>
private final TAKS_QUERY_LOADER = 0;
protected void onCreate(Bundle savedInstanceState)
// Other code.
// Get your cursor loader initialized here.
getLoaderManager().initLoader(TASKS_QUERY_LOADER, null, this).forceLoad();
现在您需要在您的TasksActivity
中实现您的LoaderCallbacks
的功能。一个示例实现应如下所示。
@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();
就是这样。您现在可以在您的 sqlite 表中立即更新您的任务,并立即在您的RecyclerView
中获得反映。我强烈建议您一次更新一个任务。如我所见,您正在使用updateDatabase
函数在此处传递整个任务列表来更新数据库表。我建议只传递被拖动或更新的任务 ID,以便表格立即更新。
解决方案 #2
此解决方案比前一个解决方案更简单。这个想法是在刷新列表或销毁视图时更新您的 sqlite 数据库。
当一个任务被拖到某个地方(即更新其位置)或删除时,您需要将轨道保存在您的TasksActivity
中的某个ArrayList
中。不要立即更新数据库中的 sqlite 表,也不要使用 AsyncTask
。当您在 onDestroy
或 onPause
方法中销毁视图时,请保存此任务以备后用。在此过程中,每次您的Activity
恢复时(即在onResume
函数中),您都需要刷新您的RecyclerView
。所以这里是伪实现。
@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);
// Do not call background thread to update database.
// new UpdateDatabaseTask(mTaskList).execute();
// Keep a local ArrayList and keep the track of dragging the item.
updateLocalArrayListWhichIsBindToRecyclerView(mTaskList);
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);
updateLocalArrayListWhichIsBindedToRecyclerView(taskToDelete);
updateUI(position);
现在,当您离开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,以便表格立即更新。”。我该怎么做?以上是关于Android:在 RecyclerView 中拖放项目时如何有效更新 SQLite 数据库?的主要内容,如果未能解决你的问题,请参考以下文章