2
votes

I'm trying to develop a simple but custom drag and drop feature of recyclerview items. The idea is to drag a child to a specific view, represented by a FrameLayout, that is not a child of recyclerview. When a child is dropped onto it it will be deleted otherwise it will simply return to its original position. I can easily achieve this behaviour without any animation effect, but my purpose is to have smooth animation on the UP dragevent and on the restoration of the element. Now whenever a child is dragged, I create a DragShadow of it and I delete the child in the dataset and notify the removal with adapter.notifyItemRemoved(position). The last one allows me to have simple remove animation provided by Recyclerview itself. The Code will explain the mechanism better than words:

public boolean dragNdrop(View arg1, int position) {
    // ------------------------
    final RecyclerAdapter adapter= ListItemFragment.getAdapter();

   /*Keep track of the position and the object for restoring purpose*/
    ListItemFragment.indexAbruptedRemoved=position;
    ListItemFragment.itemAbruptedRemoved=adapter.getItem(position);

    ClipData clipData = ClipData.newPlainText("", "");
    View.DragShadowBuilder dsb = new View.DragShadowBuilder(arg1);
    arg1.startDrag(clipData, dsb, arg1, 0);


    adapter.remove(position);
    adapter.notifyItemRemoved(position);

    //--------------------
}

getter method for the adapter:

protected static RecyclerAdapter getAdapter() {
        return (RecyclerAdapter) recList.getAdapter();
}

dragNdrop is called by a drag listener in the custom adapter:

public void onBindViewHolder(MyHolder myHolder, int position) {
   //----------------------------------

    myHolder.rowCard.setLongClickable(true);
    myHolder.rowCard.setOnClickListener(new MyClickListener.ContainerListener(context,
            myHolder.getAdapterPosition(), rowObject, myHolder.imagePreView));
    myHolder.rowCard.setOnLongClickListener(new MyClickListener.DragListener(context,
            myHolder.getAdapterPosition(), myHolder.rowCard));

 //----------------------------------
}

LongCLickListener:

static class DragListener implements View.OnLongClickListener {
    private Context context;
    private int position;
    private View dragged;

    DragListener(Context context, int position, View dragged) {
        this.context = context;
        this.position = position;
        this.dragged = dragged;
    }

    @Override
    public boolean onLongClick(View v) {
        return ((MainActivity) context).dragNdrop(dragged, position);
    }
}

The FrameLayout called trashPanel has the following code:

 trashPanel.setOnDragListener(new View.OnDragListener() {


        @Override
        public boolean onDrag(View view, DragEvent dragEvent) {
            int dragAction = dragEvent.getAction();
            View dragView = (View) dragEvent.getLocalState();//this
            //is properly the object we 've passed with startdrag() 

            switch (dragAction) {

                case DragEvent.ACTION_DRAG_EXITED:
                    imageView.setImageDrawable(getResources().getDrawable(R.drawable.bowl));
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
                    imageView.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.waste));
                    break;
                case DragEvent.ACTION_DRAG_ENDED:
                    ended(view, dragEvent);
                    break;
                case DragEvent.ACTION_DROP:
                    Log.i(TAG,"HALOOOOO DROPPED");
                    drop();
                    break;

            }

            return true;

        }//[m] end on drag


        private void drop() {
            String itemToRemove = itemAbruptedRemoved.getSessionName();


            Toast.makeText(getActivity(), "DELETED: " + itemToRemove+
                            " list length:"+items.size()+" adapter length:"+mAdapter.size(),
                    Toast.LENGTH_SHORT).show();

            /**
             * HAPTIC FEEDBACK
             */
            Vibrator v = (Vibrator) getActivity().getSystemService(Service.VIBRATOR_SERVICE);
            long pattern[] = {25, 25, 50};
            v.vibrate(pattern, -1);// -1 to not repeat
        }

        private void ended(View view, DragEvent dragEvent) {
            if ( dropEventNotHandled(dragEvent) ) {

                mAdapter.insert(itemAbruptedRemoved, indexAbruptedRemoved);
                mAdapter.notifyItemInserted(indexAbruptedRemoved);
      //********---------->mAdapter.notifyDataSetChanged();
                Log.i(TAG,"AGGIUNTO");
            }
            //change the image inside trash_panel
            imageView.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.bowl));

            //due to implementations detail it must be done in this way(otherwise mutithread exception is throwned)
            view.post(new Runnable() {
                public void run() {

                    /**
                     * Set animation for fabNew Button and trash_panel
                     * in order to make them smoothly disappear
                     * and place them in their original positions
                     *
                     */

                   ------------------------------------

                }
            });

        }


        private boolean dropEventNotHandled(DragEvent dragEvent) {
            return !dragEvent.getResult();
        }


    });

The problem is that sometimes when I start to drag an item, I obtain the correct DragShadow, but the item removed is wrong. In the code i higlited with ***----> the call to notifyDataSetChanged(), because if i call it, things work properly, but without animation.

I know there are utility classes such as ItemTouchHelper.Callback that should be used to manipulate the movement and animation of recyclerlist children, but I can't figure out how to manage to make them do what I want to do. I saw a couple of methods that could be used to achieve this, such as onChildDrawOver and onChildDraw, but i still don't know how to use them in order to intercept dragevent.DROP . I also know the existence of the interface for the LLM called ItemTouchHelper.ViewDropHandler that has the abstract method prepareForDrop, but I still don't know how to use it in a proper way.

Thanks in advance to everyone who will help me!

1

1 Answers

0
votes

Take a look at ItemTouchHelper. It will allow you customize your drag drawing. See Support7Demos for an example implementation.