【问题标题】:Update onMove() changes in recycler view data to room database将回收站视图数据中的 onMove() 更改更新到房间数据库
【发布时间】:2019-09-20 19:17:32
【问题描述】:

根据一些文章,我正在尝试使用以下内容创建一个 android 应用程序:

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

recyclerview 中的功能:

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

推荐文章:

  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

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

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

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

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,这个问题是在一年前发布的,此后由于进行了很多更改,因此该回购已被私有化。接受的答案解决了我的问题。谢谢。

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


【解决方案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。移动物品

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

    因此,当您将新 Word 添加到数据库时,您可以这样做: wordsDao.insert(new Word(position, whateverYouNeedToAdd));

  2. 用你的话说道:

    @Query("SELECT * FROM word ORDER BY position ASC")
    LiveData<List<Word>> sortByPosition();
    
  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(word));
            wordsDao.update(word);
        }
    
  8. 我会在您显示列表的活动中调用该方法onDestroy

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

所以现在用户可以随心所欲地移动项目,当他完成并进入另一个活动时,新订单将保存在您的数据库中。

广告 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. 我会创建一个方法来从数据库中删除存储在我们的ArrayList 中的所有这些单词:

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

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

我希望我没有忘记任何事情 :) 我在我现在写的应用程序中做了类似的事情,所以你也可以在 github 上查看。

我在 Shift 包中使用它:ShiftFragmentShiftAdapter

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 2020-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多