Touch Event 를 이용하여 이동이 자유로운 View 를 만들어보려고 합니다. 흔히 알고 있는 드래그 앤 드롭(Drag And Drop) 방식으로 화면에 벗어나지 않도록 좌표 값에 대한 예외 처리를 추가했습니다.         1. Touch 시...

[안드로이드] Drag And Drop View 예제(좌표 표시, 화면 밖으로 X)

Touch Event 를 이용하여 이동이 자유로운 View 를 만들어보려고 합니다.

흔히 알고 있는 드래그 앤 드롭(Drag And Drop) 방식으로

화면에 벗어나지 않도록 좌표 값에 대한 예외 처리를 추가했습니다.

 

 

 

 

1. Touch 시 이동 가능한 View 예제 결과

 - 생성된 View 는 Touch 시 이동이 가능하다.

 - View 에서는 좌표 값을 표시 한다.

 - Close 시 해당 View 는 종료 시킨다.




 

 

2. Drag And Drop View

아래 DragAndDropView 클래스 전문 입니다.

주요 내용을 요약해보자면..

 - DragAndDropView 는 LinearLayout 를 상속받은 View 클래스 이다.

 - TouchListener 인터페이스 구현을 통해 Touch 시 View 의 좌표 값을 갱신한다.

 - DragAndDropViewListener 를 통해 Close 동작을 시킨다.

 

[DragAndDropView.kt]

class DragAndDropView(context: Context) : LinearLayout(context), View.OnTouchListener {

private val binding: DragAndDropViewBinding

private var statusBarHeight = 0

private var viewX = 0f
private var viewY = 0f

private var dragAndDropViewListener: DragAndDropViewListener? = null

init {
val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
binding = DataBindingUtil.inflate(layoutInflater, R.layout.drag_and_drop_view, this, true)

// 상태바(Status Bar) 높이값 구하기
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) statusBarHeight = resources.getDimensionPixelSize(resourceId)

setOnTouchListener(this)
bringToFront() // View 최상단으로 올리기

binding.btnClose.setOnClickListener {
dragAndDropViewListener?.onClose()
}
}

interface DragAndDropViewListener {
fun onClose()
// TODO : You can customize listener.
}

fun setDragAndDropViewListener(listener: DragAndDropViewListener) {
dragAndDropViewListener = listener
}

override fun onTouch(view: View, event: MotionEvent): Boolean {

val parentWidth = (view.parent as ViewGroup).width
val parentHeight = (view.parent as ViewGroup).height

val width = parentWidth - view.width
val height = parentHeight - view.height

when (event.action) {
MotionEvent.ACTION_DOWN -> {
// Touch Event 가 발생한 View 내에서의 좌표값 저장
viewX = event.x
viewY = event.y + statusBarHeight
// (참조) Touch y 좌표값이 틀어지는 현상이 있었는데 단말의 statusBar 만큼 차이를 주니까 해결됐다..
}
MotionEvent.ACTION_MOVE -> {
// 화면의 좌표값에서 View 내에서의 좌표값만큼 빼서 위치시킨다. (View 를 선택한 위치에 따른 Offset)
view.x = event.rawX - viewX
view.y = event.rawY - viewY
}
MotionEvent.ACTION_UP -> {
// 좌표값을 벗어났을 때 예외처리
if (view.x < 0) view.x = 0f
if (view.y < 0) view.y = 0f
if (view.x > width) view.x = width.toFloat()
if (view.y > height) view.y = height.toFloat()
}
}

binding.tvX.text = "x : ${view.x}"
binding.tvY.text = "y : ${view.y}"

return true
}
}

 

[drag_and_drop_view.xml]

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout
android:layout_width="160dp"
android:layout_height="120dp"
android:background="@drawable/border"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@drawable/border"
android:gravity="center"
android:orientation="vertical">

<TextView
android:id="@+id/tvX"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/tvY"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

<Button
android:id="@+id/btnClose"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_gravity="center"
android:text="Close"
android:textAllCaps="false" />

</LinearLayout>
</layout>

 




3. TouchListener 관련 부연설명

처음에 TouchListener 인터페이스를 구현할 때 어려움이 있었는데 몇가지 이해에 도움됐던 내용을 공유 드리려고 합니다.


Touch Event 에 x, y 좌표에 대한 정의 체크하기.

 - event.x : View 내에서의 x 좌표

 - event.y : View 내에서의 y 좌표

 - event.rawX : 화면에서의 x 좌표

 - event.rawY : 화면에서의 y 좌표

 

그림으로 좌표 체크하기 (화면에 벗어나지 못하도록 예외처리)




 

4. Add View

아래 MainActivity 클래스 전문 입니다.

addDragAndDropView 메서드를 통해 DragAndDropView 생성에서 Add View 까지 하는 동작을 확인하실 수 있습니다.

 

[MainActivity.kt]

class MainActivity : AppCompatActivity() {

lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

binding.btnAddView.setOnClickListener {
addDragAndDropView()
}
}

private fun addDragAndDropView() {
val dragAndDropView = DragAndDropView(this)
dragAndDropView.setDragAndDropViewListener(object : DragAndDropView.DragAndDropViewListener {
override fun onClose() {
binding.mainFrameLayout.removeView(dragAndDropView)
}
})
val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
binding.mainFrameLayout.addView(dragAndDropView, layoutParams)
}
}

 

[activity_main.xml]

<layout xmlns:android="http://schemas.android.com/apk/res/android">

<FrameLayout
android:id="@+id/mainFrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/btnAddView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Add View"
android:textAllCaps="false" />
</FrameLayout>
</layout>

 

 

 

 

[Reference]

디바이스 높이 가져오기

 

 

 

0 comments: