[Android] Material3의 onTouch 동작 방식

Chance·2025년 2월 27일

onTouch에서 Material3는 왜 return false를 해야 할까?

onTouchEvent()에서 return true vs return false 차이

안드로이드에서 onTouchEvent() 메서드에서 true 또는 false를 반환하는 것은 이벤트의 소비 여부를 결정합니다.

반환 값의미이벤트 전달 여부
true현재 View가 이벤트를 처리하고 더 이상 전파하지 않음이벤트 중단
false현재 View가 이벤트를 처리하지 않음, 상위 View로 전달됨이벤트 전파

1. return true: 이벤트 소비 (Stop Propagation)

override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            Log.d("Touch", "손가락이 닿음")
            return true  // 이벤트 소비
        }
        MotionEvent.ACTION_MOVE -> Log.d("Touch", "손가락 움직임")
        MotionEvent.ACTION_UP -> Log.d("Touch", "손가락 뗌")
    }
    return true
}

결과:

  • 현재 View에서 이벤트를 소비(처리) 하므로 다른 뷰로 이벤트가 전달되지 않음.
  • ACTION_DOWNtrue로 반환하면 이후의 ACTION_MOVE, ACTION_UP도 해당 View에서 처리됨.

2. return false: 이벤트 전달 (Pass Event to Parent)

override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            Log.d("Touch", "손가락이 닿음")
            return false  // 이벤트 전달
        }
        MotionEvent.ACTION_MOVE -> Log.d("Touch", "손가락 움직임")
        MotionEvent.ACTION_UP -> Log.d("Touch", "손가락 뗌")
    }
    return false
}

결과:

  • 현재 View가 이벤트를 처리하지 않으므로 부모(ViewGroup)나 Activity로 이벤트가 전달됨.
  • ACTION_DOWNfalse를 반환하면 ACTION_MOVE, ACTION_UP도 전달되지 않음.
  • ViewGrouponInterceptTouchEvent()에서 이벤트를 가로챌 수도 있음.

3. false vs true 동작 차이 예제

예제 1: 이벤트를 소비하는 경우 (true)

view.setOnTouchListener { _, event ->
    Log.d("Touch", "이벤트 발생: ${event.action}")
    true  // 이벤트를 소비하여 부모로 전달되지 않음
}

결과:

  • ViewGroup이나 Activity로 이벤트가 전달되지 않고 해당 View에서만 이벤트가 처리됨.

예제 2: 이벤트를 부모로 전달하는 경우 (false)

view.setOnTouchListener { _, event ->
    Log.d("Touch", "이벤트 발생: ${event.action}")
    false  // 이벤트를 부모에게 전달
}

결과:

  • 터치 이벤트가 ViewGroup 또는 ActivitydispatchTouchEvent()로 전달됨.
  • ViewGrouponInterceptTouchEvent()를 사용하면 자식 뷰의 터치 이벤트를 가로챌 수도 있음.

4. 실전 예제: ViewGroup과 View의 이벤트 전파 흐름

예제 상황

  • ViewGroup 안에 Button이 있는 경우, ViewGroup이 터치 이벤트를 가로채거나 넘길 수 있음.
class CustomViewGroup(context: Context, attrs: AttributeSet?) : ViewGroup(context, attrs) {
    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        return true // 자식 View(Button)로 이벤트가 전달되지 않음
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.d("Touch", "ViewGroup에서 터치 이벤트 처리됨")
        return true
    }
}

button.setOnTouchListener { _, event ->
    Log.d("Touch", "Button 터치 이벤트 발생")
    false  // 이벤트를 ViewGroup으로 전달
}

결과 분석:

  • onInterceptTouchEvent()true를 반환하면 자식 뷰(Button)로 이벤트가 전달되지 않음.
  • onTouchEvent()true이면 ViewGroup이 이벤트를 처리.
  • false이면 ViewGroup의 부모(Activity)로 이벤트가 전달됨.

5. 정리

반환 값동작
true현재 View가 이벤트를 처리하고 부모로 전달되지 않음
false현재 View가 이벤트를 처리하지 않고 부모(ViewGroup, Activity)로 전달됨

🔹 true를 반환하면 이벤트를 해당 View에서만 처리하고 끝남.

🔹 false를 반환하면 이벤트가 상위 뷰(ViewGroup 또는 Activity)로 전달됨.

Material3에서 onTouchEvent()가 기본적으로 false를 반환하는 이유

Material3의 컴포넌트(예: Button, Card, TextField 등)는 대부분 터치 이벤트를 직접 소비하지 않고, 제스처 감지기(GestureDetector) 또는 클릭/터치 리스너를 통해 이벤트를 처리합니다. 이 때문에, onTouchEvent()에서 false를 반환하는 것이 일반적입니다.


