Compose는 어떻게 동작하는지 공부해 보자.
work flow를 정리하기 이전에, UI가 어떤식으로 작동? 구성되면 좋을지에 대한 부분을 먼저 정리하고 하나씩 물음에 해결하는 방식으로 공부를 진행
내가 알고잇는 웹구조와 비교하며 공부를 진행해보았다.
1. 태그:
웹에서는 main, section, container(div) 태그를 통해서 계층적으로 구조를 형성
compose 에서는 어떤식으로 구성되는 것일까? -
2. CSS:
웹에서는 CSS를 html에 바로 적용하거나, js를 통해 동적으로 적용. 혹은 css 파일로 적요.
compose 에서는 CSS를 어떻게 처리할까?
- css 크기 규격 용어 정리 (web에서는 px, auto, vw,vh)
3. position
웹에서는 relative, absolutre 등으로 포지션을 계층적으로 나눈 후.
top, left, right, bottom 을 적용하여 배치 or padding, margin을 통해 배치를 진행함
앱에서는 어떻게 되는걸까??
4. 동적 contents, function
웹에서는 js를 직접 or library를 통해 동적 처리를 수행,
compose에서는 어떤 식으로 데이터를 처리하거나, 기능을 동작시킬까?
5. scroll animation or html 5
: 웹에서는 스크롤에 따른 애니메이션 효과를 적용할 수 있었는데,
Compose에서는 어떻게 적용?
이건 따로 여기에 저장 해둠
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
가장 기본적으로, compose는 @Composable 어노테이션으로 선언이 가능하다. 즉, method 형태로 UI선언이 가능하다는 이야기.
@Composable
fun Counter() {
val count by remember { mutableStateOF(0) }
Button(onClick = { count++ } {
Text("Clicked $count times")
}
}
compose는 state의 concept으로 built 된다. statae가 변하게 되면, 관련된 UI parts는 다시 composed 된다.
datamdel과 함꼐 UI동기화를 쉽게 할 수 있음.
using State, remeber
다시 compoable function이 시작됨. UI의 변화를 반영하기 위함. state가 변하는 상황이 생길때만 recompose 된다.
→ 불필요한 UI 업데이트 감소
UI 컴포넌트를 꾸미기 위해 사용되는 클래스.
,
@Composable
fun PaddedText() {
Text("Hellot, Compose!!", modifer = Modifier.padding(16.dp))
}
질문: web의 laayout은 main > section > div 등의 구조인데 Compose는 이를 어떤 구조로 해결하는지?
Compose 역시 계층 구조가 존재한다. 계층구조에 사용되는 여러 Class를 아래에 정리
Column : div와 비슷 한 컨테이너. 대신 자식 요소를 수직으로 배치하는게 기본
Row: flex contaier 처럼 자식들을 수평으로 배치
Box: 역시나 div와 유사. 자식들을 각각 top위치에 배치
ConstarintLayout: Androide View의 ConstraintLayout기능을 그대로 가져온 것./ web에서는 flexbox, frid 와 비슷.
복잦ㅂ한 레이아웃을 정의하는데 사용된다.
-> 자식들간의 규칙을 설정 할 수 있음
@Composable
fun ProfileScreen() {
Column {
Row {
Image(painter = ..., contentDescription = "Profile picture")
Text("User name")
}
Text("Bio: Compose enthusiast.")
}
}
@Composable
fun MyApp() {
Column(modifier = Modifier.fillMaxSize()) {
TopBar()
ContentSection()
Footer()
}
}
@Composable
fun TopBar() {
Row(modifer = Modifier.fillMaxWIdht()) {
Text("Title", modifer = Modifer.weight(if))
Buttom(onClick = {/*do something*/}) {
Text("Settings")
}
}
}
@Composable
fun ContentSection() {
Column(modifer = Modifier.fillMaxWidth()) {
Text("Welcome to the app")
}
}
@Composable
fun Footer() {
Text(
text = "© 2024 My Company",
modifer = Modifer
.align(Alignment.CenterHorizontally)
.padding(8.dp)
)
}
위 코드를 보면 알겠지만, Container로 선언 후 다른 @Composable을 쌓아서 사용하고 있다. 참고로 @Composable이 붙은 메서드는 class 처럼 이름 첫 시작이 대문자이다. 이는 하나의 Class처럼 보이게끔 하기 위함인듯하다.
일단 위 글을 정리하면서 CSS역할을 하는게 Modifer라는 걸 대충 눈치를 챘다.
그럼 규격에 대해서 좀 더 자세히 정리할 필요가 잇음
Density-Indepedent Pixels.
px처럼 사용하는 단위.
Q&A
1. Desity로 부터 독립한 pirxel을 의미하는데 .. 그럼 Density가 무엇을 읨히는지?
Scale -independent Pixels
폰트사이즈에 사용됨
vh, vw, % 로 사용되던 것 들
// fill 100% of parent's width
Modifier.fillMaxWidth()
// fills 50% of the parents'witdh
Modifier.fillMaxWidth(0.5f)
weight
마치 fliexBox처럼 쓸 수 있음
Row {
Box(modifer = Modifier.weight(1f))
Box(odifier = modifier.weight(1f)) // fills 50% of the Row
}
web의 auto와 관련된 부분
Compose에서는 wrap content가 default이다 → ⚠️이거 무슨말인지 잘 모르겠어
Box {
Text("Auto - sized content")
}
// Text composable will take up only as mush space as its content needs.
You can also specify minimum, maximum, or exact sizes using Modifier methods like requiredSize(), requiredWidth(), requiredHeight(), minWidth(), maxWidth(), and more.
Box(
modifier = Modifier
.widthIn(min = 100.dp, max = 200.dp) // Similar to CSS `min-width: 100px; max-width: 200px;`
.height(100.dp) // Fixed height, similar to `height: 100px;`
)
| Web (CSS) | Jetpack Compose (Modifiers) | Description |
|---|---|---|
px | dp | Density-independent pixels, scales with screen density. |
em, rem | sp | For text scaling based on user preferences. |
%, vw, vh | fillMaxWidth, fillMaxHeight, weight | Percentages or viewport-based dimensions. |
auto | Wrapping content (default behavior) | Auto-size to fit content. |
min-width, max-width | widthIn(), heightIn() | Set minimum and maximum width or height. |
padding | padding() | Adds padding to composables. |
margin | Use padding() + parent space management | Margins can be managed using combinations of modifiers. |
position: relative, absolute, fixed, and sticky와 연관되어 잇는 부분.
기준점을 정하는 거
@Composable
fun RelativePositioningExample() {
Box {
Text("Hellow, world!!", modifier = Modifier.offset(x = 16.dp, y = 8.dp))
}
}
Modifier.offset(x = 16.dp, y = 8.dp) moves the composable 16dp to the right and 8dp down from its original position.
부모 요소로 부터 자유로워 지는거
@Composable
fun AbsolutePositioningExample() {
Box {
Text(
"Absolutely Positioned!",
modifier = Modifier.absoluteOffset(x = 30.dp, y = 50.dp)
)
}
}
직접적으로 fiexf를 지원하는건 없다.
하지만 Trick을 이용해 구현은 가능하다.
@Composable
fun FixedPositioningExample() {
Box {
// Content that scrolls
LazyColumn {
items(100) { index ->
Text("Item $index")
}
}
// "Fixed" header
Text(
"Fixed Header",
modifier = Modifier
.align(Alignment.TopCenter) // Keeps this header at the top
.background(Color.Gray)
.padding(8.dp)
)
}
}
sticky는 잘 사용하지 않아서 어떤 개념인지 공부가 필요.
간략하게는 특정 스크롤 포지션을 만나면 해당 elemnt를 relative로 변경하는 것을 말하는 것 같다.
벗어 나면 fixed가 됨
@Composable
fun StickyHeaderExample() {
LazyColumn {
stickyHeader {
Text("Sticky Header", Modifier.background(Color.Gray).padding(8.dp))
}
items(100) { index ->
Text("Item $index")
}
}
}
Modifier.align(), Modifier.layout()이걸로 가능하다고 함
@Composable
fun AlignmentExample() {
Box(modifier = Modifier.fillMaxSize()) {
Text("Centered Text", modifier = Modifier.align(Alignment.Center)) // Centered in the Box
}
}
| Web (CSS) | Jetpack Compose (Modifier/Layout) | Description |
|---|---|---|
position: static; | Default behavior | Elements flow naturally in the layout without any offset or positioning. |
position: relative; | Modifier.offset(x: Dp, y: Dp) | Elements are offset relative to their default position. |
position: absolute; | Modifier.absoluteOffset(x: Dp, y: Dp) | Elements are placed at fixed coordinates, relative to the parent container. |
position: fixed; | Modifier.align() + container structure | Elements stay fixed in place, not affected by scrolling (custom handling). |
position: sticky; | LazyColumn with stickyHeader | Headers that stick at the top while scrolling. |
align-items, justify-content | Modifier.align() and other layout modifiers | Aligning elements within a container. |
이건 근데 애초에 ktolin이라서 얼마든지 커스텀이 가능할 듯 싶다.
간단한 것들만 따로 정리해봣음
기본적으로 jetpack compose는 상태가 변함에 따라 UI가 변경된다. 이댸 compose는 Recomposes가 된다.
dynamic UI의 핵심. 상태가 변화하면 - > compse는 recompose를 발생
State를 사용하여 변하하는 데이터를 hold 해둘 수 있음.
remeber, mutalbeStateOF, and Viewmodel 등을 사용할 수 잇다.
@Composable
fun Counter() {
// Remeber and store the mutable state of the counter
var count by remember { mutableStateOf(0) }
// Column layout to vertically stack the elements.
Column(
modifer = Modifer.padding(16.dp)
horizontalAligment = Aligment.CenterHorizontally
) {
// Dynamic text that updates whenver 'count' changes
Text(text = "Count: $count", fontsize = 24.sp)
Button(
onClick = { count++ }
modifer = Modifer=padding(top = 16.dp)
) {
Text(text = "Increment"
}
}
}
web에서는 GSAP과 같은 라이브러리를 사용하여 스크롤 애니메이션 구현이 가능함.
Compose에서도 당연히 물론 비슷한 기능을 제공하는 API가 존재한다.
Modifier.graphicsLayer and Modifier.animate: Apply animations to elements as they scroll.LazyListState and rememberScrollState: Track the scroll position to trigger animations.AnimatedVisibility, animateDpAsState, animateFloatAsState: Animate composables based on state changes.code
@Composable
fun FadingScrollAnimationList() {
val listState = rememberLazyListState()
LazyColumn(state = listState) {
items(50)
}
}
4,5번은 필요한 시점에서 추가 공부 후 다시 정리.
1. JETPACK Compse는 Web가 유사하게 구조를 짜서 만들고 속성을 조정할 수 있다.
2. UI를 더욱 화려하게 해중 animation(by scroll) 기능 역시 가능하다.
3. 언어 자체가 kotlin이라서 기능적인 측면에서는 훨씬 더 쉽고 유연하게 작동할 것 같다.