将回收者视图数据中的onMove()更新更新到房间数据库

时间:2019-05-02 09:34:57

标签: android android-recyclerview drag-and-drop android-room

在阅读一些文章之后,我尝试使用以下内容创建一个android应用程序:

  • 一个recyclerview,它使用实时数据从会议室数据库中获取数据。
  • 数据结构是具有属性的自定义对象列表,用于对数据进行排序。

recyclerview中的功能:

  • 拖放以重新排序数据
  • 滑动以删除
  • 滑动删除的UNDO操作

引荐文章:

  1. Google codelabs for room database and live data
  2. AndroidHive article for recyclerview swipe to delete and undo to restore deleted item
  3. Medium post by Paul Burke for drag/drop in recycler view
  4. Medium post by Paul Burke for customizing dragged item in recycler view
  5. SO Post to detect drop event in recycler view

我的问题:
房间资料库中的数据重新排序不会更新。

注意:
我正在使用属性进行数据排序

如果您需要实现的更多详细信息,请参考this GitHub repository。我为此问题创建了一个示例应用。
添加有问题的完整代码似乎太长了,请参考我的github存储库以获取代码

如果需要特定文件的代码,请发表评论。我不确定要发布哪个代码。

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);

2 个答案:

答案 0 :(得分:1)

这是一种简单的订购商品的方法,与Room数据库一起使用。

实体对象(模型):

订单列,输入Integer:

@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;
        }
  

此方法可在移动物品时更新其位置。

     
      
  • 首先,从适配器获取物品的位置。

  •   
  • 然后,如果递减,则通过传递当前项位置和新项位置(供用户查看)来交换实体项。

  •   
  • 然后,获得当前商品和下一个商品的订单,并对其进行擦拭并使用setter方法进行设置(以便稍后将其保存在Room中)。

  •   
  • 另一方面,对增量进行相同的操作。

  •   
  • 完成,通过通知适配器项已移动(供用户查看)。

  •   

覆盖clearView以更新单词实体列表:

        @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中从最大编号到最低编号订购商品。希望对您有帮助:)

答案 1 :(得分:1)

嗨:)我会这样:

Ad.1。正在移动物品

  1. 我将在您的Word对象中添加一个“位置”列,并使其与您的words.size()相等。这将使其成为“自动增量”,但不一定是唯一的(这很重要)。

    因此,当您向数据库中添加新的Word时,您将执行以下操作: wordsDao.insert(new Word(position, whateverYouNeedToAdd));

  2. 用你的话语道:

    @Query("SELECT * FROM word ORDER BY position ASC")
    LiveData<List<WORD>> sortByPositio();
    
  3. 在ViewModel中:

    public WordsViewModel(@NonNull Application application) {
            super(application);
            wordsDao = getDatabase(application).wordsDao();
            wordsLiveData = wordsDao.sortByPosition();
    }
    
    public LiveData<List<Word>> getWordsList() {
         return wordsLiveData;
    }
    
  4. 在活动中显示列表的地方:

    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();
              }
          }
       });
    }
    
  5. 在您的适配器中:

    void setWordsList(List<Word> wordsList) {
        this.wordsList = wordsList;
        notifyDataSetChanged();
    }
    
  6. 移动项目:

     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);
    }
    
  7. 我将在适配器中创建一个方法,在其中更新数据库中的位置:

     void setIndexInDatabase() {
        WordsDao wordsDao = getDatabase(context).wordsDao();
        for (Word word : wordsList) {
            word.setPosition(wordsList.indexOf(words));
            wordsDao.update(word);
        }
    
  8. 我会在您在onDestroy中显示列表的活动中调用该方法:

     @Override
     public void onDestroy() {
        super.onDestroy();
        adapter.setIndexInDatabase();
     }
    

因此,现在用户可以根据需要移动物品,当他完成并转到另一个活动时,新订单将保存在数据库中。

Ad.2。删除项目

  1. 首先,我将在您的适配器中创建一个arraylist,以在其中存储已删除项目的列表

    private ArrayList<String> deletedWords = new ArrayList<>();
    
  2. 然后我将创建删除和撤消方法:

    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

  3. 中删除了这个已删除的单词
  4. 我将创建一种方法来从数据库中删除arrayList中存储的所有单词:

    void deleteFromDatabase() {
        WordsDao wordsDao = getDatabase(context).wordsDao();
        if (deletedWords != null) {
            for (int i = 0; i < deletedWords.size(); i++) {
                wordsDao.deleteByWord(deletedWords.get(i));
            }
        }
    }
    
  5. 我会像以前一样在onDestroy中称呼它:

    @Override
    public void onDestroy() {
        super.onDestroy();
        adapter.setIndexInDatabase();
        adapter.deleteFromDatabase();
    }
    

我希望我不会忘记任何事情:)我在正在编写的应用程序中做了类似的事情,因此您也可以在github上进行检查。

我在Shift包中使用它:ShiftFragmentShiftAdapter