Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag drop Listview item into another item

I have a list view with different item types: header, folder and file like this:

enter image description here

Now I 'd like to implement drag file item and drop it into folder item and get the source and target position.I don't want to change the target position (rearrange) while dragging like some drag sort list view libraries.

Is there any suggestion to start with?

like image 305
ductran Avatar asked Dec 20 '15 14:12

ductran


2 Answers

Switching your ListView to RecyclerView will make things a lot easier.

You can find the whole article on Styling Android and the whole code here.

This code uses OnItemTouchListener to detect when an item should be dragged. There is an ImageView above the RecyclerView with an image of the item being moved to cheaply animate it.

The OnItemTouckListener (DragController.java):

public class DragController implements RecyclerView.OnItemTouchListener {
    private RecyclerView recyclerView;
    private ImageView overlay;
    private final GestureDetectorCompat gestureDetector;

    private boolean isDragging = false;

    public DragController(RecyclerView recyclerView, ImageView overlay) {
        this.recyclerView = recyclerView;
        this.overlay = overlay;
        GestureDetector.SimpleOnGestureListener longClickGestureListener = new GestureDetector.SimpleOnGestureListener() {
            @Override
            public void onLongPress(MotionEvent e) {
                super.onLongPress(e);
                isDragging = true;
                dragStart(e.getX(), e.getY());
            }
        };
        this.gestureDetector = new GestureDetectorCompat(recyclerView.getContext(), longClickGestureListener);
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        if (isDragging) {
            return true;
        }
        gestureDetector.onTouchEvent(e);
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        int x = (int) e.getX();
        int y = (int) e.getY();
        View view = recyclerView.findChildViewUnder(x, y);
        if (e.getAction() == MotionEvent.ACTION_UP) {
            dragEnd(view);
            isDragging = false;
        } else {
            drag(y, view);
        }
    }

Starting and ending the drag (DragController.java):

private boolean isFirst = true;
private static final int ANIMATION_DURATION = 100;
private int draggingItem = -1;
private float startY = 0f;
private Rect startBounds = null;

private void dragStart(float x, float y) {
    View draggingView = recyclerView.findChildViewUnder(x, y);
    View first = recyclerView.getChildAt(0);
    isFirst = draggingView == first;
    startY = (y - draggingView.getTop());
    paintViewToOverlay(draggingView);
    overlay.setTranslationY(y - startY);
    draggingView.setVisibility(View.INVISIBLE);
    draggingItem = recyclerView.indexOfChild(draggingView);
    startBounds = new Rect(draggingView.getLeft(), draggingView.getTop(), draggingView.getRight(), draggingView.getBottom());
}

private void drag(int y, View view) {
    overlay.setTranslationY(y - startY);
}

private void dragEnd(View view) {
    overlay.setImageBitmap(null);
    view.setVisibility(View.VISIBLE);
    view.setTranslationY(overlay.getTranslationY() - view.getTop());
    view.animate().translationY(0f).setDuration(ANIMATION_DURATION).start();
}

private void paintViewToOverlay(View view) {
    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);
    overlay.setImageBitmap(bitmap);
    overlay.setTop(0);
}

The code is written by Mark Allison on StylingAndroid.

Edit:

But I don't know how to get the position of item when dragging is end

The answer is located in part 7 on Styling Android.

View view = recyclerView.findChildViewUnder(0, y);

And how can I disable drag on Folder and Header item? Just allow dragging File item?

You can do this by using multiple ViewTypes (file, folder & header). Then you can use getItemViewType in DragController to start the movement only for files.

like image 157
LordRaydenMK Avatar answered Oct 28 '22 20:10

LordRaydenMK


Use RecyclerView and ItemTouchHelper.SimpleCallback.

You can setup it like that in your activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_list); // Your layout with RecyclerView

    RecyclerView itemRecyclerView = findViewById(R.id.itemRecyclerView);

    LinearLayoutManager itemLayoutManager = new LinearLayoutManager(this);
    itemRecyclerView.setLayoutManager(itemLayoutManager);

    itemAdapter = new ItemAdapter(); // Your adapter which extends RecyclerView.Adapter

    itemRecyclerView.setAdapter(itemAdapter);
    itemRecyclerView.setHasFixedSize(true);

    itemDragAndDropCallback = new ItemDragAndDropCallback(this, itemRecyclerView);
    // Your class which extends ItemTouchHelper.SimpleCallback
    // It will be shown in the next code sample

    new ItemTouchHelper(itemDragAndDropCallback)
        .attachToRecyclerView(itemRecyclerView);
}

You can use default functionality for item dragging provided by ItemTouchHelper.SimpleCallback. The following class will demonstrate changing the background color of a folder. An item will be dropped into that folder.

class ItemDragAndDropCallback extends ItemTouchHelper.SimpleCallback {

    ItemDragAndDropCallback() {
        // Choose drag and swipe directions
        // Up and down is chosen for dragging
        // Right and left is chosen for swiping
        super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        // You can reorder items here
        // Do nothing in your case
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        // You can react for swiping items here
        // Do nothing in your case
    }

    // An item will be dropped into this folder
    private View folder;

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

        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {

            // Here you are notified that the drag operation began

            if (folder != null) {
                folder.setBackgroundResource(0); // Clear former folder background
            }
        } else if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {

            // Here you are notified that the last operation ended

            if (folder != null) {
                // Set folder background to a color indicating
                // that an item was dropped into it
                folder.setBackgroundColor(
                    ContextCompat.getColor(
                        recyclerView.getContext(), android.R.color.holo_green_dark
                    )
                );
            }
        }
    }

    @Override
    public void onChildDraw(
        Canvas c,
        RecyclerView recyclerView,
        RecyclerView.ViewHolder viewHolder,
        float dX,
        float dY,
        int actionState,
        boolean isCurrentlyActive
    ) {
        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && isCurrentlyActive) {

            // Here you are notified that the drag operation is in progress

            if (folder != null) {
                folder.setBackgroundResource(0); // Clear former folder background
            }

            float itemActualPosition = viewHolder.itemView.getTop() + dY;

            // Find folder under dragged item
            for (int i = 0; i < recyclerView.getChildCount(); i++) {
                folder = recyclerView.getChildAt(i);

                // Exclude dragged item from detection
                if (!folder.equals(viewHolder.itemView)) {

                    // Accept folder which encloses item position
                    if (folder.getTop() < itemActualPosition && itemActualPosition < folder.getBottom()) {

                        // Set folder background to a color indicating
                        // that an item will be dropped into it upon release
                        folder.setBackgroundColor(
                            ContextCompat.getColor(
                                recyclerView.getContext(), android.R.color.holo_green_light
                            )
                        );
                        break;
                    }
                }
            }
        }
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
}

When you drag an item over folders then the folder's background under the item will be light green. When you drop the item into a folder then its background will be dark green.

like image 22
Rafał Spryszyński Avatar answered Oct 28 '22 20:10

Rafał Spryszyński