ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [UI] Touch Event (1) - 터치 이벤트 전달 과정
    Android/UI 2021. 9. 15. 19:46

    참고한 블로그  https://readystory.tistory.com/185

     

    [Android] 안드로이드의 Touch Event 는 어떻게 전달 될까? (with. Touch Intercept)

    사용자와의 상호작용(Interaction)을 처리하는 것은 모바일 프로그래밍에서 굉장히 중요합니다. 안드로이드 애플리케이션은 기본적으로 Activity를 통해 화면을 구성하며, 사용자는 화면을 터치함으

    readystory.tistory.com

     

    안드로이드에서 기본적으로 터치 이벤트를 어떻게 처리하는지에 대해서 공부중이다.

    터치 이벤트의 전달과정의 이해가 필요하였으며, 터치 이벤트에 대해 클릭과 롱클릭등이 어떻게 동작하는지 확인하는 것이 목표이며 이번편에서는 기본적인 전달과 처리과정만 들여다본다.

    https://readystory.tistory.com/185
    https://readystory.tistory.com/185

    과정을 확인하기 위한 View배치 (동적으로)

     

    초록- 루트뷰의 자식으로 Outer레이아웃(ViewGroup)추가

    검정- Outer레이아웃의 자식으로 Inner레이아웃(ViewGroup)추가

    빨강- Inner레이아웃의 자식으로 View추가


    • OuterCustomLayout (커스텀 프레임 레이아웃) - ViewGroup에 해당
    import android.content.Context
    import android.util.Log
    import android.view.MotionEvent
    import android.widget.FrameLayout
    
    class OuterCustomLayout(context: Context) : FrameLayout(context) {
        override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "called dispatchTouchEvent() in Outer 레이아웃")
            return super.dispatchTouchEvent(ev)
        }
    
        override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "called onInterceptTouchEvent() in Outer 레이아웃")
            Log.d("TouchEventTest", "return: ${super.onInterceptTouchEvent(ev)}(하위 전달x true ,하위 전달o false)")
            return super.onInterceptTouchEvent(ev)
    
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "Outer 레이아웃 터치 이벤트 핸들")
            return super.onTouchEvent(event)
        }
    
    }

     

    • InnerCustomLayout (커스텀 프레임 레이아웃) - ViewGroup에 해당

    OuterCustomLayout과 같고 로그 출력만 다름

    import android.content.Context
    import android.util.Log
    import android.view.MotionEvent
    import android.widget.FrameLayout
    
    class InnerCustomLayout(context: Context) : FrameLayout(context) {
        override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "called dispatchTouchEvent() in Inner 레이아웃")
            return super.dispatchTouchEvent(ev)
        }
    
        override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "called onInterceptTouchEvent() in Inner 레이아웃")
            Log.d("TouchEventTest", "return: ${super.onInterceptTouchEvent(ev)}(하위 전달x true ,하위 전달o false)")
            return super.onInterceptTouchEvent(ev)
    
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "Inner 레이아웃 터치 이벤트 핸들")
            return super.onTouchEvent(event)
        }
    
        fun log(str: String) {
            Log.d("TouchEventTest", str)
        }
    }

     

    • CustomView (커스텀 텍스트뷰) - View에 해당
    import android.content.Context
    import android.util.Log
    import android.view.MotionEvent
    import androidx.appcompat.widget.AppCompatTextView
    
    class CustomView(context: Context) : AppCompatTextView(context) {
        override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "called dispatchTouchEvent() in CustomView")
            return super.dispatchTouchEvent(ev)
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "View 터치 이벤트 핸들")
            return super.onTouchEvent(event)
        }
    
    }

     

    • MainActivity
    import android.graphics.Color
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.widget.FrameLayout
    
    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val outer = OuterCustomLayout(this) //바깥 레이아웃
            outer.setBackgroundColor(Color.GREEN)
            addContentView(outer, FrameLayout.LayoutParams(400, 400))
    
            val inner = InnerCustomLayout(this) //중간 레이아웃
            inner.setBackgroundColor(Color.BLACK) 
            outer.addView(inner,200,200)
    
            val customView = CustomView(this) //중간 레이아웃 내부 뷰
            customView.text = "TextView"
            customView.setBackgroundColor(Color.RED) 
            inner.addView(customView,100,100)
        }
    
    }

     

    dispatchTouchEvent (Activity,ViewGroup,View에 존재)

    onInterceptTouchEvent (ViewGroup에만 존재)

    onTouchEvent( Activity,ViewGroup, View에 존재)

     

    dispatchTouchEvent는 터치 이벤트의 전달( 전달시킬 것인지 아닌지는 super.~에 의해 정해짐)

    onInterceptTouchEvent는 터치 이벤트를 가로챌것인지 유뮤

    (리턴 true이면 가로채서 하위로 전달되지 않음, false이면 하위로 전달됨, 이 역시 super에 의해 정해짐)

    onTouchEvent는 현재 View,ViewGroup에서 터치 이벤트를 처리하고 남은 터치 이벤트 처리 과정이 있는지 유무

     

    어려우니까 밑의 테스트를 보자.

     

    먼저 안드로이드에서 기본동작으로 어떻게 처리하는지 확인해봤다.

    ( 모든 메서드의 return값을 인위적으로 설정하지 않고 기본동작인 super로 그대로 설정 )

     

    1. 빈 공간 터치

    Outer레이아웃의 바깥 흰 공간을 터치하면 로그에 아무것도 찍히지 않게되는데

    액티비티에서 activity_main의 루트인 LinearLayout에 터치 이벤트를 전달하고

    LinearLayout에서 Outer레이아웃에 터치이벤트를 전달하지 않아서 그렇다. 

    (루트인 LinearLayout에는 로그를 안남겨 찍히지 않았다)

    Outer레이아웃에 해당하지도 않는 영역에서 사용자가 터치를 했기 때문에 Outer레이아웃에 터치 이벤트를 전달하지 않는 것이다. 이렇게 super에의해 자동으로 적절히 처리되는 것 같다.

     

    2. Outer레이아웃 터치(초록)

    하위인 Inner레이아웃에 터치 이벤트가 전달되지 않는다.

    (Inner레이아웃에 전달이 안되기 때문에 View에도 전달 될수없다)

    터치 이벤트를 받은 Outer레이아웃이 onTouchEvent를 통해 터치 이벤트를 핸들하였다.

    터치 이벤트 핸들 과정은 밑에서 확인해본다. 디스패치 과정만 확인하자.

     

    3. Inner레이아웃 터치(검정)

    Inner레이아웃에서 터치 이벤트 전달이 끊기고 자식인 View에게 터치 이벤트가 전달되지 않는다.

    터치 이벤트 핸들인 onTouchEvent은 dispatch와 반대로 하위계층부터 처리된다.

     

    4. View터치 (TextView 빨강)

    기본 동작은 이렇고 onInterceptTouchEvent를 true로 하여 터치 이벤트를 가로채보자


    Outer레이아웃에서 터치 이벤트를 하위로 전달하지 않게 true로 변경

    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
            Log.d("TouchEventTest", "called onInterceptTouchEvent() in Outer 레이아웃")
            Log.d("TouchEventTest", "return: true 로 설정")
            return true
            //Log.d("TouchEventTest", "return: ${super.onInterceptTouchEvent(ev)}(하위 전달x true ,하위 전달o false)")
            //return super.onInterceptTouchEvent(ev)
    
        }

    Outer레이아웃 터치

    Inner레이아웃 터치

    Inner레이아웃을 터치 하였는데도 Outer레이아웃에서 터치 이벤트를 가로채서 이벤트를 전달 받을 수 없다.

    View를 터치해도 다음과 같다.


    이제 onTouchEvent의 리턴값을 조절해보자.

    Outer레이아웃의 onInterceptTouchEvent를 다시 기본동작인 상태로 돌려놓자.

     

    CustomView의 onTouchEvent의 리턴값을 true로 바꾸고

    CustomView를 터치하면 모든 계층에 터치 이벤트가 전달이 되었지만 CustomView를 제외한 Inner, Outer레이아웃의 터치 이벤트 처리는 되지 않는다.

    onTouchEvent의 경우 디스패치와 반대로 하위계층부터 터치 이벤트를 처리하고 상위 계층으로 올라가면서 남은 터치 이벤트를 처리하는데 true로 설정하면 이 터치 이벤트 처리가 완료되어 남은 처리과정이 없다는 말이된다.

     

    좀더 복잡하게 과정을 확인하고 싶어서 Inner레이아웃에서 인위적으로 true로 터치 이벤트 전달을 끊고

    View의 onTouchEvent를 true로 설정해봤다.

     

    Inner레이아웃 (InnerCustomLayout) 만 변경

    View의 onTouchEvent의 true는 그대로 놔둔다.

    CustomView를 터치하면 다음과 같이된다.

    CustomView의 onTouchEvent가 true이지만 Inner레이아웃의 가로챔이 true라서 CustomView에 터치 이벤트가 전달되지 않아 onTouchEvent또한 호출될 기회가 없어지기 때문에 Inner레이아웃, Outer레이아웃 순서대로 터치 이벤트가 처리된다.


    dispatchTouchEvent의 동작을 보고 싶었는데 Outer레이아웃값을 false로 설정하고 Outer,Inner레이아웃, View모두 터치한 결과 Outer의 onInterceptTouchEvent메서드가 호출되지 않는다. 

     

    더 궁금해져서 Outer레이아웃의 dispatchTouchEvent를 원상복귀시키고 Inner레이아웃의 dispatchTouchEvent의 return값을 false로 한후 Inner레이아웃을 터치하니 

    Outer레이아웃 터치 이벤트 핸들만 된다.

     

    onInterceptTouchEvent를 true로 하였을 때와의 차이가 있었다.

    Inner 레이아웃 터치 이벤트 핸들

    Outer 레이아웃 터치 이벤트 핸들

     

     

    다음 포스팅내용

     

    MotionEvent.ACTION_DOWN , ACTION_MOVE, ACTION_UP과 클릭 이벤트

    'Android > UI' 카테고리의 다른 글

    [UI] 스타일과 테마 - 1  (0) 2021.09.24
    [UI] Context Menu 사용법  (0) 2021.09.18
    [UI] Touch Event (2) - 터치 이벤트의 시작과 끝  (0) 2021.09.16
    [UI] RecyclerView 공부  (0) 2021.07.31
    [UI] 이미지 관련  (0) 2021.07.28

    댓글

Designed by Tistory.