1. Material3 컴포넌트의 터치 이벤트 처리 방식

Material3에서는 다음과 같은 방식으로 터치 이벤트를 처리합니다.

  1. onTouchEvent()에서 false 반환
    • 대부분의 Material3 위젯들은 터치 이벤트를 직접 소비하지 않고, 부모 뷰(ViewGroup)로 전달합니다.
    • 이는 내부적으로 제스처 감지기(GestureDetectorCompat) 를 활용하기 때문입니다.
  2. 제스처 감지기(GestureDetector)가 이벤트를 감지
    • Material3의 UI 요소들은 GestureDetectorCompat을 사용하여 클릭, 롱클릭, 스와이프 등의 이벤트를 감지합니다.
    • 단순한 터치 이벤트는 false를 반환하여 다른 이벤트 리스너(setOnClickListener, setOnLongClickListener)에서 감지하도록 합니다.
  3. 클릭 이벤트(setOnClickListener())가 최종적으로 이벤트를 처리
    • 예를 들어, Button의 경우 onTouchEvent()에서는 false를 반환하지만, setOnClickListener()에서 클릭 이벤트를 처리합니다.

2. 예제 코드 및 분석

(1) 기본 View에서 onTouchEvent() 동작

아래처럼 일반적인 View는 터치 이벤트를 true로 반환하면 이벤트가 소비됩니다.

class CustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.d("Touch", "CustomView 터치 이벤트 발생")
        return true // 이벤트를 소비하여 부모로 전달되지 않음
    }
}

결과

  • true를 반환하면 이벤트가 여기에서 소비되고 부모로 전달되지 않습니다.

(2) Material3 Button에서 onTouchEvent() 동작

button.setOnTouchListener { _, event ->
    Log.d("Touch", "Button 터치 이벤트 발생")
    false  // 부모(ViewGroup)로 이벤트 전달
}

결과

  • 버튼이 터치 이벤트를 직접 소비하지 않고 false를 반환하여 부모(ViewGroup)로 이벤트가 전달됨.
  • 버튼의 클릭 이벤트는 내부적으로 GestureDetector를 통해 처리됨.

3. Material3의 false 반환 이유

1) 터치 이벤트를 직접 소비하지 않고 부모로 전달해야 함

  • MaterialButton, CardView, TextField 같은 Material3 위젯들은 터치 이벤트를 직접 소비하지 않습니다.
  • 터치 이벤트를 직접 소비할 경우, 스크롤 뷰나 다른 제스처 감지가 정상적으로 동작하지 않을 수 있기 때문입니다.

2) 클릭 & 롱클릭 이벤트를 위한 GestureDetector 활용

  • Material3의 컴포넌트들은 GestureDetectorCompat을 사용하여 클릭, 롱클릭을 감지합니다.
  • 터치 이벤트를 true로 반환하면 다른 이벤트(onClick, onLongClick)가 동작하지 않을 수 있습니다.

3) 상위 View에서 터치 이벤트를 제어해야 할 경우가 많음

  • 예를 들어, RecyclerView 안에 있는 MaterialButtontrue를 반환하면 RecyclerView의 스크롤 이벤트가 막힐 수 있습니다.
  • 따라서 기본적으로 false를 반환하여 부모(RecyclerView, ScrollView 등)에서 추가적인 터치 처리를 할 수 있도록 허용합니다.

4. 해결 방법 (필요할 경우 true 반환)

만약 Material3 컴포넌트에서 터치 이벤트를 직접 처리하고 싶다면, setOnTouchListener()에서 true를 반환하면 됩니다.

button.setOnTouchListener { _, event ->
    Log.d("Touch", "Material3 버튼 터치 이벤트 발생")
    true  // 이벤트를 여기에서 소비 (부모로 전달되지 않음)
}

결과

  • 이벤트가 button에서 소비되며, 상위 ViewGroup으로 전달되지 않음.
  • 이 경우, 스크롤 뷰 안에서 버튼이 눌리지 않는 문제가 발생할 수도 있음.

5. 결론

Material3에서 onTouchEvent()가 기본적으로 false를 반환하는 이유는?

  1. 터치 이벤트를 부모(ViewGroup)로 전달하여 스크롤 등 다른 제스처를 방해하지 않기 위해서
  2. Material3 내부적으로 GestureDetector를 사용하여 클릭 & 롱클릭 이벤트를 처리하기 때문
  3. RecyclerView, ScrollView 등과의 터치 충돌을 방지하기 위해서

📌 만약 특정 위젯에서 터치 이벤트를 직접 처리하고 싶다면 setOnTouchListener()에서 true를 반환하면 된다! 🚀

from. ChatGPT

0개의 댓글