Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag and Drop RecyclerView Which is Populated from SQLiteDatabase

Info:

I have a RecyclerView, and I populate it with the ArrayList dataList inside the ListAdapter class. I get the data for dataList from the SQLiteDatabase table: TABLE_USERdETAIL in the DbHelper class. I'm trying to implement drag and drop re-organisation into the RecyclerView by using the SimpleItemTouchHelperCallback class; however, although I am now able to move the RecyclerView elements, the list doesn't re-sort or move; I'm only able to temporarily drag the list elements over each other, as shown in the image below, but it's meant to be permanently slotting the list elements into whichever position it's held:

I expect the problem to be within the ListAdapter class, inside the method onItemMove, but I don't know how to solve my issue.

Question: How do I make this drag and drop feature actually re-organise the RecyclerView data list?

Update 5: I have updated my current code for further assistance.

ListAdapter Class

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ListViewHolder> {

    Context context;
    List<UserData> dataList = new ArrayList<>();
    LayoutInflater inflater;
    Listener listener;
    DbHelper dbHelper;



    public interface Listener {
        void nameToChnge(String name);
    }

    public ListAdapter(Context context, List<UserData> dataList1) {
        this.context = context;
        this.dataList = dataList1;
        //this.listener= (Listener) context;
        inflater = LayoutInflater.from(context);
    }


    @Override
    public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View convertView = inflater.inflate(R.layout.recylerview_one, parent, false);
        ListViewHolder viewHolder = new ListViewHolder(convertView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ListViewHolder holder, final int position) {
        holder.tv_name.setText(dataList.get(position).name);
        holder.tv_quantity.setText(dataList.get(position).quantity);
        holder.tv_description.setText(dataList.get(position).description + " ");

        if(dataList.get(position).description.isEmpty()) {
            holder.tv_description.setVisibility(View.GONE);
        }

        holder.relLayout.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                String s = dataList.get(position).id;
                Integer stringo = Integer.parseInt(s);
                Intent intent = new Intent(context, ItemEditActivity.class);
                intent.putExtra("ItemNumber", stringo);
                context.startActivity(intent);
            }
        });
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    class ListViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
        TextView tv_name, tv_quantity, tv_description;
        RelativeLayout relLayout;

        public ListViewHolder(View itemView) {
            super(itemView);
            tv_name = (TextView) itemView.findViewById(R.id.nameDisplay);
            tv_quantity = (TextView) itemView.findViewById(R.id.quantityDisplay);
            tv_description = (TextView) itemView.findViewById(R.id.descriptionDisplay);
            relLayout = (RelativeLayout) itemView.findViewById(R.id.relLayout);
        }
        @Override
        public void onItemSelected() {
            Log.d("ListViewHolder", "item selected");
        }

        @Override
        public void onItemClear() {
            Log.d("ListViewHolder", "item clear");
            for (int count = 0; count < dataList.size(); count++) {
                UserData u = dataList.get(count);
                u.setSort(count);
                dbHelper.updateUserData(u);
            }
            notifyDataSetChanged();
        }
    }

    public void onItemDismiss(int position) {
        dataList.remove(position);
        //dbHelper = DbHelper.getInstance(context);
        //dbHelper.deleteRowItem(position + 1);
        notifyItemRemoved(position);
    }

    //Collections.swap(dataList, fromPosition, toPosition);
    //notifyItemMoved(fromPosition, toPosition);


    public boolean onItemMove(int fromPosition, int toPosition) {
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(dataList, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(dataList, i, i - 1);
            }
        }
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }
}

SimpleItemTouchHelperCallback Class

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
    private final ListAdapter  mAdapter;


    public SimpleItemTouchHelperCallback(ListAdapter adapter) {
        mAdapter = adapter;
    }

    @Override
    public boolean isLongPressDragEnabled() {return true; }

    @Override
    public boolean isItemViewSwipeEnabled() { return false; }

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

    @Override
    public boolean onMove(RecyclerView recyclerView,
                          RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder target) {
        mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

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

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        // We only want the active item to change
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof ItemTouchHelperViewHolder) {
                // Let the view holder know that this item is being moved or dragged
                Log.i("ADAPTER", "----DRAGGING----");
                ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
                itemViewHolder.onItemSelected();
            }
        }

        super.onSelectedChanged(viewHolder, actionState);
    }
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        if (viewHolder instanceof ItemTouchHelperViewHolder) {
            // Tell the view holder it's time to restore the idle state
            ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
            itemViewHolder.onItemClear();
        }
    }
}

DbHelper Class

public class DbHelper extends SQLiteOpenHelper {

   private static final String DATABASE_NAME = "UserDatabase";
    private static final int DATABASE_VERSION = 2;
    private static DbHelper mDbHelper;

