将回收站视图数据中的 onMove() 更改更新到房间数据库
Posted
技术标签:
【中文标题】将回收站视图数据中的 onMove() 更改更新到房间数据库【英文标题】:Update onMove() changes in recycler view data to room database 【发布时间】:2019-09-20 19:17:32 【问题描述】:根据一些文章,我正在尝试使用以下内容创建一个 android 应用程序:
recyclerview
使用实时数据从房间数据库中获取数据。
数据结构是自定义对象的列表,具有用于对数据进行排序的属性。
recyclerview 中的功能:
拖放以重新排序数据 滑动删除 取消滑动删除操作推荐文章:
-
Google codelabs for room database and live data
AndroidHive article for recyclerview swipe to delete and undo to restore deleted item
Medium post by Paul Burke for drag/drop in recycler view
Medium post by Paul Burke for customizing dragged item in recycler view
SO Post to detect drop event in recycler view
我的问题: 房间库中的数据重新排序不会更新。
注意: 我正在使用属性进行数据排序
如果需要特定文件的代码,请发表评论。我不确定要发布哪个代码。
MainFragment.java(重新排序数据的代码,不起作用)
// To handle recycler view item dragging
@Override
public void onItemMove(int fromPosition, int toPosition)
// Log
Log.e(TAG, "Item moved from " + fromPosition + " to " + toPosition);
// Move the item within the recycler view
mainRecyclerViewAdapter.moveItem(fromPosition, toPosition);
// To handle recycler view item drop
@Override
public void onItemDragged(int fromPosition, int toPosition)
// Log
Log.e(TAG, "Item dragged from " + fromPosition + " to " + toPosition);
mainActivityViewModel.moveWord(fromPosition, toPosition);
MainActivityViewModel.java
public void moveWord(int fromPosition, int toPosition)
// Move word
wordRepository.move(fromPosition, toPosition);
WordRepository.java
public void move(int from, int to)
new moveAsyncTask(wordDao).execute(from, to);
// Async update task
private static class moveAsyncTask extends AsyncTask<Integer, Void, Void>
// Dao
private WordDao asyncTaskDao;
// Constructor
moveAsyncTask(WordDao wordDao)
// Get dao
asyncTaskDao = wordDao;
@Override
protected Void doInBackground(final Integer... params)
int from = params[0];
int to = params[1];
if (from > to)
// Move upwards
asyncTaskDao.getAllWordsBetween(to, from - 1).forEach(wordToUpdate ->
// Update word number
wordToUpdate.decreaseSNo();
// Update word in database
update(wordToUpdate);
);
asyncTaskDao.getWordWithNo(from).forEach(wordToUpdate ->
// Update word number
wordToUpdate.setSno(to);
// Update word in database
update(wordToUpdate);
);
else
// Move downwards
asyncTaskDao.getAllWordsBetween(from + 1, to).forEach(wordToUpdate ->
// Update word number
wordToUpdate.increaseSNo();
// Update word in database
update(wordToUpdate);
);
asyncTaskDao.getWordWithNo(from).forEach(wordToUpdate ->
// Update word number
wordToUpdate.setSno(to);
// Update word in database
update(wordToUpdate);
);
return null;
WordDao.java
@Query("SELECT * FROM words WHERE sno >= :low AND sno <= :high")
List<Word> getAllWordsBetween(int low, int high);
@Query("SELECT * FROM words WHERE sno == :sNo")
List<Word> getWordWithNo(int sNo);
【问题讨论】:
你能发布你的完整代码示例吗? Github 链接失效。 @blueware,这个问题是在一年前发布的,此后由于进行了很多更改,因此该回购已被私有化。接受的答案解决了我的问题。谢谢。 【参考方案1】:这是订购物品的简单方法,与房间数据库一起使用。
实体对象(模型):
订单列,输入整数:
@ColumnInfo(name = "word_order")
private Integer mOrder;
在道:
一个查询,检索列 word_order
中的最大订单:
@Query("SELECT MAX(word_order) FROM word_table")
int getLargestOrder();
还有一个更新整个单词列表的查询:
@Update(onConflict = OnConflictStrategy.REPLACE)
void update(List<WordEntity> wordEntities);
使用
MAX
SQL 命令获取最大数。
在活动中(添加新词时):
查询该方法getLargestOrder()
并为其添加+1,然后创建一个新词。
在活动中(onCreate()):
创建ItemTouchHelper
移动单词项目,只使用UP & DOWN 移动:
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP | ItemTouchHelper.DOWN)
);
这是一个实用程序类,用于向 RecyclerView 添加滑动以关闭和拖放支持。 它与 RecyclerView 和 Callback 类一起使用,该类配置启用的交互类型,并在用户执行这些操作时接收事件。
覆盖其中的getMovementFlags
以指定所需的方向:
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
0);
在其中覆盖 onMove
以交换项目位置:
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
if (fromPosition < toPosition)
for (int i = fromPosition; i < toPosition; i++)
Collections.swap(mWordEntities, i, i + 1);
int order1 = mWordEntities.get(i).getOrder();
int order2 = mWordEntities.get(i + 1).getOrder();
mWordEntities.get(i).setOrder(order2);
mWordEntities.get(i + 1).setOrder(order1);
else
for (int i = fromPosition; i > toPosition; i--)
Collections.swap(mWordEntities, i, i - 1);
int order1 = mWordEntities.get(i).getOrder();
int order2 = mWordEntities.get(i - 1).getOrder();
mWordEntities.get(i).setOrder(order2);
mWordEntities.get(i - 1).setOrder(order1);
mAdapter.notifyItemMoved(fromPosition, toPosition);
return true;
此方法在移动时更新项目位置。
首先,从适配器获取项目的位置。
然后,如果递减,则交换 Entities 的项目,通过传递当前项目位置和新项目位置(对于用户而言)。
之后,获取当前项目和下一个项目的顺序,并用setter方法将它们擦拭并设置它们(以便稍后将它们保存在Room中)。
另一方面,对增量执行相同操作。
完成,通过通知适配器项目已移动(对于用户的眼睛)。
覆盖 clearView
以更新 Word 实体列表:
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
super.clearView(recyclerView, viewHolder);
mWordViewModel.update(mWordEntities);
当用户放下物品时调用此方法。它将适合更新实体并将其保存到房间数据库中。
覆盖onSwiped
:
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
这必须被系统覆盖。
将 ItemTouchHelper
附加到 RecyclerView:
itemTouchHelper.attachToRecyclerView(mRecyclerView);
您可以创建许多 ItemTouchHelper 并根据需要将它们附加到 RecyclerView。
请注意,您需要在 RecyclerView 中从最大数量到最小数量对项目进行排序。希望这对你有帮助:)
【讨论】:
mwordentities 从何而来? @PedroPinheiro 是一个全局变量,其中包含所有单词实体的列表,因此更新后的列表可以稍后输入mWordViewModel
,从而允许数据库进行相应更新【参考方案2】:
嗨:)我会这样做:
广告 1。移动物品
我会在您的 Word 对象中添加一个“位置”列,并且我会这样做等于您的 words.size()。这将使其“自动增量”,但不一定是唯一的(这很重要)。
因此,当您将新 Word 添加到数据库时,您可以这样做:
wordsDao.insert(new Word(position, whateverYouNeedToAdd));
用你的话说道:
@Query("SELECT * FROM word ORDER BY position ASC")
LiveData<List<Word>> sortByPosition();
在 ViewModel 中:
public WordsViewModel(@NonNull Application application)
super(application);
wordsDao = getDatabase(application).wordsDao();
wordsLiveData = wordsDao.sortByPosition();
public LiveData<List<Word>> getWordsList()
return wordsLiveData;
在您显示列表的活动中:
public void initData()
wordsViewModel = ViewModelProviders.of(this).get(WordsViewModel.class);
wordsViewModel.getWordsList().observe(this, new Observer<List<Word>>()
@Override
public void onChanged(@Nullable List<Word> words)
adapter.setWordsList(words);
if (words == null)
position = 0;
else
position = words.size();
);
在您的适配器中:
void setWordsList(List<Word> wordsList)
this.wordsList = wordsList;
notifyDataSetChanged();
要移动项目:
public void onItemMove(int fromPosition, int toPosition)
if (fromPosition < toPosition)
for (int i = fromPosition; i < toPosition; i++)
Collections.swap(wordsList, i, i + 1);
else
for (int i = fromPosition; i > toPosition; i--)
Collections.swap(wordsList, i, i - 1);
notifyItemMoved(fromPosition, toPosition);
我会在适配器中创建一个方法,您可以在其中更新数据库中的位置:
void setIndexInDatabase()
WordsDao wordsDao = getDatabase(context).wordsDao();
for (Word word : wordsList)
word.setPosition(wordsList.indexOf(word));
wordsDao.update(word);
我会在您显示列表的活动中调用该方法onDestroy
:
@Override
public void onDestroy()
super.onDestroy();
adapter.setIndexInDatabase();
所以现在用户可以随心所欲地移动项目,当他完成并进入另一个活动时,新订单将保存在您的数据库中。
广告 2。删除项目
首先我会在你的适配器中创建一个 Arraylist 来存储已删除项目的列表
private ArrayList<String> deletedWords = new ArrayList<>();
然后我会创建删除和撤消方法:
public void deleteItem(int position)
word = wordsList.get(position);
wordPosition = position;
deletedWord = word.getWord();
deletedWords.add(deletedWord);
wordsList.remove(position);
notifyItemRemoved(position);
showUndoSnackbar();
我正在向我们创建的这个数组中添加一个您要删除的单词。
private void showUndoSnackbar()
Snackbar snackbar = Snackbar.make( (((Activity) context).findViewById(android.R.id.content)), R.string.snack_bar_text,
Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.snack_bar_undo, new View.OnClickListener()
@Override
public void onClick(View v)
undoDelete();
);
snackbar.show();
private void undoDelete()
wordsList.add(wordPosition,
word);
deletedWords.remove(deletedWord);
notifyItemInserted(wordPosition);
在undoDelete
中,我将从我们的ArrayList
中删除这个已删除的词
我会创建一个方法来从数据库中删除存储在我们的ArrayList
中的所有这些单词:
void deleteFromDatabase()
WordsDao wordsDao = getDatabase(context).wordsDao();
if (deletedWords != null)
for (int i = 0; i < deletedWords.size(); i++)
wordsDao.deleteByWord(deletedWords.get(i));
我会像以前一样在onDestroy
中调用它:
@Override
public void onDestroy()
super.onDestroy();
adapter.setIndexInDatabase();
adapter.deleteFromDatabase();
我希望我没有忘记任何事情 :) 我在我现在写的应用程序中做了类似的事情,所以你也可以在 github 上查看。
我在 Shift 包中使用它:ShiftFragment
和 ShiftAdapter
。
【讨论】:
以上是关于将回收站视图数据中的 onMove() 更改更新到房间数据库的主要内容,如果未能解决你的问题,请参考以下文章