Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Following some articles, I am trying to create an android app with the following:

  • A recyclerview which fetches data from room database using live data.
  • Data structure is a list of custom objects with an attribute for ordering the data.

Features in recyclerview:

  • Drag and drop for reordering data
  • Swipe to delete
  • UNDO action for swipe to delete

Referred articles:

  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

My problem:
Data reordering is not updated in the room library.

Note:
I am using an attribute for data ordering

Please drop a comment if code of a particular file is required.I am not sure which code to post.

MainFragment.java (Code to reorder data, which is not working)

// 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);
like image 832
Abhimanyu Avatar asked May 02 '19 09:05

Abhimanyu


1 Answers

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

like image 52
MohammadL Avatar answered Oct 08 '22 04:10

MohammadL