일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 안드로이드
- Python
- 힐트
- material3
- 레트로핏
- Could not find method
- multiseekbar
- rangeslider
- 지오코더
- isFinishing()
- 원생성
- 안드로이드 다이얼로그 오류
- 도형생성
- onIntercepterTouchEvent
- Android
- android compose
- dualthumbseekbar
- Java
- android.view.WindowManager.BadTokenException
- 원이동
- 듀얼시크바
- 터치순서
- databinding xml
- @provides @binds 차이
- 터치이벤트 순서
- [databinding]
- databinding error
- dispatchTouchEvent
- WindowManager$BadTokenException
- {"msg":"cannot find method
- Today
- Total
개발관련일지
안드로이드 뷰의 터치이벤트 순서(touch event ) 본문
뷰가 겹쳐져있는 상황이 발생했고 그중에서 특정 뷰가 터치 되었을 시 이벤트를 받아야할 상황이 생겨서 찾아본 내용을 정리
안드로이드에서 xml로 사용자에게 보여줄 화면을 만들게 될 시 View(UI Widget) 와 ViewGroup(linear , frame , constraint etc) 객체를 이용해서 만든다. 뷰그룹 안에 뷰그룹 혹은 뷰를 배치하게 되며 유저가 화면에서 뷰그룹 혹은 뷰를 터치했을 경우 클래스에서 어떤 흐름으로 코드가 동작하는지 찾아보았다
스택오버플로우에서 확인 한 내용의 경우 엑티비티 -> 뷰그룹 -> 뷰 순으로 터치가 코드가 동작한다고 나와있었으며 직관적인 그림이 나와있었다
우측 사진의 왼쪽화살표를 보면 알 수 있듯이 상위뷰 먼저 dispatchTouchEvent() -> onIntercepterTouchEvent()(뷰그룹일 경우) 으로 각 뷰마다 진행 후 다음 자식 뷰 혹은 뷰그룹도 동일하게 진행된다 터치 이벤트가 다 전달되면 반대로 자식뷰부터 onTouchEvent가 상위뷰로 이동하면서 동작하게된다. 글의 설명보다 사진이 엄청 직관적이며 포함되어있는 메소드가 어떤건지 알아보았다.
dispatchTouchEvent() -> dispatch(파견하다 , 발송하다) + TouchEvent
디벨로퍼보단 스택오버플로우 글의 설명이 좀더 쉽게 느껴졌으며 터치 이벤트가 발생했다고 알려주는 역할이다. 코드를 보게되면 MotionEvent(모션 움직임 포인터)에 대한 정보를 담고있는 객체)도 같이 넘어오게 된다.
리턴값을 간단하게 설명하면 트루면 흐름이 멈춘다고 생각하면된다.
onIntercepterTouchEvent() -> interceptor(훔치다 , 가로채다) + TouchEvent
dispatchTouchEvent와 다르게 뷰에 선언되어있지 않고 "뷰그룹"에서만 사용하는 메소드이다
/**Implement this method to intercept all touch screen motion events.
해당 메소드를 선언하세요 모든 스크린 터치 이벤트를 가로채기 위해서
This
* allows you to watch events as they are dispatched to your children,
이건 이벤트를 관찰할 수 있어요 자식에게 보내지는걸
and
* take ownership of the current gesture at any point.
그리고 가지세요 현재 제스처의 소유를 특정 포인트에
주석 첫번째 문장을 가져오면 이렇게 적혀있다. 자식에게 넘어가는 이벤트를 터치 이벤트를 뺏어오고 싶으면 사용하라 는거다.
터치가 있다고 알려주는 위의 사진에서 왼쪽 흐름이 끝나고 우측 위로올라가는 화살표가 진행된다 보면된다.
onIntercepterTouchEvent와 비슷한게 하나 더있다. requestDisallowInterceptTouchEvent() 라는거다.
보통 스크롤뷰 -> 뷰 구조일경우 스크롤뷰가 터치를 뺏어가니까 뷰의 터치를 따로 사용하고 싶을떄 사용한다. "스크롤뷰 내 뷰 터치"라고 아래와 같은 느낌의 코드를 쉽게 찾고 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
뷰.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow ScrollView to intercept touch events.
부모뷰.requestDisallowInterceptTouchEvent(true);
// Disable touch on transparent view
return false;
case MotionEvent.ACTION_UP:
// Allow ScrollView to intercept touch events.
부모뷰.requestDisallowInterceptTouchEvent(false);
return true;
case MotionEvent.ACTION_MOVE:
부모뷰.requestDisallowInterceptTouchEvent(true);
return false;
default:
return true;
}
}
});
|
cs |
이렇게 사용하면 바로 터치를 가져올 수 있다.
onIntercepterTouchEvent를 알고나니 어떻게 터치를 뻇어 올 수 있었는지를 이해할 수 있었다.
코드를 만들어서 테스트를 해보았다.
↓ 파란색 -> 컨스트레인트
↓ 빨간색 -> 컨스트레인트
↓ 초록색 -> 텍스트뷰
의 구조로 되어잇다.
초록색 부분을 클릭하게되면
스택오버플로우의 설명 사진처럼 동작하는 것을 확인 할 수 있다.
사용한 코드
1. 첫번째 뷰 - 파란색
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class FirstLayout @JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr){
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.d("touchEvent", "FirstLayout dispatchTouchEvent")
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
Log.d("touchEvent", "FirstLayout onInterceptTouchEvent")
return super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.d("touchEvent", "FirstLayout onTouchEvent")
return super.onTouchEvent(event)
}
}
|
cs |
2. 두번째 뷰 - 빨간색
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class SecLayout @JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr){
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.e("touchEvent" , "SecLayout dispatchTouchEvent" )
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
Log.e("touchEvent" , "SecLayout onInterceptTouchEvent" )
// return super.onInterceptTouchEvent(ev)
return false
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.e("touchEvent" , "SecLayout onTouchEvent" )
return super.onTouchEvent(event)
}
}
|
cs |
3. 세번째뷰 - 초록색
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class ChildView : androidx.appcompat.widget.AppCompatTextView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
//@JvmOverloads
//constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : MaterialButton(context, attrs, defStyleAttr){
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
Log.i("touchEvent" , "ChildView dispatchTouchEvent" )
return super.dispatchTouchEvent(event)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.i("touchEvent" , "ChildView onTouchEvent")
return super.onTouchEvent(event)
}
}
|
cs |
4. xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".touchEvent.TouchEventCheckActivity">
<com.example.androidnote.touchEvent.view.FirstLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.7"
app:layout_constraintHeight_percent="0.7"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#6897BB"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="첫번째 뷰그룹"
android:textColor="@color/white"
android:textSize="20dp"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.androidnote.touchEvent.view.SecLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent="0.7"
app:layout_constraintHeight_percent="0.7"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="#FF6B68"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="두번째 뷰그룹"
android:textColor="@color/white"
android:textSize="20dp"
android:layout_marginTop="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.androidnote.touchEvent.view.ChildView
android:id="@+id/childBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="두번째 뷰그룹 안에\n위치한 텍스트뷰"
android:gravity="center_horizontal"
android:background="#6A8759"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.example.androidnote.touchEvent.view.SecLayout>
</com.example.androidnote.touchEvent.view.FirstLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
각 뷰들의 메소드에 리턴값을 트루로 놓으면서 실제 동작하는 코드들을 테스트 해 볼 수 있었다.
참고
https://stackoverflow.com/questions/7449799/how-are-android-touch-events-delivered
'개발기록 > 안드로이드' 카테고리의 다른 글
안드로이드 내부저장소 외부저장소 이해한것 정리 (0) | 2021.07.20 |
---|---|
안드로이드 WindowManager$BadTokenException 오류 해결 (0) | 2021.07.08 |
안드로이드 캔버스에 도형(원) 생성하고 움직이기 (0) | 2021.05.29 |
안드로이드 위도경도를 이용해 구글 Geocoder로 국내주소 구하기 (1) | 2021.01.10 |
안드로이드 java] dual/multi thumbs seekbar -> RangeSlider를 이용해 만들기 (0) | 2021.01.03 |