다음 섹션에서는 드래그 앤 드롭 프로세스의 몇 가지 핵심 개념을 설명합니다.
드래그 앤 드롭 프로세스
드래그 앤 드롭 프로세스에는 시작됨, 진행 중, 삭제됨, 종료됨이라는 4개의 단계 또는 상태가 있습니다.
- 시작됨
사용자의 드래그 동작에 대한 응답으로 애플리케이션은
startDragAndDrop()
를 호출하여 시스템에 드래그 앤 드롭 작업을 시작하도록 알립니다. 이 메서드의 인수는 다음을 제공합니다.- 드래그할 데이터입니다.
- 드래그 섀도우를 그리는 콜백
- 드래그된 데이터를 설명하는 메타데이터
- 시스템은 드래그 섀도우를 가져오기 위해 애플리케이션을 다시 호출하여 응답합니다. 그런 다음 기기에 드래그 섀도우를 표시합니다.
- 다음으로 시스템은 작업 유형이
ACTION_DRAG_STARTED
인 드래그 이벤트를 현재 레이아웃에 있는 모든View
객체의 드래그 이벤트 리스너로 전송합니다. 발생할 수 있는 드롭 이벤트를 포함하여 드래그 이벤트를 계속 수신하려면 드래그 이벤트 리스너가true
를 반환해야 합니다. 그러면 리스너가 시스템에 등록됩니다. 등록된 리스너만 드래그 이벤트를 계속 수신합니다. 이때, 리스너는 뷰가 드롭 이벤트를 허용할 수 있다고 표시하도록 드롭 타겟View
객체의 모양을 변경할 수도 있습니다. - 드래그 이벤트 리스너가
false
를 반환하면 시스템에서 작업 유형이ACTION_DRAG_ENDED
인 드래그 이벤트를 보낼 때까지 현재 작업의 드래그 이벤트를 받지 않습니다.false
를 반환하면 리스너가 드래그 앤 드롭 작업에 관심이 없으며 드래그된 데이터를 허용하지 않겠다고 시스템에 알리는 것입니다.
- 진행 중
- 사용자가 드래그를 계속합니다. 드래그 그림자가 드롭 타겟의 경계 상자를 교차할 때 시스템은 하나 이상의 드래그 이벤트를 타겟의 드래그 이벤트 리스너로 전송합니다. 리스너는 이벤트의 응답으로 드롭 타겟
View
의 모양을 변경할 수 있습니다. 예를 들어 이벤트에서 드래그 그림자가 드롭 타겟의 경계 상자에 진입했다고 표시하면(작업 유형ACTION_DRAG_ENTERED
) 리스너는View
를 강조표시하여 반응할 수 있습니다. - 드롭됨
- 사용자가 드롭 타겟의 경계 상자 내에 드래그 그림자를 놓습니다. 그러면 시스템은 드롭 타겟의 리스너에 작업 유형이
ACTION_DROP
인 드래그 이벤트를 전송합니다. 드래그 이벤트 객체에는 작업을 시작하는startDragAndDrop()
호출에서 시스템에 전달되는 데이터가 포함됩니다. 리스너가 드롭된 데이터를 성공적으로 처리하면 리스너는 시스템에 불리언true
를 반환해야 합니다. : 이 단계는 사용자가 드래그 이벤트를 수신하도록 리스너가 등록된View
의 경계 상자(드롭 타겟) 내에 드래그 섀도우를 드롭할 때만 발생합니다. 사용자가 다른 상황에서 드래그 섀도우를 놓으면ACTION_DROP
드래그 이벤트가 전송되지 않습니다. - 종료됨
사용자가 드래그 그림자를 놓고
작업 유형이
ACTION_DROP
인 드래그 이벤트를 전송한 후에는 드래그 앤 드롭 작업이 종료되었음을 알리기 위해 시스템에서 작업 유형이ACTION_DRAG_ENDED
인 드래그 이벤트를 전송합니다. 이 동작은 사용자가 드래그 섀도우를 놓는 위치와 상관없이 완료됩니다. 이 이벤트는 리스너가ACTION_DROP
이벤트를 수신한 경우에도 드래그 이벤트를 받도록 등록된 모든 리스너에 전송됩니다.
각 단계는 드래그 앤 드롭 작업 섹션에 자세히 설명되어 있습니다.
드래그 이벤트
시스템은 DragEvent
객체 형태의 드래그 이벤트를 전송하며, 전송된 객체에는 드래그 앤 드롭 진행 과정에서 발생하고 있는 일을 설명하는 작업 유형이 포함됩니다. 작업 유형에 따라 객체는 다른 데이터를 포함할 수도 있습니다.
드래그 이벤트 리스너는 DragEvent
객체를 수신합니다. 작업 유형을 가져오려면 리스너가 DragEvent.getAction()
을 호출합니다.
DragEvent
클래스에서 상수로 정의된 6가지 값이 있으며 이는 표 1에 설명되어 있습니다.
표 1. DragEvent 작업 유형
작업 유형 | 의미 |
---|---|
ACTION_DRAG_STARTED |
애플리케이션이 startDragAndDrop() 을 호출하고 드래그 섀도우를 획득합니다. 리스너가 이 작업의 드래그 이벤트를 계속 수신하려면 리스너에서 불리언 true 를 시스템에 반환해야 합니다.
|
ACTION_DRAG_ENTERED |
드래그 섀도우가 드래그 이벤트 리스너 View 의 경계 상자에 진입합니다. 이는 드래그 그림자가 경계 상자에 진입할 때 리스너가 수신하는 첫 번째 이벤트 작업 유형입니다.
|
ACTION_DRAG_LOCATION |
ACTION_DRAG_ENTERED 이벤트 후에도 드래그 섀도우는 드래그 이벤트 리스너 View 의 경계 상자 내부에 계속 있습니다.
|
ACTION_DRAG_EXITED |
ACTION_DRAG_ENTERED 및 하나 이상의 ACTION_DRAG_LOCATION 이벤트 후에 드래그 섀도우가 드래그 이벤트 리스너의 View 경계 상자 외부로 이동합니다.
|
ACTION_DROP |
드래그 섀도우가 드래그 이벤트 리스너의 View 위에 놓입니다. 이 작업 유형은 리스너가 ACTION_DRAG_STARTED 드래그 이벤트의 응답으로 불리언 true 를 반환했을 때만 View 객체의 리스너에 전송됩니다. 사용자가 리스너가 등록되지 않은 View 에 드래그 섀도우를 놓거나 현재 레이아웃에 포함되지 않은 항목에 드래그 섀도우를 놓으면 이 작업 유형이 전송되지 않습니다.
드롭이 성공적으로 처리되면 리스너는 불리언 |
ACTION_DRAG_ENDED |
시스템이 드래그 앤 드롭 작업을 종료하고 있습니다. 이 작업 유형이 반드시 ACTION_DROP 이벤트 다음에 발생하는 것은 아닙니다. 시스템에서 ACTION_DROP 을 전송했다면 ACTION_DRAG_ENDED 작업 유형을 수신했다고 해서 드롭을 성공했다는 의미는 아닙니다. 리스너는 표 2와 같이 getResult() 를 호출하여 ACTION_DROP 의 응답으로 반환된 값을 가져와야 합니다. ACTION_DROP 이벤트가 전송되지 않으면 getResult() 는 false 를 반환합니다.
|
DragEvent
객체에는 애플리케이션이 startDragAndDrop()
호출에서 시스템에 제공하는 데이터와 메타데이터도 포함됩니다. 일부 데이터는 표 2에 요약된 특정 작업 유형에만 유효합니다. 이벤트 및 이벤트에 관련된 데이터에 관한 자세한 내용은 드래그 앤 드롭 작업 섹션을 참고하세요.
표 2. 작업 유형별 유효한 DragEvent 데이터
getAction() 값 |
getClipDescription() 값 |
getLocalState() 값 |
getX() 값 |
getY() 값 |
getClipData() 값 |
getResult() 값 |
---|---|---|---|---|---|---|
ACTION_DRAG_STARTED |
✓ | ✓ | ||||
ACTION_DRAG_ENTERED |
✓ | ✓ | ||||
ACTION_DRAG_LOCATION |
✓ | ✓ | ✓ | ✓ | ||
ACTION_DRAG_EXITED |
✓ | ✓ | ||||
ACTION_DROP |
✓ | ✓ | ✓ | ✓ | ✓ | |
ACTION_DRAG_ENDED |
✓ | ✓ |
DragEvent
메서드 getAction()
, describeContents()
, writeToParcel()
, toString()
는 항상 유효한 데이터를 반환합니다.
메서드가 특정 작업 유형에 유효한 데이터를 포함하지 않으면 결과 유형에 따라 null
또는 0을 반환합니다.
드래그 섀도우
드래그 앤 드롭 작업 중 시스템은 사용자가 드래그하는 이미지를 표시합니다. 데이터 이동 작업에서 이 이미지는 드래그되고 있는 데이터를 나타냅니다. 다른 작업의 경우 이미지는 드래그 작업의 특정 측면을 나타냅니다.
이 이미지를 드래그 섀도우라고 합니다. 드래그 그림자는 View.DragShadowBuilder
객체에 선언한 메서드를 사용하여 만듭니다. startDragAndDrop()
를 사용하여 드래그 앤 드롭 작업을 시작할 때 시스템에 빌더를 전달합니다. startDragAndDrop()
에 대한 응답의 일부로 시스템은 View.DragShadowBuilder
에 정의한 콜백 메서드를 호출하여 드래그 그림자를 가져옵니다.
View.DragShadowBuilder
클래스에는 다음과 같은 두 가지 생성자가 있습니다.
View.DragShadowBuilder(View)
이 생성자는 애플리케이션의 모든
View
객체를 허용합니다. 생성자는View
객체를View.DragShadowBuilder
객체에 저장하므로 콜백은 이 객체에 액세스하여 드래그 섀도우를 생성할 수 있습니다. 이 뷰가 드래그 작업을 시작하도록 사용자가 선택한View
일 필요는 없습니다.이 생성자를 사용하면
View.DragShadowBuilder
를 확장하거나 빌더의 메서드를 재정의할 필요가 없습니다. 기본적으로 드래그 그림자는 인수로 전달하는View
와 동일한 모양이고, 사용자가 화면을 터치하는 위치 아래를 중심으로 표시됩니다.View.DragShadowBuilder()
이 생성자를 사용하면
View.DragShadowBuilder
객체에서View
객체를 사용할 수 없습니다. 필드는null
로 설정됩니다.View.DragShadowBuilder
를 확장하고 메서드를 재정의해야 합니다. 그렇지 않으면 드래그 섀도우가 표시되지 않습니다. 시스템에서 오류를 발생시키지 않습니다.
View.DragShadowBuilder
클래스에는 함께 드래그 섀도우를 만드는 두 가지 메서드가 있습니다.
onProvideShadowMetrics()
애플리케이션이
startDragAndDrop()
을 호출한 직후 시스템은 이 메서드를 호출합니다. 메서드를 사용하여 드래그 섀도우의 크기와 터치 포인트를 시스템에 전송합니다. 이 메서드에는 다음과 같은 두 개의 매개변수가 있습니다.outShadowSize
:Point
객체입니다. 드래그 그림자 너비는x
에 해당하고 높이는y
에 해당합니다.outShadowTouchPoint
:Point
객체입니다. 터치 포인트는 드래그 그림자 내 위치로, 드래그하는 동안 사용자 손가락 아래에 있어야 합니다. X 위치는x
에 해당하고 Y 위치는y
에 해당합니다.onDrawShadow()
onProvideShadowMetrics()
호출 직후, 시스템은onDrawShadow()
를 호출하여 드래그 섀도우를 만듭니다. 이 메서드에는 하나의 인수로Canvas
객체가 있으며 이 객체는onProvideShadowMetrics()
에 제공한 매개변수를 사용하여 시스템이 생성합니다. 이 메서드는 제공된Canvas
에 드래그 섀도우를 그립니다.
성능을 개선하려면 드래그 섀도우의 크기를 작게 유지합니다. 단일 항목의 경우 아이콘을 사용하는 것이 좋습니다. 여러 항목을 선택하는 경우, 화면 전체에 걸쳐 전체 이미지를 표시하기보다는 스택 형태의 아이콘을 사용하는 것이 좋습니다.
드래그 이벤트 리스너 및 콜백 메서드
View
는 View.OnDragListener
를 구현하는 드래그 이벤트 리스너 또는 뷰의 onDragEvent()
콜백 메서드를 사용하여 드래그 이벤트를 수신합니다. 시스템이 메서드나 리스너를 호출하면 호출된 메서드 또는 리스너에서 DragEvent
인수를 제공합니다.
대부분의 경우 콜백 메서드보다 리스너를 사용하는 것이 더 좋습니다. UI를 설계할 때 일반적으로는 View
클래스의 서브클래스를 만들지 않지만, 콜백 메서드를 사용하면 서브클래스를 만들어 메서드를 재정의해야 합니다. 이와 달리 리스너는 하나의 리스너 클래스를 구현하여 여러 다른 View
객체에 사용할 수 있습니다.
리스너 클래스는 익명의 인라인 클래스 또는 람다 표현식으로 구현할 수도 있습니다. View
객체의 리스너를 설정하려면 setOnDragListener()
를 호출합니다.
또는 메서드를 재정의하지 않고 onDragEvent()
의 기본 구현을 변경할 수도 있습니다. 뷰에 OnReceiveContentListener
를 설정합니다. 자세한 내용은 setOnReceiveContentListener()
를 참고하세요.
그러면 onDragEvent()
메서드는 기본적으로 다음을 실행합니다.
startDragAndDrop()
호출의 응답으로 true를 반환합니다.드래그 앤 드롭 데이터가 뷰에 드롭되면
performReceiveContent()
를 호출합니다. 데이터는ContentInfo
객체로 메서드에 전달됩니다. 이 메서드는OnReceiveContentListener
를 호출합니다.드래그 앤 드롭 데이터가 뷰에 드롭되고
OnReceiveContentListener
가 콘텐츠를 모두 사용하면 true를 반환합니다.
앱에 맞게 데이터를 처리하려면 OnReceiveContentListener
를 정의합니다. API 수준 24와의 하위 호환성을 제공하려면 Jetpack 버전의 OnReceiveContentListener
를 사용하세요.
View
객체의 드래그 이벤트 리스너와 콜백 메서드를 모두 사용할 수 있습니다. 이 경우 시스템은 리스너를 먼저 호출합니다. 리스너에서 false
를 반환하지 않는 한, 시스템에서는 콜백 메서드를 호출하지 않습니다.
onDragEvent()
메서드와 View.OnDragListener
를 함께 사용하는 것은 터치 이벤트에서 onTouchEvent()
와 View.OnTouchListener
를 함께 사용하는 것과 유사합니다.