본문 바로가기
프로그래밍/Android

[Android] Drag And Drop View 예제

by Youngs_ 2024. 3. 4.

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
    }
}

 

<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. Touch Listenere 관련 부연설명

처음에 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]

디바이스 높이 가져오기

 

[Android, Device Height] Android P 이후 디바이스 높이 가져오기

안녕하세요. 블랙진입니다. Android 9(Pie, API 레벨 28) 이후 나온 스마트폰에서 높이 값을 가져오는 코드에 대한 필자가 겪은 이슈 내용을 포스팅 하고자 합니다. 먼저 그동안 디바이스의 높이를 가

black-jin0427.tistory.com

 

 


출처 : https://bictoselfdev.blogspot.com/2023/02/drag-and-drop-view.html

 

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

(안드로이드 코틀린) 드래그 앤 드롭(Drag And Drop) 을 활용하여 이동 가능한 View 예제입니다. 화면 밖으로 벗어나지 못하게 예외처리와 View 에 대한 좌표표시를 확인 할 수 있습니다.

bictoselfdev.blogspot.com

 

댓글