뷰 기반의 UI를 사용하는 앱의 경우 전체 UI를 한 번에 재작성하지 않는 것이 좋습니다. 이 페이지에서는 새 Compose 요소를 기존 UI에 추가하는 방법을 설명합니다.
공유된 UI 이전
Compose로 점진적으로 이전하는 경우 공유된 UI 요소를 Compose와 뷰 시스템에 모두 사용해야 할 수 있습니다. 예를 들어 앱에 맞춤 CallToActionButton
구성요소가 있으면 Compose와 뷰 기반 화면에 모두 이 구성요소를 사용해야 할 수 있습니다.
Compose에서 공유된 UI 요소는 그 요소가 XML을 사용하여 스타일이 지정된 것인지 아니면 맞춤 뷰인지에 관계없이 앱 전체에서 재사용할 수 있는 컴포저블이 됩니다. 예를 들어 맞춤 클릭 유도 문구 Button
구성요소와 관련해 CallToActionButton
컴포저블을 만들 수 있습니다.
뷰 기반 화면에서 컴포저블을 사용하려면 AbstractComposeView
에서 확장되는 맞춤 뷰 래퍼를 만들어야 합니다. 재정의된 Content
컴포저블의 경우 생성한 컴포저블을 아래 예에서와 같이 Compose 테마에 래핑된 상태로 둡니다.
@Composable fun CallToActionButton( text: String, onClick: () -> Unit, modifier: Modifier = Modifier, ) { Button( colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary ), onClick = onClick, modifier = modifier, ) { Text(text) } } class CallToActionViewButton @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 ) : AbstractComposeView(context, attrs, defStyle) { var text by mutableStateOf("") var onClick by mutableStateOf({}) @Composable override fun Content() { YourAppTheme { CallToActionButton(text, onClick) } } }
컴포저블 매개변수가 맞춤 뷰 내에서 변경 가능한 변수가 되는 것을 알 수 있습니다. 이렇게 하면 맞춤 CallToActionViewButton
뷰가 기존 뷰처럼 뷰 결합 등을 통해 확장 및 사용 가능하게 됩니다. 아래 예를 참고하시기 바랍니다.
class ViewBindingActivity : ComponentActivity() { private lateinit var binding: ActivityExampleBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) binding.callToAction.apply { text = getString(R.string.greeting) onClick = { /* Do something */ } } } }
맞춤 구성요소에 변경 가능한 상태가 포함된 경우 상태 정보 소스를 참고하세요.
앱 테마 이전
Android 앱 테마 설정을 위한 권장되는 디자인 시스템은 Material Design입니다.
뷰 기반 앱은 다음 세 가지 버전의 Material을 사용할 수 있습니다.
- AppCompat 라이브러리를 사용하는 Material Design 1(
Theme.AppCompat.*
) - MDC-Android 라이브러리를 사용하는 Material Design 2(
Theme.MaterialComponents.*
) - MDC-Android 라이브러리를 사용하는 Material Design 3(
Theme.Material3.*
)
Compose 앱은 다음 두 가지 버전의 Material을 사용할 수 있습니다.
- Compose Material 라이브러리를 사용하는 Material Design 2(
androidx.compose.material.MaterialTheme
) - Compose Material 3 라이브러리를 사용하는 Material Design 3(
androidx.compose.material3.MaterialTheme
)
앱의 디자인 시스템이 지원하는 경우 최신 버전(Material 3)을 사용하는 것이 좋습니다. 뷰 및 Compose를 위한 이전 가이드가 준비되어 있습니다.
Compose에서 새 화면을 만들 때는 사용 중인 Material Design 버전과 관계없이 먼저 MaterialTheme
을 적용한 후에 Compose Material 라이브러리에서 UI를 내보내는 컴포저블을 적용해야 합니다. Material 구성요소(Button
, Text
등)는 설정된 MaterialTheme
에 종속되며 동작도 이 항목이 없으면 정의되지 않습니다.
모든 Jetpack Compose 샘플은 MaterialTheme
을 기반으로 빌드된 맞춤 Compose 테마를 사용합니다.
자세한 내용은 Compose의 디자인 시스템 및 Compose로 XML 테마 이전을 참고하세요.
WindowInsets 및 IME 애니메이션
Compose 1.2.0부터 수정자를 사용하여 레이아웃 내에 있는 WindowInsets
를 처리할 수 있습니다. IME 애니메이션도 지원됩니다.
class ExampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MaterialTheme {
MyScreen()
}
}
}
}
@Composable
fun MyScreen() {
Box {
LazyColumn(
modifier = Modifier
.fillMaxSize() // fill the entire window
.imePadding() // padding for the bottom for the IME
.imeNestedScroll(), // scroll IME at the bottom
content = { }
)
FloatingActionButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp) // normal 16dp of padding for FABs
.navigationBarsPadding() // padding for navigation bar
.imePadding(), // padding for when IME appears
onClick = { }
) {
Icon( /* ... */)
}
}
}
그림 2. IME 애니메이션
프레젠테이션에서 분할 상태 우선순위 지정
기본적으로 View
는 스테이트풀(Stateful)입니다. View
는 표시할 내용뿐만 아니라 그 내용을 표시할 방법을 설명하는 필드를 관리합니다. View
를 Compose로 변환할 때는 상태 호이스팅에서 설명한 것처럼 단방향 데이터 흐름이 되도록 렌더링되는 데이터를 분리해야 합니다.
예를 들어, View
에는 뷰가 표시되는지, 표시되지 않는지, 사라졌는지를 나타내는 visibility
속성이 있습니다. 이 속성은 View
의 고유한 속성입니다. 코드의 다른 부분에서 View
의 공개 상태를 변경할 수도 있지만, View
자체만 현재 공개 상태를 알 수 있습니다. View
가 표시되도록 하는 로직은 오류가 발생하기 쉽고 종종 View
자체에 연결됩니다.
반면, Compose를 사용하면 Kotlin의 조건부 로직을 사용하여 완전히 다른 컴포저블을 쉽게 표시할 수 있습니다.
if (showCautionIcon) {
CautionIcon(/* ... */)
}
설계상 CautionIcon
은 자신이 표시되고 있는 이유를 알 필요도 없고 관리할 필요도 없으며, 컴포지션 내에 위치하는지 여부를 나타내는 visibility
에 관한 개념도 없습니다.
상태 관리와 프레젠테이션 로직을 명확하게 구분하여 상태를 UI로 변환하는 것처럼 콘텐츠 표시 방식을 더 자유롭게 변경할 수 있습니다. 상태 소유권이 더 유연하므로 필요한 경우 상태를 호이스팅할 수 있으면 컴포저블의 재사용 가능성이 커집니다.
구성요소의 캡슐화 및 재사용 촉진
View
요소는 종종 자신이 Activity
, Dialog
, Fragment
내에 있는지 또는 다른 View
계층 구조 내에 있는지 알 수 있습니다. 이러한 요소는 정적 레이아웃 파일에서 확장되는 경우가 많으므로 View
의 전체 구조는 매우 견고합니다. 그 결과 결합이 더 긴밀해지고 View
를 변경하거나 재사용하기가 더 어려워집니다.
예를 들어, 맞춤 View
가 특정 ID를 가진 특정 유형의 하위 뷰를 갖는다고 가정하고 어떤 작업의 응답으로 속성을 직접 변경할 수 있습니다. 이는 이러한 View
요소를 함께 긴밀히 결합합니다. 맞춤 View
는 하위 요소를 찾지 못하면 다운되거나 손상될 수 있고 하위 요소는 상위 맞춤 View
가 없어서 재사용되지 못할 가능성이 있습니다.
재사용 가능한 컴포저블을 사용하는 Compose에서는 거의 문제가 되지 않습니다. 상위 요소는 상태와 콜백을 쉽게 지정할 수 있으므로, 재사용 가능한 컴포저블의 정확한 사용 위치를 모르더라도 이 컴포저블을 작성할 수 있습니다.
var isEnabled by rememberSaveable { mutableStateOf(false) }
Column {
ImageWithEnabledOverlay(isEnabled)
ControlPanelWithToggle(
isEnabled = isEnabled,
onEnabledChanged = { isEnabled = it }
)
}
위의 예에서는 세 부분이 모두 더 캡슐화되어 덜 결합됩니다.
ImageWithEnabledOverlay
는 현재isEnabled
상태만 알면 됩니다.ControlPanelWithToggle
의 존재 여부와 제어 방법은 알 필요가 없습니다.ControlPanelWithToggle
은ImageWithEnabledOverlay
가 있는지 알 수 없습니다.isEnabled
를 표시하는 방법은 0개, 1개 또는 그 이상 있을 수 있으며ControlPanelWithToggle
은 변경할 필요가 없습니다.ImageWithEnabledOverlay
또는ControlPanelWithToggle
이 얼마나 깊이 중첩되었는지는 상위 요소에 중요하지 않습니다. 이러한 하위 요소는 변경사항을 애니메이션 처리하거나 콘텐츠를 바꾸거나 다른 하위 요소에 콘텐츠를 전달할 수 있습니다.
이 패턴을 컨트롤 반전이라고 하며, CompositionLocal
문서에서 자세히 알아볼 수 있습니다.
화면 크기 변경 처리
다양한 창 크기에 따라 다른 리소스를 사용하는 것은 반응형 View
레이아웃을 만드는 주요 방법 중 하나입니다. 정규화된 리소스는 여전히 화면 수준 레이아웃을 결정하는 옵션이지만, Compose를 사용하면 일반적인 조건부 로직으로 코드에서 레이아웃을 전체적으로 훨씬 더 쉽게 변경할 수 있습니다. 자세한 내용은 다양한 화면 크기 지원을 참고하세요.
또한 Compose에서 제공하는 적응형 UI 빌드 기법에 관한 자세한 내용은 적응형 레이아웃 빌드를 참고하세요.
뷰를 사용한 중첩 스크롤
양방향으로 중첩되어 있으며 스크롤 가능한 뷰 요소와 컴포저블 간에 중첩 스크롤 상호 운용성을 사용 설정하는 방법에 관한 자세한 내용은 중첩 스크롤 상호 운용성을 참고하세요.
RecyclerView의 Compose
RecyclerView의 컴포저블은 RecyclerView 버전 1.3.0-alpha02부터 뛰어난 성능을 발휘합니다. 이러한 이점을 확인하려면 1.3.0-alpha02 버전 이상의 RecyclerView를 사용해야 합니다.