    public static String TABLE_USERdETAIL = "userdetail";

    private static final String _ID = "_id";
    private static final String SORT_ID = "sort_id";
    private static final String NAME = "name";
    private static final String QUANTITY = "quantity";
    private static final String WEIGHT = "weight";
    private static final String WEIGHTTOTAL = "weighttotal";
    private static final String VALUE = "value";
    private static final String VALUETOTAL = "valuetotal";
    private static final String DESCRIPTION = "description";

    public static synchronized DbHelper getInstance(Context context) {
        if (mDbHelper == null) {
            mDbHelper = new DbHelper(context.getApplicationContext());
        }
        return mDbHelper;
    }

    public DbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }


    @Override
    public void onCreate(SQLiteDatabase db) {

        String CREATE_USERDETAIL_TABLE = "CREATE TABLE " + TABLE_USERdETAIL +
                "(" +
                _ID + " INTEGER PRIMARY KEY , " +
                SORT_ID + " INTEGER," +
                NAME + " TEXT," +
                QUANTITY + " INTEGER," +
                WEIGHT + " INTEGER," +
                WEIGHTTOTAL + " INTEGER," +
                VALUE + " INTEGER," +
                VALUETOTAL + " INTEGER," +
                DESCRIPTION + " TEXT" +
                ")";

        db.execSQL(CREATE_USERDETAIL_TABLE);
    }



    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion != newVersion) {
            db.execSQL("ALTER TABLE " + TABLE_USERdETAIL + " ADD COLUMN " + WEIGHT + " INTEGER DEFAULT 0");
            db.execSQL("ALTER TABLE " + TABLE_USERdETAIL + " ADD COLUMN " + WEIGHTTOTAL + " INTEGER DEFAULT 0");
            db.execSQL("ALTER TABLE " + TABLE_USERdETAIL + " ADD COLUMN " + VALUE + " INTEGER DEFAULT 0");
            db.execSQL("ALTER TABLE " + TABLE_USERdETAIL + " ADD COLUMN " + VALUETOTAL + " INTEGER DEFAULT 0");
        }
    }

    /**
   Insert a  user detail into database
   */
    public void insertUserDetail(UserData userData) {

        SQLiteDatabase db = getWritableDatabase();

        db.beginTransaction();

        try {
            ContentValues values = new ContentValues();
            values.put(SORT_ID, userData.sort_id);
            values.put(NAME, userData.name);
            values.put(QUANTITY, userData.quantity);
            values.put(WEIGHT, userData.weight);
            values.put(WEIGHTTOTAL, userData.weighttotal);
            values.put(VALUE, userData.value);
            values.put(VALUETOTAL, userData.valuetotal);
            values.put(DESCRIPTION, userData.description);


            db.insertOrThrow(TABLE_USERdETAIL, null, values);
            db.setTransactionSuccessful();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            db.endTransaction();
        }


    }

    public void updateUserDetail(int id, String v1, String v2, String v3, String v4, String v5, String v6, String v7){
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(NAME, v1);
        values.put(QUANTITY, v2);
        values.put(DESCRIPTION, v3);
        values.put(WEIGHT, v4);
        values.put(WEIGHTTOTAL, v5);
        values.put(VALUE, v6);
        values.put(VALUETOTAL, v7);
        db.update(TABLE_USERdETAIL, values, "_id="+id, null);
    }

   /**
   fetch all data from UserTable
    */
    public List<UserData> getAllUser() {
        List<UserData> usersdetail = new ArrayList<>();

        String USER_DETAIL_SELECT_QUERY = "SELECT * FROM " + TABLE_USERdETAIL + " ORDER BY " + SORT_ID + " COLLATE NOCASE;";

        SQLiteDatabase db = getReadableDatabase();
        Cursor cursor = db.rawQuery(USER_DETAIL_SELECT_QUERY, null);

        try {
            if (cursor.moveToFirst()) {
                do {
                    UserData userData = new UserData();
                    userData.id = cursor.getString(cursor.getColumnIndex(_ID));
                    userData.sort_id = cursor.getInt(cursor.getColumnIndex(SORT_ID));
                    userData.name = cursor.getString(cursor.getColumnIndex(NAME));
                    userData.quantity = "Quantity: " + cursor.getString(cursor.getColumnIndex(QUANTITY));
                    userData.weight = cursor.getString(cursor.getColumnIndex(WEIGHT));
                    userData.weighttotal = cursor.getString(cursor.getColumnIndex(WEIGHTTOTAL));
                    userData.value = cursor.getString(cursor.getColumnIndex(VALUE));
                    userData.valuetotal = cursor.getString(cursor.getColumnIndex(VALUETOTAL));
                    userData.description = cursor.getString(cursor.getColumnIndex(DESCRIPTION));

                    usersdetail.add(userData);
                } while (cursor.moveToNext());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null && !cursor.isClosed()) {
                cursor.close();
            }
        }
        return usersdetail;

    }

    /**
   Delete single row from UserTable
     */
    void deleteRow(String name) {
        SQLiteDatabase db = getWritableDatabase();
        try {
            db.beginTransaction();
            db.execSQL("delete from " + TABLE_USERdETAIL + " where name ='" + name + "'");
            db.setTransactionSuccessful();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            db.endTransaction();
        }
    }

    void deleteRowItem(int id) {
        SQLiteDatabase db = getWritableDatabase();
        try {
            db.beginTransaction();
            db.execSQL("delete from " + TABLE_USERdETAIL + " where _id ='" + id + "'");
            db.setTransactionSuccessful();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            db.endTransaction();
        }
    }

    public UserData getSingleUserDetail(String userId) {
        SQLiteDatabase db = this.getWritableDatabase();
        UserData userData = null;

        //Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_USERdETAIL, null);

        // this is the code to order the RecyclerView by _ID:
        Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_USERdETAIL + " WHERE " + _ID + "= ?", new String[]{userId});
        try {
            while (cursor.moveToNext()) {
                userData = new UserData();
                userData.name = cursor.getString(cursor.getColumnIndex(NAME));
                userData.quantity = cursor.getString(cursor.getColumnIndex(QUANTITY));
                userData.weight = cursor.getString(cursor.getColumnIndex(WEIGHT));
                userData.weighttotal = cursor.getString(cursor.getColumnIndex(WEIGHTTOTAL));
                userData.value = cursor.getString(cursor.getColumnIndex(VALUE));
                userData.valuetotal = cursor.getString(cursor.getColumnIndex(VALUETOTAL));
                userData.description = cursor.getString(cursor.getColumnIndex(DESCRIPTION));
            }
            cursor.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null && !cursor.isClosed()) {
                cursor.close();
            }
        }
        return userData;
    }

    public int getTotalWeight() {
        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cur = db.rawQuery("SELECT SUM(WEIGHTTOTAL) FROM userdetail", null);
        if(cur.moveToFirst())
        {
            return cur.getInt(0);
        }
        cur.close();
        return getTotalWeight();
    }

    public int getTotalQuantity() {
        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cur = db.rawQuery("SELECT SUM(QUANTITY) FROM userdetail", null);
        if(cur.moveToFirst())
        {
            return cur.getInt(0);
        }
        cur.close();
        return getTotalQuantity();
    }

    public int getTotalValue() {
        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cur = db.rawQuery("SELECT SUM(VALUETOTAL) FROM userdetail", null);
        if(cur.moveToFirst())
        {
            return cur.getInt(0);
        }
        cur.close();
        return getTotalValue();
    }

    public int getMaxColumnData() {
        SQLiteDatabase db = this.getWritableDatabase();
        final SQLiteStatement stmt = db
                .compileStatement("SELECT MAX(SORT_ID) FROM " + TABLE_USERdETAIL);

        return (int) stmt.simpleQueryForLong();
    }

    public void updateUserData(UserData userData) {

        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();

        values.put(NAME, userData.getName());
        values.put(QUANTITY, userData.getQuantity());
        values.put(DESCRIPTION, userData.getDescription());
        values.put(WEIGHT, userData.getWeight());
        values.put(WEIGHTTOTAL, userData.getWeighttotal());
        values.put(VALUE, userData.getValue());
        values.put(VALUETOTAL, userData.getValuetotal());
        values.put(SORT_ID, userData.getSort());

        Log.i("DBhelper", "USER UPDATED = " + userData.getName());

        db.update(TABLE_USERdETAIL, values, _ID + "=?", new String[]{String.valueOf(userData.getId())});

    }
}

