8

I have a two dimensional ArrayList of type String which is the input to the adapter class of the RecyclerView. The data in the list is taken from an SQLite database everytime when onResume() is called. I have implemented a drag and drop feature and an onMove() function that swaps the list elements successfully. However, I need to store the modified list before onResume() is called which will rewrite the list and fill it with old positions. My guess is to implement the rewriting functionality within the onStop() event. This is my main activity:

RecyclerView recyclerView;
RecyclerViewAdapter adapter;
RecyclerView.LayoutManager layoutManager;

ArrayList<ArrayList<String>> data;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    assert fab != null;
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent i = new Intent(MainActivity.this, ViewDbActivity.class);
            startActivity(i);
        }
    });

    recyclerView = (RecyclerView) findViewById(R.id.myRecyclerView);
    layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);
    RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();

    ItemTouchHelper item = new ItemTouchHelper(new ItemTouchHelper.Callback() {

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

        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            Collections.swap(data, viewHolder.getAdapterPosition(), target.getAdapterPosition());
            adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            return true;
        }

        @Override
        public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
            super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);
            Toast.makeText(MainActivity.this, "Moved", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            data.remove(viewHolder.getAdapterPosition());
            adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
        }
    });

    item.attachToRecyclerView(recyclerView);
    recyclerView.setItemAnimator(itemAnimator);
}

@Override
protected void onResume() {
    super.onResume();

    initialize();

    if(adapter == null) {
        Log.v(TAG, "Adapter = NULL. Initialized");
        setAdapter();
    }
    else {
        Log.v(TAG, "notify is called");
        adapter.updateData(data);
    }
}

public void initialize() {
    Database ourDB = new Database(this);
    ourDB.openDB();
    data = ourDB.getData();
    ourDB.closeDB();
}

public void setAdapter() {
    adapter = new RecyclerViewAdapter(data, this);
    recyclerView.setAdapter(adapter);
}

It is important to update the database as long as I know when to store the data. How can I solve this problem? Any suggestions?

!!!

ANSWER:

Final solution I have implemented:

@Override
public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
    super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);

    //preparing new position values
    long movedItem = Long.parseLong(data.get(fromPos).get(0));     // .get(fromPos).get(0) returns PRIMARY KEY from the list
    long draggedItem = Long.parseLong(data.get(toPos).get(0));

    // updating affected rows with new orders
    Database db = new Database(MainActivity.this);
        db.open();
        db.updateOrder(draggedItem, toPos);
        db.updateOrder(movedItem, fromPos);
        db.close();
}

And inside of DB class:

public void updateOrder(long rowID, int newPos) {
    ContentValues cv = new ContentValues();
    cv.put(KEY_ORDER, newPos);
    ourDatabase.update(DATABASE_TABLE, cv, KEY_ROWID + "=" + rowID, null);
}
Marat
  • 6,142
  • 6
  • 39
  • 67
  • I have a same question(My newest question) I did try this . But I have an error . What is a KEY_ROWID . Can you explain KEY_ROWID – ogi_plus Jan 01 '21 at 04:56

1 Answers1

8

You need a field in each DB row for storing the order. Then you need to implements those features:

  • On new row insert (when you insert a new object in database) you need to set the order field to the next int. You can get the current max value (with sql function MAX) and then simply do +1

  • When user move an item in RecyclerView, in method onMovedyou must update all other rows. You can use the fromPos and toPos for that. More on that below

  • When you fill your RecyclerView with data you need to order them by order field



Explanation of 2nd feature to be implemented: basically you need to update all rows with order between fromPos and toPos:

  • if user moved the item up (for example from position 4 to 2), you need to:

    1. get primary key field of current item (using position 4)
    2. change all rows between order 2 and order 4: so change 2 -> 3 and 3 -> 4
    3. Change current item order (using primary key of first point) to toPos: in this example change current item order to 2
  • if user moved the item down (for example from position 2 to 4) you need to:

    1. get primary key field of current item (using position 2)
    2. change all rows between order 2 and order 4: so change 4 -> 3 and 3 -> 2
    3. change current item order (using primary key of first point) to toPos: in this example change current item order to 4


Hope it helps a little

rsella
  • 337
  • 1
  • 7
  • Thanks for your answer. It's my fault I forgot to mention that I have a separate column for storing the order and, as you've said, when I create new item I increment the max line. I had the same idea as you have offered, but I'm concerned about performance of app. Because `onMoved` is called everytime the list items are swapped. If user drags items very fast it may result in app being frozen. What do you think about that? – Marat Jul 06 '16 at 14:03
  • So you just need to implements 2nd point of the list. You can either add methods in database class (for example `moveOrderUp(int from, int to)` and `moveOrderDown(int from, int to)` or do it directly in your `onMoved` method. For me, the first approach is better – rsella Jul 06 '16 at 14:06
  • My second idea was to use the ArrayList<> which is already storing fresh data. As you can see `onMove` swaps the moved items there. Now I thought that maybe I could try to delete all data from DB, sort list object and upload it to DB again. But I don't know where to implement that and how it will affect on performance. But for now your solution to simply rewrite affected rows in DB and sort it at start – Marat Jul 06 '16 at 14:13
  • Well, deleting and re-inserting all data to db every time a row is moved is a (really) bad idea. With my idea you update only the rows that actually need to be updated. I don't know how to use that idea with your ArrayList – rsella Jul 06 '16 at 14:17
  • Sorry, but I meant to make these operations after all item movements are done. For instance, in `onStop` :) sorry for misunderstanding. – Marat Jul 06 '16 at 14:18
  • Ok then. But how to get the row which corresponds to the dragged item? – Marat Jul 06 '16 at 14:28
  • I understand now, sorry for that. You can get the current dragged item in the `onMoved` method. Just search it in arraylist using the `fromPos` parameter (it's equal to the current item order in ArrayList) – rsella Jul 06 '16 at 14:37
  • Thanks! I will try this idea soon – Marat Jul 06 '16 at 14:57
  • @Marat Have you got the answer this because I am trying the same I need to save the new position at the `SQLite`. If you have done this please can you share code. – TheCoderGuy Feb 14 '19 at 10:03
  • @rsella I am trying the same changing the position of recyclerview with onItemMoved but it doesn't save at the `SQLite` – TheCoderGuy Feb 24 '19 at 21:04
  • @rsella I have written code for this but it did not work properly. Please check and help me https://stackoverflow.com/questions/62665019/android-sorting-recycleview-item-on-drop-not-working-properly – Dan Jun 30 '20 at 19:40