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
- 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., yourRelativeLayout) should be the drag listener instead. ACTION_DRAG_STARTEDneeds to returntrue: If you don't returntruehere, the system assumes the listener won't handle any further drag events, leaving onlyACTION_DRAG_ENDEDto fire.ACTION_DROPwasn't properly handling position updates: Your code didn't update the ImageView's layout params in the drop event, and wasn't returningtrueto 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_STARTEDreturnstrue: 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_DROPreturnstrue: Confirms to the system the drop was successful, which fixes theReporting drop result: falselog message and finalizes the ImageView's position.
内容的提问来源于stack exchange,提问作者zarrin