UserData

import java.io.Serializable;

public class UserData implements Serializable {
    String id, name, quantity, weight, weighttotal, value, valuetotal, description;
    int sort_id;

    public UserData() {
        super();
    }

    public void setId(String id){
        this.id= id;
    }
    public String getId(){
        return id;
    }
    public void setSort(int sort){
        this.sort_id= sort;
    }
    public int getSort(){
        return sort_id;
    }

    public void setName(String name){
        this.name= name;
    }
    public String getName(){
        return name;
    }
    public void setDescription(String description){
        this.description= description;
    }
    public String getDescription(){
        return description;
    }
    public void setQuantity(String quantity){
        this.quantity= quantity;
    }
    public String getQuantity(){
        return quantity;
    }
    public void setWeight(String weight){
        this.weight= weight;
    }
    public String getWeight(){
        return weight;
    }
    public void setWeighttotal(String weighttotal){
        this.weighttotal= weighttotal;
    }
    public String getWeighttotal(){
        return weighttotal;
    }
    public void setValue(String value){
        this.value= value;
    }
    public String getValue(){
        return value;
    }
    public void setValuetotal(String valuetotal){
        this.quantity= valuetotal;
    }
    public String getValuetotal(){
        return quantity;
    }
}

ItemTouchHelperViewHolder Class

