안드로이드(Android) 이미지 드래그 앤 드랍(Drag and Drop) 하는 방법

 

 환경: Eclipse Mars, Android 4.2.2

 

이번에 소개할 내용은 이미지 개체를 두 개의 이미지뷰 사이로 이동할 수 있는 기능입니다. 이미지를 끌어서 드래그 앤 드랍으로 옮길 수가 있습니다. 응용하시면 스마트폰에 폴더를 휴지통에 집어넣는 기능을 구현할 수도 있습니다. 동영상을 보시면 어떤 동작을 하는지 쉽게 알 수 있을 겁니다.

 

 

 

▼ 일단 이미지를 이동할 것인지 여부를 판단해야 합니다. 이것을 판단하는 기준은 오랫동안 클릭하는 것입니다. OnLongClickListener 인터페이스를 구현하기 위해 클래스를 하나 만듭니다. OnLongClickListener 는 화면에서 이미지 개체를 일정 시간 동안 누르게 되면 클릭 이벤트를 받을 수 있습니다. onLongClick 함수의 인수로 넘어온 View 객체에 값을 채우는데 startDrag() 함수를 이용합니다. startDrag() 함수에 필요한 값들을 채우게 되면 드래그 해서 옮길 수 있는 이미지가 됩니다.

private final class LongClickListener implements
		OnLongClickListener {

	public boolean onLongClick(View view) {

		// 태그 생성
		ClipData.Item item = new ClipData.Item(
				(CharSequence) view.getTag());

		String[] mimeTypes = { ClipDescription.MIMETYPE_TEXT_PLAIN };
		ClipData data = new ClipData(view.getTag().toString(),
				mimeTypes, item);
		DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
				view);

		view.startDrag(data, // data to be dragged
				shadowBuilder, // drag shadow
				view, // 드래그 드랍할  Vew
				0 // 필요없은 플래그
		);

		view.setVisibility(View.INVISIBLE);
		return true;
	}
}

 

▼ 아래 두 개의 XML 파일은 이미지를 드래그해서 옮길 때 영역에 들어간 View 위젯의 백그라운드 색상을 변경하기 위한 값들입니다. 영역에 들어가면 target_shape.xml 에 설정 값이 적용되고 빠져 나오면 normal_shape.xml 이 적용됩니다.

 

normal_shape.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<color 
    xmlns:android="http://schemas.android.com/apk/res/android"
	android:color="#FFCC33" />

target_shape.xml

<?xml version="1.0" encoding="UTF-8"?>
<color 
    xmlns:android="http://schemas.android.com/apk/res/android"
	android:color="#66FF33" />

 

▼ 드래그가 되는지 표시하기 위한 XML 셋팅 소스입니다. Activity 에서 getDrawable() 함수를 이용해 Drawable 객체를 생성합니다. 그리고 View.setBackground() 함수에 인자로 넘기면 색상 정보가 설정한 값으로 변경됩니다.

Drawable normalShape = getResources().getDrawable(
		R.drawable.normal_shape);
Drawable targetShape = getResources().getDrawable(
		R.drawable.target_shape);
v.setBackground(targetShape);

 

▼ 이제 드래그앤 드랍을 위한 이벤트 정보를 알아 봐야겠죠. 아래에서 설명한 여러 이벤트 중에서 가장 핵심인 ACTION_DROP 소스만 설명하도록 하겠습니다.

 

DragEvent.ACTION_DRAG_STARTED: 드래그앤 드랍의 시작을 알립니다. 얼마 동안 누르고 있으면 해당 이벤트가 발생하게 됩니다.

DragEvent.ACTION_DRAG_ENTERED: 이미지를 드래그해서 다른 경계 지점으로 넘어갔을 때 발생하는 이벤트 입니다. 드랍할 지역으로 넘어왔다는 메시지입니다.

DragEvent.ACTION_DRAG_EXITED: 드래그한 이미지가 해당 영역에서 빠져 나갔다는 메시지 입니다.

DragEvent.ACTION_DROP : 이미지를 드래그해서 다른 지역으로 옮긴 후 누른 상태를 놓았을 때 발생하는 메시지 입니다. 드래그앤 드랍이 완료된 것이죠.

DragEvent.ACTION_DRAG_ENDED : ACTION_DROP 메시지가 완료되고 다음으로 실행되는 이벤트 입니다.

 

ACTION_DROP 이벤트가 발생했을 때 이미지를 처리하는 로직입니다. 그러니까 드래그해서 옮기다가 누른 상태를 놓은 것이죠. 이때 구분해야 되는 3가지 경우의 수가 있습니다. 첫 번째는 원래 영역의 위치에 이미지가 있는지, 아니면 옮기려고 했던 영역에 이미지가 있는지, 또는 다른 영역인지 판단합니다. 판단은 인수로 넘어온 View 객체로 하시면 됩니다. ID 를 비교하면 어떤 영역인지 알 수 있겠죠.

 

case DragEvent.ACTION_DROP:
	Log.d("DragClickListener", "ACTION_DROP");

if (v == findViewById(R.id.bottomlinear)) {
	View view = (View) event.getLocalState();
	ViewGroup viewgroup = (ViewGroup) view.getParent();
	viewgroup.removeView(view);

	// change the text
	TextView text = (TextView) v.findViewById(R.id.text);
	text.setText("이미지가 드랍되었습니다.");

	LinearLayout containView = (LinearLayout) v;
	containView.addView(view);
	view.setVisibility(View.VISIBLE);
	
}else if (v == findViewById(R.id.toplinear)) {
	View view = (View) event.getLocalState();
	ViewGroup viewgroup = (ViewGroup) view.getParent();
	viewgroup.removeView(view);

	LinearLayout containView = (LinearLayout) v;
	containView.addView(view);
	view.setVisibility(View.VISIBLE);
	
}else {
	View view = (View) event.getLocalState();
	view.setVisibility(View.VISIBLE);
	Context context = getApplicationContext();
	Toast.makeText(context,"이미지를 다른 지역에 드랍할수 없습니다.",
			Toast.LENGTH_LONG).show();
	break;
}
break;

 

▼ 드래그앤 드랍의 전체 Activity  소스 입니다. 하나의 클래스에 다 집어 넣어서 복잡할 수도 있지만 핵심은 DragListener 클래스의 ACTION_DROP 이벤트 입니다.

import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.example.sampleandroidinfo.R;

public class DragAndDropActivity extends Activity {

	private ImageView mImg;
	private static final String IMAGEVIEW_TAG = "드래그 이미지";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_drag_and_drop);
		mImg = (ImageView) findViewById(R.id.image);
		mImg.setTag(IMAGEVIEW_TAG);

		mImg.setOnLongClickListener(new LongClickListener());

		findViewById(R.id.toplinear).setOnDragListener(
				new DragListener());
		findViewById(R.id.bottomlinear).setOnDragListener(
				new DragListener());

	}

	private final class LongClickListener implements
			OnLongClickListener {

		public boolean onLongClick(View view) {

			// 태그 생성
			ClipData.Item item = new ClipData.Item(
					(CharSequence) view.getTag());

			String[] mimeTypes = { ClipDescription.MIMETYPE_TEXT_PLAIN };
			ClipData data = new ClipData(view.getTag().toString(),
					mimeTypes, item);
			DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
					view);

			view.startDrag(data, // data to be dragged
					shadowBuilder, // drag shadow
					view, // 드래그 드랍할  Vew
					0 // 필요없은 플래그
			);

			view.setVisibility(View.INVISIBLE);
			return true;
		}
	}

	class DragListener implements OnDragListener {
		Drawable normalShape = getResources().getDrawable(
				R.drawable.normal_shape);
		Drawable targetShape = getResources().getDrawable(
				R.drawable.target_shape);

		public boolean onDrag(View v, DragEvent event) {

			// 이벤트 시작
			switch (event.getAction()) {

			// 이미지를 드래그 시작될때 
			case DragEvent.ACTION_DRAG_STARTED:
				Log.d("DragClickListener", "ACTION_DRAG_STARTED");
				break;

			// 드래그한 이미지를 옮길려는 지역으로 들어왔을때 
			case DragEvent.ACTION_DRAG_ENTERED:
				Log.d("DragClickListener", "ACTION_DRAG_ENTERED");
				// 이미지가 들어왔다는 것을 알려주기 위해 배경이미지 변경
				v.setBackground(targetShape); 
				break;

			// 드래그한 이미지가 영역을 빠져 나갈때 
			case DragEvent.ACTION_DRAG_EXITED:
				Log.d("DragClickListener", "ACTION_DRAG_EXITED");
				v.setBackground(normalShape);
				break;

			// 이미지를 드래그해서 드랍시켰을때 
			case DragEvent.ACTION_DROP:
				Log.d("DragClickListener", "ACTION_DROP");
				
				if (v == findViewById(R.id.bottomlinear)) {
					View view = (View) event.getLocalState();
					ViewGroup viewgroup = (ViewGroup) view
							.getParent();
					viewgroup.removeView(view);

					// change the text
					TextView text = (TextView) v
							.findViewById(R.id.text);
					text.setText("이미지가 드랍되었습니다.");

					LinearLayout containView = (LinearLayout) v;
					containView.addView(view);
					view.setVisibility(View.VISIBLE);
					
				}else if (v == findViewById(R.id.toplinear)) {
					View view = (View) event.getLocalState();
					ViewGroup viewgroup = (ViewGroup) view
							.getParent();
					viewgroup.removeView(view);

					LinearLayout containView = (LinearLayout) v;
					containView.addView(view);
					view.setVisibility(View.VISIBLE);
					
				}else {
					View view = (View) event.getLocalState();
					view.setVisibility(View.VISIBLE);
					Context context = getApplicationContext();
					Toast.makeText(context,
							"이미지를 다른 지역에 드랍할수 없습니다.",
							Toast.LENGTH_LONG).show();
					break;
				}
				break;

			case DragEvent.ACTION_DRAG_ENDED:
				Log.d("DragClickListener", "ACTION_DRAG_ENDED");
				v.setBackground(normalShape); // go back to normal shape

			default:
				break;
			}
			return true;
		}
	}
}

 

▼ 메인 Activity 에 쓰인 화면 레이아웃 XML 입니다. 원래 영역과 드래그앤 드랍할 영역으로 나누어져 있습니다.

 

activity_drag_and_drop.xml

 

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/toplinear"
        android:layout_width="fill_parent"
        android:layout_height="170dp"
        android:background="@drawable/normal_shape"
        android:layout_marginBottom="5dp" >

        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/bottomlinear"
        android:layout_width="fill_parent"
        android:layout_height="170dp"
        android:background="@drawable/normal_shape" >

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="이미지를 여기로 드랍하세요 .." />

    </LinearLayout>

</GridLayout> 
Posted by 녹두장군

댓글을 달아 주세요

  1. 행인 2016.06.27 14:15  댓글주소  수정/삭제  댓글쓰기

    감사합니다~ 많은 참고가 되었습니다

  2. 차밍 2016.10.04 10:28  댓글주소  수정/삭제  댓글쓰기

    감사합니다 정리 잘해주셨네요 공부 많이하고 갑니다 ^^