Home Android: ComposeView inside CoordinatorLayout (ComposeView CoordinatorLayout에서 사용하기)
Post
Cancel

Android: ComposeView inside CoordinatorLayout (ComposeView CoordinatorLayout에서 사용하기)

compose-with-coordinator

ComposeView inside CoordinatorLayout

100% ComposeView로 작성한 앱은 발생하지 않을 문제이긴하나 위 레이아웃 구조 처럼 AndroidViewComposeView를 섞어서쓰는경우 ComposeView에서 발생한 scrollView 이벤트를 CoordiantorLayout에 전파하는 방법에대해 정리

Touch propagation

AndroidView

1
2
3
4
5
6
7
8
9
10
main doCallback
- MessageQueue.next
 - InputEventRecevier.dispatchInputEvent()
  - ViewRoot.enqueueEvent()
   - View.dispatchPointerEvent()
    - DecorView.dispatchTouchEvent()
     - Activity.dispatchTouchEvent()
      - ViewGroup.dispatchTouchEvent()
       - TargetView.onInterceptTouchEvent()
        - if nested widget THEN startNestedScroll()

dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent 의 chaning 으로 처리되는 touch event 자세한 내용은 skip

ComposeView dispatchTouch

1
2
3
4
5
6
7
8
9
10
11
12
13
main doCallback
- MessageQueue.next
 - InputEventRecevier.dispatchInputEvent()
  - ViewRoot.enqueueEvent()
   - View.dispatchPointerEvent()
    - DecorView.dispatchTouchEvent()
     - Activity.dispatchTouchEvent()
      - ViewGroup.dispatchTouchEvent()
       - AndroidComposeView
        - dispatchTouchEvent
        - handleTouchEvent
        - PointerInputEventProcesser.process
        - ...

AndroidComposeView 까지의 touch propagation과정은 동일하나 이후 Compose framework에서 자체적으로 제공되는 touchEvent 처리방식을 통해 이벤트 처리가 진행된다. 해당 방식과 AndroidView의 touchEvent를 처리하는 방식은 data struct부터 touch event를 처리하는 value 등 다른게 구현된 부분이 대부분이며 AndroidView와의 compatibility를 지원하지 않는다.

문제점

ComposeView의 nestedScroll 동작이 필요하다.

해결방안 (proto typing LazyColumn)

단순 가능여부를 판단하기위한 테스트이며 실제 프로덕트에 적용하면 사이드 발생가능성이…

가장 좋은방법은 100% ComposeView를 활용하여 구현하는것이다. (마이그레이션은..?)

dispatch compose nested scroll event

LazyColumn, Pager등에서 사용되는 NestedScrollConnectionNestedScrollingChildHelper를 구현하여 테스트 진행.

Compose의 touch event를 처리하는데 사용하는 값(consumed, available 등)은 AndroidView와 반대로 계산된다. 따라서, -값으로 childHelper에 전달.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
val listState = rememberLazyListState()
val childHelper = remember {
  NestedScrollingChildHelper(requireView()).apply {
    isNestedScrollingEnabled = true
  }
}
if (listState.isScrollInProgress) {
  DisposableEffect(Unit) {
    childHelper.startNestedScroll(SCROLL_AXIS_VERTICAL)

    onDispose {
      childHelper.stopNestedScroll()
    }
  }
}
val scrollConnection = remember {
  object : NestedScrollConnection {
    override suspend fun onPostFling(
      consumed: Velocity,
      available: Velocity
    ): Velocity {
      childHelper.dispatchNestedFling(
        -consumed.x,
        -consumed.y,
        false
      )
      return super.onPostFling(consumed, available)
    }

    override fun onPostScroll(
      consumed: Offset,
      available: Offset,
      source: NestedScrollSource
    ): Offset {
      childHelper.dispatchNestedScroll(
        -consumed.x.toInt(),
        -consumed.y.toInt(),
        -available.x.toInt(),
        -available.y.toInt(),
        null
      )
      return super.onPostScroll(consumed, available, source)
    }

    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
      childHelper.dispatchNestedPreScroll(
        -available.x.toInt(),
        -available.y.toInt(),
        reusableIntPair,
        null
      )
      return super.onPreScroll(available, source)
    }

    override suspend fun onPreFling(available: Velocity): Velocity {
      childHelper.dispatchNestedPreFling(
        -available.x,
        -available.y,
      )
      return super.onPreFling(available)
    }
  }
}

AppBar, BottomNav behaviour등은 문제없이 동작하는 부분은 확인하였으나 ExpandableToolbar등의 동작은 확인하지 못하였다.

This post is licensed under CC BY 4.0 by the author.

Android: Nested Coordinator Layout (중첩 CoordinatorLayout)

Android: Compose Custom Layout - Basic