[Android] JetpackCompose 기본

코랑·2023년 5월 9일
0

android

목록 보기
13/16

Compose의 기본 개념 이해

사용하는 이유

컴포즈는 코틀린으로 만들어져서 강력하다!
모던하고 강력함, 코드를 읽고 쓰기 편함. 간단함.
재사용 가능한 Component를 만들기 쉬움.
UI 요구 사항이 복잡해짐에 따라 각 UI는 상태에 따라 변경되어야 하는데 이게 꼬여서 발생되는 버그가 많음. 이걸 쉽게 해결하기 위해서 나온게 선언형 UI

프로젝트 만들기

새로운 Jetpack Compose 프로젝트 생성

NewProject > Empty Compose Activity
Jetpack Activity의 onCreate() 함수에서 setContent에서 해당 액티비티의 UI 레이아웃을 정의합니다.
=> 이 코드 블럭에 UI 요소를 추가하면된다.

그럼 기존 xml 프로젝트에서 Compose 사용 할때는?

기존 앱의 일부 UI만 컴포즈로 구성할 수 있음.

  • Compose BOM과 필요한 Compose 라이브러리를 추가하면 모든 기능을 사용할 수 있음.

android {
    buildFeatures {
        compose = true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.2"
    }
}

샘플 앱 사용해보기

Compsable 함수 만들기

Composble하게 만들려면 @Composable을 붙여주면 됨.
@Preview를 붙이면 실 단말이나 에뮬레이터를 사용하지 않고도 UI 미리보기가 가능함.
사용할 땐 @Preview를 먼저 붙이고 @Composable를 붙임.

// 실제 UI
@Composable
fun MessageCard(name: String) {
    Text(text = "Hello $name!")
}
// 미리보기
@Preview
@Composable
fun PreviewMessageCard() {
    MessageCard("Android")
}

프레임 세단계


1. 컴포지션: 표시할 UI입니다. Compose는 구성 가능한 함수를 실행하고 UI 설명을 만듭니다.
2. 레이아웃: UI를 배치할 위치입니다. 이 단계는 측정과 배치라는 두 단계로 구성됩니다. 레이아웃 요소는 레이아웃 트리에 있는 각 노드의 레이아웃 요소 및 모든 하위 요소를 2D 좌표로 측정하고 배치합니다.
3. 그리기: UI를 렌더링하는 방법입니다. UI 요소는 일반적으로 기기 화면인 캔버스에 그려집니다.

툴(안드로이드 스튜디오) 사용법

안드로이드 툴(=안드로이드 스튜디오)로 UI를 새로운 함수로 추출해 재사용할 수 있게 해줌(툴이 좋다는 뜻인듯)
UI 미리보기 제공

Layout

Composable 함수 끼리 호출해서 UI 계층적 구조를 만들 수 있음.
여러가지 UI함수를 나열해서 사용하면, 그 UI들 간에 정렬이 필요함.
이때 사용할 수 있는것들이

  • Column: UI요소를 수직으로 정렬(세로)
  • Row: UI요소를 수평으로 정렬(가로)
  • Box: UI요소를 쌓음

또한 UI간 간격이나 크기 모양을 변경할 때는 Modifier를 사용하면 된다.

Image(
	painter = painterResource(R.drawable.profile_picture),
	contentDescription = "Contact profile picture",
	modifier = Modifier
		// Set image size to 40 dp
		.size(40.dp)
		// Clip image to be shaped as a circle
		.clip(CircleShape)
)

참고 목록

더 복잡한 UI를 구성하기 위해 ConstraintLayout을 제공하지만 위 세개를 사용해서 훨씬 간단하게 구성할 수 있음.

MaterialDesign

컴포즈에서는 마테리얼 디자인을 적용할 수 있음.

Material Design은 Color, Typography, Shape 세 가지 핵심 요소를 중심으로 구성됨.

  • Color 색상
    MaterialTheme.colors를 사용하여 정의된 색상 사용 가능
  • Typography 서체 스타일
    Text에 추가하면됨.
Text(
	text = msg.author,
	color = MaterialTheme.colors.secondaryVariant,
	style = MaterialTheme.typography.subtitle2
)
  • Shape 도형
    최종 터치를 추가함.
    Surface 컴포저블로 감싸면 본문의 도형과 높이를 맞춤설정할 수 있음. => Container의 역할
  • 어두운 테마
    Material Theme는 기본적으로 다크모드를 지원해서 텍스트나 배경색이 알아서 맞춰짐.
    uiMode=Configuration.UI_MODE_NIGHT_YES로 주면 다크모드의 UI에 에 대해 미리보기를 볼 수 있음.
// 아래 처럼 어노테이션을 붙여주면 두가지 미리보기를 같이 볼 수 있음.
@Preview(name = "Light Mode")
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    showBackground = true,
    name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
   ComposeTutorialTheme {
    Surface {
      MessageCard(
        msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!")
      )
    }
   }
}

Material: 테마, 컬러, 모양 변경이나 Button, Cards, FABs, 등등.. 자주 사용되는것들 제공
위에 Layout에서 봤던 정렬을 위한걸 제공하고, 이걸 중첩해서 사용해야함.
애니메이션 시스템이 아주 간단하고 효과적으로 적용할 수 있게 변경되었음.
테스트 객체가 1급 접근성. UI테스트를 위한 것들이 많다. 애니메이션 까지도.,.,
코루틴을 사용하면 비동기 처리를 쉽게 할 수 있음.

