将回收站视图数据中的 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 包中使用它:ShiftFragmentShiftAdapter

【讨论】:

以上是关于将回收站视图数据中的 onMove() 更改更新到房间数据库的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 回收站视图中重新排序项目后,位置未更新

在复选框状态更改时从回收器视图适配器更新 sqlite 数据库

不更新片段中的回收站视图

如何找出分页数据适配器中的第一项已更改

无法将 Gridview 的更改更新到数据库

在不重新加载活动的情况下更新回收站视图中的内容