将Listview项目拖放到另一个项目中

时间:2015-12-20 14:45:02

标签: android android-listview drag-and-drop

我有一个包含不同项目类型的列表视图:标题,文件夹和文件,如下所示:

enter image description here

现在我想实现拖动文件项并将其放入文件夹项并获取源和目标位置。我不想像拖动排序列表视图库那样拖动时更改目标位置(重新排列)。

有什么建议可以开始吗?

2 个答案:

答案 0 :(得分:1)

ListView切换为RecyclerView会让事情变得更轻松。

您可以在Styling Android和整个代码here.

上找到整篇文章

此代码使用OnItemTouchListener来检测何时应该拖动项目。 ImageView上方有一个RecyclerView,其中的图片被移动到便宜的动画中。

OnItemTouckListenerDragController.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);
        }
    }

开始和结束拖动(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);
}

代码由Mark Allison on StylingAndroid.

编写

编辑:

  

但是我不知道如何在拖动结束时获取项目的位置

答案位于Styling Android上的part 7

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

如何禁用文件夹和标题项上的拖动?只需允许拖动文件项?

您可以使用多个ViewTypes(文件,文件夹和标题)来完成此操作。然后,您可以使用DragController中的getItemViewType来仅为文件启动移动。

答案 1 :(得分:1)

使用RecyclerViewItemTouchHelper.SimpleCallback

您可以在活动中将其设置为:

@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);
}

您可以使用ItemTouchHelper.SimpleCallback提供的项目拖动的默认功能。 以下类将演示如何更改文件夹的背景颜色。项目将被删除到该文件夹​​中。

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);
    }
}

将项目拖到文件夹上时,该项目下的文件夹背景将显示为浅绿色。 将项目放入文件夹时,其背景将为深绿色。