remember, mutableState

  • Remember와 mutableStateOf
    initial composition 때 저장된 값이 Recomposition에서 사용할 수 있도록 해주는 값.
    State<T>는 immutable, mutable하게 우리가 값을 바꾸고 싶은 값에 사용.
    아래 세개는 같은 표현이다~
    • val mutableState = remember { mutableStateOf(default) }
    • var value by remember { mutableStateOf(default) }
    • val (value, setValue) = remember { mutableStateOf(default) }

animation

  • animate*AsState => animateColorAsState
  • AnimatedContent(실험용)
  • animateContentSize
  • Crossfade

LazyLayout

아래 처럼 그리드 커스텀도 가능하고

스크롤 될때 아이템 애니메이션 주기도 쉽다. => item.animatePlacement { ... }
reordering 기능도 제공하고 lazy Grid도 제공한다.
key: 기본적으로 항목 위치를 기준으로 키가 정해지는데 원하는대로 데이터의 id값을 키로 줄수도 있음. 키가 있으면 데이터랑 일관성이 유지되어 관리가 더 쉬움.

LazyList

LazyColumn/Row 표현되는 요소만 렌더링하여 긴 목록에서 효율적으로 사용됨.(RecyclerView 같은)

DSL

  • item 단일항목 추가 / items 여러 항목 추가 / itemsIndexed() 인덱스가 필요한 경우 사용

컨텐츠의 패딩, 간격,

패딩줄땐 패딩 모디파이어를 쓰면 되는데 얘는 보여지는 컨텐츠 영역 자체를 좁혀버려서
contentPadding을 사용하면 패딩은 주되 보여지는 컨텐츠 영역이 좁아지지 않아서 얘를 활용하는 방법도 있다~
컨텐츠 아이템 사이의 패딩은 arrangemment space 사용해서 간격 띄워주면 된다.

LazyColumn(
	// 패딩
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
    // 간격
    verticalArrangement = Arrangement.spacedBy(4.dp),
) {
    // ...
}
  • rememberLazyListState() 스크롤 컨트롤러와 같은 역할로, 스크롤 될 때마다 많은 것이 변경되는데 코드가 실행되는 순간의 스크롤 상태를 잡아 무언가 처리해 주기 위해 사용.
    (첫번째로 보이는것의 index 라던가,,)
    ex> 스크롤 됐을 때 맨 위의 버튼이 사라져야하는 경우
val state = rememberLazyListState()
val list = state.layoutInfo.visibleItemsInfo.map{
	it.index
}
state.layoutInfo.totalItemsCount

LazyGrid

LazyVerticalGrid, LazyHorizontalGrid 두가지가 있고
columns와 rows 두개를 매개변수로 받음.
GridCells.Adaptive(minSize = 128.dp) 같은걸로 128dp 이상의 크기를 가진 그리드를 그릴 수 있음.
사용할 열의 수를 정확히 알고있으면GridCells.Fixed 를 사용하면 됨.
items도 사용가능한데 span 을 사용해서 열을 지정해 줌.

// items 사용 예시
LazyVerticalGrid(
    // ...
) {
    item(span = {
        // LazyGridItemSpanScope:
        // maxLineSpan
        GridItemSpan(maxLineSpan)
    }) {
        CategoryCard(“Fruits”)
    }
    // ...
}

Tips

1. 0 픽셀 사이즈 아이템을 사용하지 마라(default 사이즈를 정해줘라)

2. 같은 방향의 스크롤 UI 중첩하는것을 피하기

3. 한 항목에 여러 요소를 넣을 때 주의

여러 요소가 한 항목의 일부로 내보내지면 한항목으로 처리되어 개별적으로 구성하기가 힘듬.
모든 요소를 한 항목에 배치하는 극단적인 경우에는 지연 레이아웃 사용의 목적이 완전히 무효화됩니다. 잠재적인 성능 문제 외에도 한 항목에 더 많은 요소를 넣으면 scrollToItem() 및 animateScrollToItem()도 방해합니다

// 나쁜예
LazyVerticalGrid(
    // ...
) {
    item { Item(0) }
    item {
        Item(1)
        Item(2)
    }
    item { Item(3) }
    // ...
}
// 좋은 예
LazyVerticalGrid(
    // ...
) {
    item { Item(0) }
    item {
        Item(1)
        Divider()
    }
    item { Item(2) }
    // ...
}

4. 맞춤 정렬을 사용하는 것이 좋음

배치에 대한 구체적인 요구사항이 있을 경우

object TopWithFooter : Arrangement.Vertical {
    override fun Density.arrange(
        totalSize: Int,
        sizes: IntArray,
        outPositions: IntArray
    ) {
        var y = 0
        sizes.forEachIndexed { index, size ->
            outPositions[index] = y
            y += size
        }
        if (y < totalSize) {
            val lastIndex =
                outPositions.lastIndex
            outPositions[lastIndex] =
                totalSize - sizes.last()
        }
    }
}

5. contentType을 추가하는 것이 좋음(컴포지션 재사용과 성능 이점 극대화를 위해)

6. 성능 측정(디버그 모드에서 더 느릴수 있음 R8 최적화 후 괜찮아 진다고,,)

  • 더 공부하려면 여기

0개의 댓글