ภาพรวม
ตัวอย่างนี้สาธิตการใช้ Drag and Drop API ใน Android เพื่อลาก View (รูปภาพ) จากพื้นที่ซ้ายไปวางในพื้นที่ขวา โดยใช้ View.OnDragListener และ View.OnTouchListener
ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่
Android Studio → File → New Project → Empty Activity
ขั้นตอนที่ 2: จัดรูปแบบหน้าจอ (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="16dp"
tools:context=".MainActivity">
<!-- พื้นที่ต้นทาง (Source) -->
<LinearLayout
android:id="@+id/leftView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_margin="8dp"
android:background="@android:color/darker_gray"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Drag from here"
android:textColor="@android:color/white"
android:textStyle="bold" />
<ImageView
android:id="@+id/boxView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:layout_margin="8dp"
android:background="@drawable/ic_launcher_foreground"
android:contentDescription="Draggable Item" />
</LinearLayout>
<!-- พื้นที่ปลายทาง (Target) -->
<LinearLayout
android:id="@+id/rightView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_margin="8dp"
android:background="@android:color/holo_blue_light"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Drop here"
android:textColor="@android:color/white"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
ขั้นตอนที่ 3: เขียนโค้ด MainActivity.java
import android.os.Bundle;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity
implements View.OnTouchListener, View.OnDragListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ตั้ง Listener
findViewById(R.id.boxView).setOnTouchListener(this);
findViewById(R.id.leftView).setOnDragListener(this);
findViewById(R.id.rightView).setOnDragListener(this);
}
@Override
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// เริ่ม Drag
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
// ClipData (Optional) - ส่งข้อมูลไปกับ Drag
// ClipData data = ClipData.newPlainText("label", "boxView");
// view.startDragAndDrop(data, shadowBuilder, view, 0); // API 24+
// Deprecated แต่ยังทำงานได้
view.startDrag(null, shadowBuilder, view, 0);
// ซ่อน View ต้นทางขณะลาก
view.setVisibility(View.INVISIBLE);
return true;
}
return false;
}
@Override
public boolean onDrag(View view, DragEvent event) {
int action = event.getAction();
switch (action) {
case DragEvent.ACTION_DRAG_STARTED:
// Drag เริ่มต้น - ตรวจสอบว่า View นี้รับ Drop ได้ไหม
// return true เพื่อรับ Drop ต่อไป
return true;
case DragEvent.ACTION_DRAG_ENTERED:
// Drag เข้ามาใน View นี้ - เปลี่ยนสีพื้นหลังบอกว่า Drop ได้
view.setBackgroundColor(getResources().getColor(android.R.color/holo_green_light));
break;
case DragEvent.ACTION_DRAG_EXITED:
// Drag ออกจาก View นี้ - คืนสีเดิม
view.setBackgroundColor(getResources().getColor(android.R.color/holo_blue_light));
break;
case DragEvent.ACTION_DROP:
// Drop ลงใน View นี้
View draggedView = (View) event.getLocalState();
ViewGroup sourceParent = (ViewGroup) draggedView.getParent();
// ย้าย View จาก Parent เดิม
if (sourceParent != null) {
sourceParent.removeView(draggedView);
}
// เพิ่มเข้า Target Parent
if (view instanceof ViewGroup) {
((ViewGroup) view).addView(draggedView);
}
// แสดง View อีกครั้ง
draggedView.setVisibility(View.VISIBLE);
Toast.makeText(this, "Dropped!", Toast.LENGTH_SHORT).show();
break;
case DragEvent.ACTION_DRAG_ENDED:
// Drag จบสิ้น (ไม่ว่าจะ Drop สำเร็จหรือไม่)
// คืนสีพื้นหลัง
view.setBackgroundColor(getResources().getColor(android.R.color/holo_blue_light));
// ถ้า Drop ไม่สำเร็จ ให้คืน View กลับไปที่เดิม
if (!event.getResult()) {
View draggedView = (View) event.getLocalState();
draggedView.setVisibility(View.VISIBLE);
}
break;
}
return true;
}
}
ขั้นตอนที่ 4: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="app.com.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
</intent-filter>
</activity>
</application>
</manifest>
การทดสอบ
รันแอป → กดค้างที่รูปภาพในพื้นที่สีเทา → ลากไปวางในพื้นที่สีฟ้า → รูปภาพจะย้ายไปอยู่ที่นั่น
Drag and Drop แบบ Modern (API 24+)
// ใช้ startDragAndDrop แทน startDrag
view.startDragAndDrop(
ClipData.newPlainText("item_id", "boxView"), // Data
new View.DragShadowBuilder(view), // Shadow
view, // Local State
0 // Flags
);
// ใน OnDragListener ใช้ ClipData
case DragEvent.ACTION_DROP:
ClipData clipData = event.getClipData();
if (clipData != null && clipData.getItemCount() > 0) {
String itemId = clipData.getItemAt(0).getText().toString();
// ประมวลผลตาม itemId
}
break;
RecyclerView Drag & Drop (ItemTouchHelper)
สำหรับรายการที่สามารถเรียงลำดับได้ (Reorder List) ใช้ ItemTouchHelper:
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder source,
RecyclerView.ViewHolder target) {
// Swap items in adapter
int from = source.getAdapterPosition();
int to = target.getAdapterPosition();
Collections.swap(dataList, from, to);
adapter.notifyItemMoved(from, to);
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// Handle swipe to delete
int position = viewHolder.getAdapterPosition();
dataList.remove(position);
adapter.notifyItemRemoved(position);
}
};
new ItemTouchHelper(callback).attachToRecyclerView(recyclerView);
Jetpack Compose Drag & Drop
@Composable
fun DragDropExample() {
var draggedItem by remember { mutableStateOf(null) }
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
// Source
Box(
modifier = Modifier
.size(100.dp)
.fillMaxHeight()
.background(Color.Gray)
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(
onDragStart = { draggedItem = 1 },
onDragEnd = { draggedItem = null }
)
}
) {
Text("Drag Me", color = Color.White, textAlign = TextAlign.Center, modifier = Modifier.align(Alignment.Center))
}
// Target
Box(
modifier = Modifier
.size(100.dp)
.fillMaxHeight()
.background(if (draggedItem != null) Color.Green else Color.Blue)
.pointerInput(Unit) {
detectDragGestures(
onDragEnd = {
if (draggedItem != null) {
// Handle drop
draggedItem = null
}
}
)
}
) {
Text("Drop Here", color = Color.White, textAlign = TextAlign.Center, modifier = Modifier.align(Alignment.Center))
}
}
}