컴포즈 앱 첫 빌드
화면 크기, 픽셀 밀도, 폼 팩터가 다른 수많은 디바이스 생성 + 안드로이드 뷰 시스템을 이해하기 쉽게 하기 위해 앱 복잡성 증가 + 목록을 스크롤, 애니메이션 추가 시 많은 양의 상용구 코드가 필요하게 됨.
명력적 접근 방식의 UI 툴킷의 문제
리액트, 플러터, SwiftUI 가 선언적 접근 방식 도입.
잿팩 컴포즈 : 구글이 안드로이드용으로 만든 코틀린 전용 선언적 UI 프래임워크.
컴포저블 함수
컴포저블 함수
컴포저블 함수 Composable functions 는 컴포즈 앱의 핵심 구성 요소
@Composable
어노테이션으로 식별
반환타입을 가질 필요가 없으며, 대신 UI 요소를 내보냄.
일반적으로 다른 컴포저블 함수를 호출하는 것으로 끝남.
@Composable
fun Welcome() {
Text(
text = stringResource(id = R.string.welcome),
style = MaterialTheme.typography.bodyMedium,
)
}
Text()
는 기본 내장된 컴포저블 함수.
- text, style 2개의 매개변수를 가짐.style → 텍스트의 외형을 변경
- text → 어떤 문구를 보여줄지 결정
→ Welcome 함수 :: 상태를 갖지 않았다고 한다. stateless
- 매개변수가 있는 컴포저블 함수.textAlign → 텍스트를 가로로 어떻게 배치할 지 명시.
// string.xml <string name="hello">Hello, %1$s. \nNice to meet you.</string>
@Composable fun Greeting(name: String) { Text( text = stringResource(id = R.string.hello, name), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodyMedium, ) }
열, 텍스트 필드, 버튼 사용.
@Composable
fun TextAndButton(
name: MutableState<String>,
nameEntered: MutableState<Boolean>,
) {
Row(modifier = Modifier.padding(top = 8.dp)) {
// ...
}
}
Row()
: 동일선상 아이템 배치. 매개변수 목록을 받을 수 있음. 자식 컴포저블 함수는 중괄호 안에!
Modifier 변경자
: 잿팩 컴포즈의 핵심 기술. 컴포저블 함수의 외형과 행위에 영향을 준다.
Row(modifier = Modifier.padding(top = 8.dp)) {
TextField(
value = name.value,
onValueChange = {
name.value = it
},
placeholder = {
Text(text = stringResource(id = R.string.hint))
},
modifier = Modifier.alignByBaseline().weight(1.0F),
singleLine = true,
keyboardOptions = KeyboardOptions(
autoCorrect = false,
capitalization = KeyboardCapitalization.Words,
),
keyboardActions = KeyboardActions(onAny = {
nameEntered.value = true
}),
) {
}
}
MutableState 객체
: 변경할 수 있는 값이라는 특성을 가짐. name.value, nameEntered.value 처럼 값에 접근할 수 있음.
- TextField() 매개변수
- value → 이미 입력된 텍스트와 같은 텍스트 입력 필드의 현재 값을 인자로 받음.
- onValueChange → 텍스트를 변경하는 일이 발생하는 경우(사용자가 입력하거나 삭제하는 경우) 호출.
- alignByBaseline() → 특정 Row() 내부에서 다른 컴포지션 함수들의 기준선을 정리
- keyboardOptions, keyboardActions → 화면에 나타난 키보드 동작 제어
💡 재구성 Recomposition
MutableState와 같은 특정 타입은 재구성을 유발함.
컴포저블 함수를 다시 그리는 것.
MutableState 타입의 값을 변경하면 TextField() 컴포저블 함수는 다시 그려지거나 다시 채색된다.
@Composable
fun Hello() {
val name = remember { mutableStateOf("") }
val nameEntered = remember { mutableStateOf(false) }
Box(
modifier = Modifier.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center,
) {
if (nameEntered.value) {
Greeting(name = name.value)
} else {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Welcome()
TextAndButton(name = name, nameEntered = nameEntered)
}
}
}
}
Box()
: layout 패키지에 포함. 한 개 이상의 자식 컴포저블 함수를 포함할 수 있음. 자식요소는 contentAlignment 값에 따라 위치함.
remember
, mutableStateOf
: 상태를 생성하고 관리하는 데 중요한 역할.
State 상태
: 일반적으로 앱에서는 상태는 시간이 지남에 따라 변할 수 있는 값과 관련됨.
도메인 데이터에도 적용되기도 하지만, 상태는 대개 UI 요소에서 표시되거나 사용되는 항목과 관련이 있음.
컴포저블 함수가 상태를 갖거나 상태에 의존하면 상태가 변경될 경우, 컴포저블 함수는 재구성된다.
mutableStateOf()를 사용하여 상태를 생성하고, remeber를 사용하여 상태를 기억함.
그리고 다른 컴포저블함수 TextAndButton()로 상태를 전달한다. ⇒ 상태 호이스팅 State Hoisting
→ Hello() :: 상태를 가짐. stateful.
미리보기 Preview
매개변수가 있는 함수에 @Preview 어노테이션을 추가하면
Composable functions with non-default parameters are not supported in Preview unless they are annotated with @PreviewParameter.
라는 에러 생김
- 컴포저블 함수를 감싼 함수 만들기
@Preview
@Composable
fun GreetingWrapper() {
Greeting(name = "Jetpack Compose")
}
@Composable
fun Greeting(name: String) {
Text(
text = stringResource(id = R.string.hello, name),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium,
)
}
매개변수가 없는 또 다른 컴포저블 함수를 생성하는 것.
내부에서 필요한 매개변수와 함께 기존 함수를 호출한다는 것.
미리보기를 가능하게 한다는 점 이외에는 추가적인 가치가 없음.
- 컴포저블 함수에 기본값을 추가
@Preview
@Composable
fun Greeting(name: String = "Jetpack Compose") {
// ...
}
전보다 나아보이지만, 함수를 호출하는 방식이 변경됨. 매개변수를 호출하지 않고 호출할 수 있는 형태.
기본 값을 정의하지 않는 이유가 있는 경우라면 부적절한 방법.
- @PreviewParameter를 사용
미리보기에만 영향을 주면서 컴포저블 함수에 값을 전달
class MainProvider : PreviewParameterProvider<String> {
override val values: Sequence<String>
get() = listOf("PreviewParameterProvider").asSequence()
}
@Composable
@Preview
fun AltGreeting2(@PreviewParameter(MainProvider::class) name: String) {
Text(
text = stringResource(id = R.string.hello, name),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium
)
}
결국 상용구 코드를 만드는 것일수도.. 어떤 것을 하든 개인 취향의 문제임.
미리보기 설정
- 배경 설정
@Preview(showBackground = true, backgroundColor = 0xffff0000)
- 면적 명시적 선택
@Preview(widthDp = 100, heightDp = 100)
- 다른 지역 사용자 테스트
@Preview(locale = "de-rDE") // 독일어
- 상태바, 액션바 보이기
@Preview(showSystemUi = true)
- 기기 대응 (폼 팩터, 화면 비율, 픽셀 밀도 대응)
@Preview(device = Devices.AUTOMOTIVE_1024p)
미리보기 그룹화
@Preview(group = "my-group")
그룹 설정하여 원하는 함수만 보기
컴포즈 앱 실행
컴포저블 함수 배포
Preview에서 특정 함수만 실행 가능
액티비티에서 컴포저블 함수 사용
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Hello() // UI
}
}
}
UI는 setContent라는 함수가 호출되면서 화면에 표시됨.
MainActivity는 어떠한 컴포저블 함수도 포함하고 있지 않다!
컴포저블 함수는 클래서의 일부가 될 필요가 없다.
사실 가능하다면 컴포저블 함수는 최상위 함수로 구현해야 한다.
- setContent 함수
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
val existingComposeView = window.decorView
.findViewById<ViewGroup>(android.R.id.content)
.getChildAt(0) as? ComposeView
if (existingComposeView != null) with(existingComposeView) {
setParentCompositionContext(parent)
setContent(content)
} else ComposeView(this).apply {
// Set content and parent before setContentView
// to have ComposeView create the composition on attach
setParentCompositionContext(parent)
setContent(content)
// Set the view tree owners before setting the content view so that the inflation process
// and attach listeners will see them already present
setOwners()
setContentView(this, DefaultActivityContentLayoutParams)
}
}
setContent함수
는 androidx.activity.ComponentActivity의 확장함수. (androidx.activity.compose 패키지에 포함)
컴포저블 함수를 렌더링하려는 액티비티는 반드시 ComponentActivity 또는 ComponentActivity를 직/간접 적으로 부모 클래스로 갖는 다른 클래스(FragmentActivity, AppCompatActivity)를 상속해야만 한다.
매개변수 parent, content
parent → null 허용. Androidx.compose.runtime.CompositionContext
의 인스턴스. 두 컴포지션을 논리적으로 연결하는 데 사용.
- 컴포즈 앱 기반 vs 뷰 기반 앱
- 뷰 기반 앱 setContentView() 형태setContentView()를 호출하면서 레이아웃 아이디 또는 루트 뷰 자체(일반적으로 바인딩 메커니즘)를 전달함.
class MainActivity : Activity() { private var backing : MainActivityBinding? = null private val binding get() = backing!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) backing = MainActivityBinding.inflate(layoutInflater, null, false) setContentView(binding.root) } }
- 컴포즈 앱 기반 setContent()
- UI 컴포넌트 그리나 각각의 컴포넌트 요소의 참조를 유지할 필요가 없다.
'Android Studio' 카테고리의 다른 글
[젯팩 컴포즈로 개발하는 안드로이드 UI] 3장 컴포즈 핵심 원칙 자세히 알아보기 (0) | 2023.07.04 |
---|---|
[젯팩 컴포즈로 개발하는 안드로이드 UI] 2장 선언적 패러다임 이해 (0) | 2023.07.01 |
서버 Base Url 정보 숨기기 (0) | 2023.03.18 |
[코틀린문법] 입력 함수 readLine, readln, 자바 Scanner (0) | 2023.01.03 |
[코틀린 문법] 변수 타입, val과 var, const (0) | 2023.01.01 |