단계별 수정자 분석

Jetpack Compose는 여러 단계에 걸쳐 수정자를 실행합니다. 다음 분석은 일반적인 수정자가 속한 단계와 인수 또는 람다가 평가되는 시점을 보여줍니다.

1단계: 컴포지션 단계

이러한 수정자는 @Composable 본문 내에서 동기적으로 인수를 평가합니다. 여기서 읽은 상태는 컴포저블 다시 시작 범위에 종속 항목을 등록하고 변경되면 전체 리컴포지션을 트리거합니다.

  • 정적 측정기준 및 크기 조절 수정자:
    • Modifier.size(dp), Modifier.width(dp), Modifier.height(dp)
    • Modifier.fillMaxSize(), Modifier.fillMaxWidth(), Modifier.fillMaxHeight()
    • Modifier.wrapContentHeight() Modifier.wrapContentWidth() Modifier.wrapContentSize()
    • Modifier.defaultMinSize() Modifier.requiredSize()
    • Modifier.aspectRatio(ratio)
    • Modifier.weight(weight) ( RowScope 또는 ColumnScope)
    • Modifier.width(IntrinsicSize.Min / Max)
    • Modifier.height(IntrinsicSize.Min / Max)
  • 정적 간격 및 인셋:
    • Modifier.padding(start, top, end, bottom) Modifier.padding(all = dp)
    • Modifier.statusBarsPadding() Modifier.windowInsetsPadding(insets)
  • 직접 값 시각적 수정 도구:
    • Modifier.alpha(float) (컴포지션 중에 상태를 읽음)
    • Modifier.rotate(degrees) (컴포지션 중에 상태를 읽음)
    • Modifier.scale(scaleX, scaleY) (컴포지션 중에 상태를 읽음)
    • Modifier.background(color = Color, shape = Shape)
    • Modifier.border(width, color, shape)
    • Modifier.shadow(elevation, shape)
    • Modifier.clip(shape)
    • Modifier.blur(radius)
  • 정적 위치 수정자:
    • Modifier.offset(x = dp, y = dp) (직접 값 오버로드)
    • Modifier.absoluteOffset(x = dp, y = dp)
  • 동적 컴포저블 수정자:
    • Modifier.composed { ... } (컴포지션 중에 팩토리 본문을 실행함)

2단계: 레이아웃 단계

레이아웃 단계는 별도의 무효화 범위가 있는 두 개의 개별 하위 단계로 나뉩니다.

2A단계: 측정

크기 (width, height)와 제약 조건을 결정합니다. 여기에서 상태를 읽으면 재측정 및 재배치가 트리거됩니다.

  • Modifier.layout { measurable, constraints -> ... }: layout(w, h) 전의 코드는 측정 중에 실행됩니다.

2B단계: 배치 및 레이아웃 콜백

(x, y) 좌표 위치를 결정합니다. 여기에서 상태를 읽으면 재배치만 트리거됩니다 (측정 및 컴포지션 건너뛰기).

  • Modifier.offset { IntOffset(x, y) } (람다 오버로드)
  • Modifier.absoluteOffset { IntOffset(x, y) } (람다 오버로드)
  • Modifier.layout { ... layout(w, h) { placeable.place(x, y) } }: layout(w, h) { ... } 블록 내의 코드입니다.
  • Modifier.onPlaced { layoutCoordinates -> ... }: 배치 중에 상위 좌표가 결정된 후 호출됩니다.
  • Modifier.onSizeChanged { intSize -> ... }: 크기가 변경된 경우 레이아웃 측정 완료 후 호출됩니다.
  • Modifier.onGloballyPositioned { layoutCoordinates -> ... }: 전체 창 레이아웃 패스가 완료된 후 호출됩니다.

3단계: 그리기 단계

