[안드로이드] 터치 이벤트

wooki·2022년 7월 22일

Android

목록 보기
3/3

안드로이드

안드로이드에서 터치 이벤트가 발생했을 때 어떻게 작동하는지에 대해 궁금해 찾아보게 되었다.

touch event
위 그림을 보면 Activity안에 2개의 ViewGroup과 1개의 View가 존재하고 해당 View에 터치 이벤트가 발생했을 때의 모습이다. 그렇다면 내부적으로는 어떻게 처리할까에 대해서 확인해보았다.

touch event logic

위 그림에서 볼 수 있듯이 맨 처음 터치 이벤트가 발생하면 Activity의 dispatchTouchEvent() 메소드를 통해 하위 뷰들로 터치 이벤트를 전달한다. 그리고 내가 터치한 뷰에 도착하면 onTouch() 또는 onTouchEvent() 메소드가 호출되고 역순으로 터치 메소드를 실행시켜주게 된다. 만약 중간에 onInterceptTouchEvent() 메소드가 true를 반환하면 하위 뷰들에게는 이벤트를 전달하지 않고 해당 뷰에서 터치를 처리한다. 단, Activity에는 onInterceptTouchEvent()가 없기 때문에 dispatchTouchEvent()를 이용할 수도 있다. 이것을 직접 코드로 확인해보면 아래와 같다.

CustomLayout

class CustomLayout(context: Context, attrs: AttributeSet?) : ConstraintLayout(context, attrs) {

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        Log.d("TEST", "Layout dispatchTouchEvent")
        return super.dispatchTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        Log.d("TEST", "Layout onTouchEvent")
        return super.onTouchEvent(event)
    }
}

CustomView

class CustomView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    private val name = context.resources.getResourceName(this.id)
    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        Log.d("TEST", "[$name] View dispatchTouchEvent")
        return super.dispatchTouchEvent(event)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        Log.d("TEST", "[$name] View onTouchEvent")
        return super.onTouchEvent(event)
    }

    override fun performClick(): Boolean {
        Log.d("TEST", "[$name] View performClick")
        return super.performClick()
    }
}

MainActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        Log.d("TEST", "Activity dispatchTouchEvent")
        return super.dispatchTouchEvent(ev)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        Log.d("TEST", "Activity onTouchEvent")
        return super.onTouchEvent(event)
    }

xml

<?xml version="1.0" encoding="utf-8"?>
<com.example.android_example.CustomLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:id="@+id/layout">

    <com.example.android_example.CustomView
        android:id="@+id/view1"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="@color/black"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.example.android_example.CustomView
        android:id="@+id/view2"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:background="@color/purple_200"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</com.example.android_example.CustomLayout>

화면

화면

결과

D/TEST: Activity dispatchTouchEvent
D/TEST: Layout dispatchTouchEvent
D/TEST: [com.example.android_example:id/view2] View dispatchTouchEvent
D/TEST: [com.example.android_example:id/view2] View onTouchEvent
D/TEST: [com.example.android_example:id/view1] View dispatchTouchEvent
D/TEST: [com.example.android_example:id/view1] View onTouchEvent
D/TEST: Layout onTouchEvent
D/TEST: Activity onTouchEvent

위 결과를 확인해보면 Activity -> Layout -> View(view2) -> View(view1) 순으로 이벤트를 전달하고, 그 역으로 터치 이벤트를 실행시켜주는 것을 알 수 있다. 여기서 조금 의문이었던게 뷰를 겹쳐서 배치하니 view2(보라색)를 터치하면 이벤트가 발생하고 그 후에 view1(검은색)에서 발생되었다. 개인적으로 view1(검은색)은 발생하지 않을 것이라고 생각했었다.
그래서 뷰가 겹쳐져있지 않은 경우에 터치를 했을 때를 확인해보니 그때는 하나의 뷰만 터치 이벤트가 발생했다.
ViewGroup도 View를 상속받은 것이기 때문에 이상할 게 없다는 결론을 내리긴했다. 물론 좀 더 내부적으로 알아야 이해하기가 더 쉬울 것 같다는 생각이 들었다. 아직 제대로 이해되지 않은 부분이 있어 이해한 후에 글을 수정해볼 생각이다.

아래 주소는 스택오버플로우에 어떤 분이 하나하나 상세히 설명해준 글이다. 깊게 이해하고 싶다면 아래 주소를 참고하면 될 것 같다.

참고 자료
미디엄 주소

0개의 댓글