【问题标题】:Drag drop Listview item into another item将 Listview 项目拖放到另一个项目中
【发布时间】:2016-03-26 17:31:51
【问题描述】:

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

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

有什么建议可以开始吗?

【问题讨论】:

  • 是否有可能使用支持库中的RecyclerViewItemTouchHelper
  • 如果我改成RecyclerView,会更容易吗?
  • 应该是。我没有使用它,但ItemTouchHelper 用于添加拖放和滑动以取消对RecyclerView 的支持。查看ItemTouchHelper 文档,看看是否可以在线找到示例。我不明白为什么你的问题没有得到更多关注,有很多图书馆可以做这种事情。
  • 很好,例子,对我有帮助

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


【解决方案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);
    }
}

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

【讨论】:

    【解决方案2】:

    将您的ListView 切换为RecyclerView 将使事情变得更容易。

    你可以在Styling Android找到整篇文章和完整代码here.

    此代码使用OnItemTouchListener 来检测何时应拖动项目。在RecyclerView 上方有一个ImageView,其中包含正在移动的项目图像以廉价地对其进行动画处理。

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

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

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

    您可以通过使用多个视图类型(文件、文件夹和标题)来做到这一点。然后您可以在DragController 中使用getItemViewType 来开始仅针对文件的移动。

    【讨论】:

    • 谢谢。我尝试过这个。但是我不知道拖动结束时如何获取项目的位置。以及如何禁用 FolderHeader 项目的拖动?只允许拖动File 项目?
    猜你喜欢
    • 1970-01-01
    • 2013-01-05
    • 1970-01-01
    • 2016-11-04
    • 1970-01-01
    • 2016-04-02
    • 2014-09-22
    • 1970-01-01
    • 2015-06-04
    相关资源
    最近更新 更多