이러한 수정자는 캔버스 그리기 중에 실행됩니다. 이러한 블록 내에서 상태를 읽으면 컴포지션과 레이아웃 모두가 건너뛰고 그리기 패스만 실행됩니다.

  • Modifier.graphicsLayer { ... }:
    • 람다에서 업데이트된 속성: alpha, translationX, translationY, scaleX, scaleY, rotationX, rotationY, rotationZ, shadowElevation, shape, clip, renderEffect, transformOrigin
  • Modifier.drawBehind { ... }:
    • 컴포저블 콘텐츠 뒤에 캔버스 명령어를 그립니다.
  • Modifier.drawWithContent { ... }:
    • drawContent()로 그리기 순서를 제어합니다.
  • Modifier.drawWithCache { ... }:
    • 외부 블록은 크기 또는 레이아웃 변경 시 실행되어 Path, Paint 또는 Brush 객체를 캐시합니다. onDrawBehind 또는 onDrawWithContent는 각 프레임 그리기에서 실행됩니다.

리컴포지션 루프 외부 - 관찰 수정자

일부 수정자는 접근성, 사용자 입력, 수명 주기 관찰을 처리하기 위해 세 프레임 단계 외부에서 작동합니다.

시맨틱 및 접근성

접근성 서비스 (TalkBack) 및 테스트를 위한 시맨틱 속성을 처리합니다.

  • Modifier.semantics { ... }
  • Modifier.clearAndSetSemantics { ... }

입력, 동작, 이벤트

3단계 프레임 렌더링 파이프라인 외부에서 작동하며 OS 동작 이벤트와 사용자 상호작용을 수신합니다.

  • Modifier.pointerInput(key) { detectTapGestures { ... } } / detectDragGestures / awaitPointerEventScope
  • Modifier.clickable(enabled, onClickLabel) { ... }
  • Modifier.combinedClickable { ... }
  • Modifier.toggleable { ... }
  • Modifier.selectable { ... }
  • Modifier.draggable(...) / Modifier.scrollable(...) / Modifier.draggable2D(...)
  • Modifier.onFocusChanged { focusState -> ... }
  • Modifier.onKeyEvent { keyEvent -> ... }
  • Modifier.onVisibilityChanged { isVisible -> ... }: 콜백은 코루틴에서 구성된 지연 후 컴포저블의 공개 상태가 변경될 때 호출됩니다.

빠른 참조: 가치 대 람다 단계

람다에서 상태 값을 읽으면 읽기가 후반 단계로 지연되어 상태가 변경될 때 이전 단계를 건너뜁니다.

수정자 / 패턴 직접 가치 (구성) 람다 변형 (지연된 단계) 상태가 변경될 때
오프셋 Modifier.offset(x, y) Modifier.offset { IntOffset(x, y) } 레이아웃 (배치) (컴포지션 건너뛰기)
알파 / 불투명도 Modifier.alpha(alpha) Modifier.graphicsLayer { this.alpha = alpha } 그리기 (구성 및 레이아웃 건너뛰기)
회전 Modifier.rotate(degrees) Modifier.graphicsLayer { rotationZ = degrees } 그리기 (구성 및 레이아웃 건너뛰기)
확장 Modifier.scale(scale) Modifier.graphicsLayer { scaleX = s; scaleY = s } 그리기 (구성 및 레이아웃 건너뛰기)
번역 Modifier.padding(...) Modifier.graphicsLayer { translationX = ... } 그리기 (구성 및 레이아웃 건너뛰기)
배경 색상 Modifier.background(color) Modifier.drawBehind { drawRect(color) } 그리기 (구성 및 레이아웃 건너뛰기)
스크롤 / 시차 Modifier.offset(scroll.dp) Modifier.offset { IntOffset(0, scroll) } 레이아웃 (배치) (컴포지션 건너뛰기)

Modifier.Node 인터페이스 매핑

고성능 Modifier.Node API를 사용하는 맞춤 수정자의 경우 단계가 노드 기능 인터페이스에 직접 매핑됩니다.

단계 Modifier.Node 인터페이스 핵심 메서드
레이아웃 (측정 및 배치) LayoutModifierNode measure(measurable, constraints)
레이아웃 (전역 좌표) GlobalPositionAwareModifierNode onGloballyPositioned(coordinates)
레이아웃 (배치) LayoutAwareModifierNode onPlaced(coordinates) onRemeasured(size)
그리기 DrawModifierNode draw(ContentDrawScope)
입력 / 포인터 PointerInputModifierNode onCancelPointerInput() onPointerEvent(...)
시맨틱 SemanticsModifierNode applySemantics(SemanticsPropertyReceiver)
포커스 FocusEventModifierNode onFocusEvent(focusState)