Update onMove() changes in recycler view data to room database

Here is an easy approach for ordering items, used it with Room Database.

Entity Object (Model):

A column for order, type Integer:

@ColumnInfo(name = "word_order")
private Integer mOrder;

In Dao:

A query, that retrieve the largest order in column word_order:

@Query("SELECT MAX(word_order) FROM word_table")
int getLargestOrder();

And a query that update an entire word list:

@Update(onConflict = OnConflictStrategy.REPLACE)
void update(List<WordEntity> wordEntities);

Used MAX SQL command to get the largest number.

In Activity (when adding new word):

Query that method getLargestOrder() and added +1 to it, then create a new word.

In Activity (onCreate()):

Create ItemTouchHelper to move word items, use only UP & DOWN movement:

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP | ItemTouchHelper.DOWN) {

});

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

Override getMovementFlags inside it to specified the wanted directions:

        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
                    0);
        }

Override onMove inside it to swap item positions:

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

This method where items position get updated when moved.

  • First, get the positions for items from the adapter.

  • Then, if decrement, swap the items of Entities, by passing the current item position and the new one (for user eyes).

  • After that, get the order the current item and the next item, and swab them and set them with setter methods (for saving them in Room later).

  • On the other hand, do same for increment.

  • Finishing, by notify the adapter items has moved (for user eyes).

Override clearView to update Word Entities List:

        @Override
        public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            super.clearView(recyclerView, viewHolder);
            mWordViewModel.update(mWordEntities);
        }

This method called when user drop the item. It will be suitable to update and save the entities into the Room Database.

Override onSwiped:

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        }

This must be override by the system.

Attache ItemTouchHelper to the RecyclerView:

itemTouchHelper.attachToRecyclerView(mRecyclerView);

You can create many ItemTouchHelper and attach them to the RecyclerView as you need.

Beware that you need to order items in RecyclerView from Biggest number to Lowest number. Hope this help you :)


Hi:) I would do it like this:

Ad.1. Moving items

  1. I would add a "position" column to your Word object, and I would do it equal to your words.size(). That would make it "autoincrement" but not necessarily unique (which is important).

    So when you add new Word to your database, you do it like this: wordsDao.insert(new Word(position, whateverYouNeedToAdd));

  2. In your WordsDao:

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

    public WordsViewModel(@NonNull Application application) {
            super(application);
            wordsDao = getDatabase(application).wordsDao();
            wordsLiveData = wordsDao.sortByPosition();
    }
    
    public LiveData<List<Word>> getWordsList() {
         return wordsLiveData;
    }
    
  4. In activity where you display your list:

    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. In your adapter:

    void setWordsList(List<Word> wordsList) {
        this.wordsList = wordsList;
        notifyDataSetChanged();
    }
    
  6. To move the items:

     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. I would create a method in adapter in which you update positions in database:

     void setIndexInDatabase() {
        WordsDao wordsDao = getDatabase(context).wordsDao();
        for (Word word : wordsList) {
            word.setPosition(wordsList.indexOf(words));
            wordsDao.update(word);
        }
    
  8. I would call that method in the activity where you display your list in it's onDestroy:

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

So now the user can move the items as long as he wants, and when he finishes and goes to another activity the new order is saved in your database.

Ad.2. Delete Items

  1. First I would create an arraylist in your adapter to store a list of deleted items in it

    private ArrayList<String> deletedWords = new ArrayList<>();
    
  2. Then I would create delete and undo methods:

    public void deleteItem(int position) {
        word = wordsList.get(position);
        wordPosition = position;
        deletedWord = word.getWord();
        deletedWords.add(deletedWord);
        wordsList.remove(position);
        notifyItemRemoved(position);
        showUndoSnackbar();
    }
    

    I'm adding a word which you want to delete to this array we created.

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

    In undoDelete I'm removing this deleted word from our arrayList

  3. I would create a method to delete all this words stored in our arrayList from database:

    void deleteFromDatabase() {
        WordsDao wordsDao = getDatabase(context).wordsDao();
        if (deletedWords != null) {
            for (int i = 0; i < deletedWords.size(); i++) {
                wordsDao.deleteByWord(deletedWords.get(i));
            }
        }
    }
    
  4. I would call it in the onDestroy like before:

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

I hope I didn't forget about anything :) I did something like that in app I'm writing now, so you can check it out also on github.

I use it in Shift package: ShiftFragment and ShiftAdapter.