You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android拖拽功能异常:仅阴影可动无法放置,求技术协助

Fixing Android Drag-and-Drop: Only ACTION_DRAG_ENDED Triggered, ImageView Won't Drop

Let's break down and fix your drag-and-drop issues. I've identified the key problems in your code and adjusted it to work across API 16+ while handling all drag events correctly.

Key Issues in Your Original Code

  1. Drag listener attached to the ImageView itself: When you drag the ImageView away from its original position, it can't receive subsequent drag events (like ACTION_DRAG_ENTERED, ACTION_DROP) because it's no longer under the touch point. The parent container (e.g., your RelativeLayout) should be the drag listener instead.
  2. ACTION_DRAG_STARTED needs to return true: If you don't return true here, the system assumes the listener won't handle any further drag events, leaving only ACTION_DRAG_ENDED to fire.
  3. ACTION_DROP wasn't properly handling position updates: Your code didn't update the ImageView's layout params in the drop event, and wasn't returning true to confirm a successful drop.

Corrected Implementation

Step 1: Update Your Layout

Ensure your ImageView is wrapped in a parent container (we'll use this container as the drag listener):

<RelativeLayout
    android:id="@+id/drag_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/draggable_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/your_image_resource"
        android:tag="draggable_image"/>
</RelativeLayout>

Step 2: Initialize Views and Set Listeners

In your Activity/Fragment:

RelativeLayout dragContainer = findViewById(R.id.drag_container);
ImageView draggableImg = findViewById(R.id.draggable_img);

// Set long click listener to start drag
draggableImg.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        // Create clip data for drag operation
        ClipData.Item item = new ClipData.Item((CharSequence) v.getTag());
        String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
        ClipData dragData = new ClipData(v.getTag().toString(), mimeTypes, item);

        // Custom drag shadow to match the ImageView's size and position
        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(draggableImg) {
            @Override
            public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
                // Match shadow size to the ImageView
                shadowSize.set(v.getWidth(), v.getHeight());
                // Set touch point to the center of the shadow (matches finger position)
                shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
            }
        };

        // Handle API version differences
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // API 24+: Use startDragAndDrop (non-deprecated)
            v.startDragAndDrop(dragData, shadowBuilder, null, 0);
        } else {
            // API 16-23: Use startDrag (deprecated but fully compatible)
            v.startDrag(dragData, shadowBuilder, null, 0);
        }
        return true;
    }
});

// Set drag listener on the parent container
dragContainer.setOnDragListener(new View.OnDragListener() {
    private RelativeLayout.LayoutParams imgLayoutParams;
    private final String LOG_TAG = "DragDebug";

    @Override
    public boolean onDrag(View v, DragEvent event) {
        switch (event.getAction()) {
            case DragEvent.ACTION_DRAG_STARTED:
                Log.d(LOG_TAG, "Action is DragEvent.ACTION_DRAG_STARTED");
                // Must return true to receive subsequent drag events
                return true;

            case DragEvent.ACTION_DRAG_ENTERED:
                Log.d(LOG_TAG, "Action is DragEvent.ACTION_DRAG_ENTERED");
                break;

            case DragEvent.ACTION_DRAG_LOCATION:
                Log.d(LOG_TAG, "Action is DragEvent.ACTION_DRAG_LOCATION");
                // Update ImageView position in real-time for smooth dragging
                int xPos = (int) event.getX() - draggableImg.getWidth() / 2;
                int yPos = (int) event.getY() - draggableImg.getHeight() / 2;
                
                imgLayoutParams = (RelativeLayout.LayoutParams) draggableImg.getLayoutParams();
                imgLayoutParams.leftMargin = xPos;
                imgLayoutParams.topMargin = yPos;
                draggableImg.setLayoutParams(imgLayoutParams);
                break;

            case DragEvent.ACTION_DRAG_EXITED:
                Log.d(LOG_TAG, "Action is DragEvent.ACTION_DRAG_EXITED");
                break;

            case DragEvent.ACTION_DROP:
                Log.d(LOG_TAG, "ACTION_DROP event");
                // Finalize the ImageView's position on drop
                int dropX = (int) event.getX() - draggableImg.getWidth() / 2;
                int dropY = (int) event.getY() - draggableImg.getHeight() / 2;
                
                imgLayoutParams = (RelativeLayout.LayoutParams) draggableImg.getLayoutParams();
                imgLayoutParams.leftMargin = dropX;
                imgLayoutParams.topMargin = dropY;
                draggableImg.setLayoutParams(imgLayoutParams);
                // Return true to confirm successful drop
                return true;

            case DragEvent.ACTION_DRAG_ENDED:
                Log.d(LOG_TAG, "Action is DragEvent.ACTION_DRAG_ENDED");
                // Check if drop was successful
                if (!event.getResult()) {
                    Log.d(LOG_TAG, "Drop operation failed!");
                }
                break;

            default:
                break;
        }
        return true;
    }
});

Why This Works

  • Parent container as listener: The parent covers the entire screen, so it captures all drag events throughout the operation, even when the ImageView moves away from its original spot.
  • ACTION_DRAG_STARTED returns true: This tells the system the listener wants to handle further drag events, so you'll see all log messages now.
  • Custom shadow builder: Ensures the drag shadow matches your ImageView exactly, avoiding the default shadow's size/position mismatch.
  • ACTION_DROP returns true: Confirms to the system the drop was successful, which fixes the Reporting drop result: false log message and finalizes the ImageView's position.

内容的提问来源于stack exchange,提问作者zarrin

火山引擎 最新活动