public interface ItemTouchHelperViewHolder {
    void onItemSelected();
    void onItemClear();
}

MainActivity

public class MainActivity extends AppCompatActivity implements ListAdapter.Listener {


    RecyclerView recyclerView;
    DbHelper dbHelper;
    ListAdapter adapter;
    private ItemTouchHelper mItemTouchHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        dbHelper = DbHelper.getInstance(getApplicationContext());

        recyclerView = (RecyclerView) findViewById(R.id.rv_contactlist);
        adapter = new ListAdapter(this, dbHelper.getAllUser());
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        ItemTouchHelper.Callback callback =
                new SimpleItemTouchHelperCallback(adapter);
        mItemTouchHelper = new ItemTouchHelper(callback);
        mItemTouchHelper.attachToRecyclerView(recyclerView);

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return super.onCreateOptionsMenu(menu);


    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.about_task:
                Intent intentChangeActivity2 = new Intent(MainActivity.this, AboutActivity.class);
                startActivity(intentChangeActivity2);
                return true;
            case R.id.encumbrance_task:
                Intent intentChangeActivity = new Intent(MainActivity.this, EncumbranceActivity.class);
                startActivity(intentChangeActivity);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        dbHelper = DbHelper.getInstance(getApplicationContext());

        recyclerView = (RecyclerView) findViewById(R.id.rv_contactlist);
        adapter = new ListAdapter(this, dbHelper.getAllUser());
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

    }

    @Override
    public void nameToChnge(String name) {
        dbHelper.deleteRow(name);

        adapter = new ListAdapter(this, dbHelper.getAllUser());
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }

}

Error log:

8-26 11:20:49.443 23533-23533/name.appE/AndroidRuntime: FATAL EXCEPTION: main
                                                                       Process: name.app, PID: 23533
                                                                       java.lang.NullPointerException: Attempt to invoke virtual method 'void name.app.DbHelper.updateUserData(name.app.UserData)' on a null object reference
                                                                           at name.app.ListAdapter$ListViewHolder.onItemClear(ListAdapter.java:95)
                                                                           at name.app.SimpleItemTouchHelperCallback.clearView(SimpleItemTouchHelperCallback.java:63)
                                                                           at android.support.v7.widget.helper.ItemTouchHelper$3.onAnimationEnd(ItemTouchHelper.java:619)
                                                                           at android.support.v4.animation.HoneycombMr1AnimatorCompatProvider$AnimatorListenerCompatWrapper.onAnimationEnd(HoneycombMr1AnimatorCompatProvider.java:115)
                                                                           at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1149)
                                                                           at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1309)
                                                                           at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:146)
                                                                           at android.animation.AnimationHandler.-wrap2(AnimationHandler.java)
                                                                           at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:54)
                                                                           at android.view.Choreographer$CallbackRecord.run(Choreographer.java:869)
                                                                           at android.view.Choreographer.doCallbacks(Choreographer.java:683)
                                                                           at android.view.Choreographer.doFrame(Choreographer.java:616)
                                                                           at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
                                                                           at android.os.Handler.handleCallback(Handler.java:751)
                                                                           at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                           at android.os.Looper.loop(Looper.java:154)
                                                                           at android.app.ActivityThread.main(ActivityThread.java:6123)
                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                           at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
like image 639
Tom James Avatar asked Jan 25 '17 17:01

Tom James


People also ask

How do I drag and drop items in RecyclerView Android?

Drag and Drop can be added in a RecyclerView using the ItemTouchHelper utility class. Following are the important methods in the ItemTouchHelper. Callback interface which needs to be implemented: isLongPressDragEnabled - return true here to enable long press on the RecyclerView rows for drag and drop.


1 Answers

I'm not sure that catching your problem but what i noticed (hope this helps): in your onItemMove you are refactoring dataList by swapping items but adapter notifies only about general change.

guess the simplest way to move the item (and save animation) is to remove it and add again:

UserData item = dataList.remove(fromPosition);
dataList.add(i, toPossition);
notifyItemMoved(fromPosition, toPosition);
like image 196
Siarhei Avatar answered Oct 25 '22 08:10

Siarhei