업데이트:

카테고리:

/

태그: ,

레이아웃 구상할 때 만드는 순서는 요소를 쪼개서 제일 작은 부분부터 제작 후 감싸가는 형태로 만든다.

특정 형태가 반복된다면, 함수를 재활용해서 처리

화면을 구상하는 전체적인 레이아웃이 들어가도록… Scaffold에 간단하게 적용이 될 수 있도록 한다.

스타일 저장

  1. Color → 색 저장
  2. Shape → 모양 지정
    1. 모서리 둥글게 ( Modifier.background )
     val shapes = Shapes(
         extraSmall = RoundedCornerShape(4.dp),
         small = RoundedCornerShape(8.dp),
         medium = RoundedCornerShape(16.dp),
         large = RoundedCornerShape(24.dp),
         extraLarge = RoundedCornerShape(32.dp),
     )
    
  3. Type → 내부에 들어가는 글시체, 크기…

     val Typography = Typography(
         headlineSmall = TextStyle(
             fontWeight = FontWeight.SemiBold,
             fontSize = 24.sp,
             lineHeight = 32.sp,
             letterSpacing = 0.sp
         ),
     ...
     )
    

    지정하지 않은 속성은 Default로 정해진 값을 따라간다.

  4. Theme → 앱에서 사용하는 전체적인 Theme 설정

     @Composable
     fun AppTheme(
         useDarkTheme: Boolean = isSystemInDarkTheme(),
         content: @Composable() () -> Unit
     ) {
     		// 다크 모드 설정 여부
         val context = LocalContext.current
         val colors = when {
             (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
                 if (useDarkTheme) dynamicDarkColorScheme(context)
                 else dynamicLightColorScheme(context)
             }
             useDarkTheme -> DarkColors
             else -> LightColors
         }
        		
     		// 상태바 설정 
         val view = LocalView.current
         if (!view.isInEditMode) {
             val window = (view.context as Activity).window
             window.statusBarColor = colors.primary.toArgb()
             WindowCompat
                 .getInsetsController(window, view)
                 .isAppearanceLightNavigationBars = useDarkTheme
         }
        		
         MaterialTheme(
             colorScheme = colors,
             typography = Typography,
             shapes = shapes,
             content = content
         )
     }
    

    MaterialTheme.colorScheme로 요청하면, 지정된 색상을 꺼내 사용할 수 있음

1. LazyColumn / Row (= RecyclerView)

스크롤이 가능한 목록을 보여주기 위해서 사용

많은 데이터를 미리 랜더링 해놓으면 성능상에 문제가 발생, RecyclerView와 달리 이전에 사용했던 요소를 재활용하지 않고 새로운 Composable을 방출한다.

private fun Greetings(
	modifier:  Modifier = Modifier, 
	names: List<String> = List(1000) {"$it"}
) {
	LazyColumn{
		items(items = names) {name ->
			Greeting(name = name)
		}
	}
}

Lazy 요소는 내부에 items 를 통해서 리스트로 된 데이터를 리스트뷰로 보여준다.

Lazy Component의 스크롤 위치를 저장하고 싶다면 state: LazyListState = rememberLazyListState() 을 추가한다.

버튼을 클릭하여 특정로직을 구현한다면, 삭제기능을 가장 최상위 화면? 컴포넌트에 작성하고 그 하위로직에서는 삭제할 아이템의 아이디를 넘겨주고, 최 하위 로직에서는 어떤 버튼을 눌러야 로직이 트리거 되는지 설정한다.

아이템 삭제 로직

아이템 삭제 로직

2. animation

화면이 펼쳐질 때 열리는 느낌의 애니메이션 적용

  1. animate*AsState 컴포저블 사용

    * 에는 dp, color… 바꾸고 싶은 상태값이 들어감

    이렇게 작성하는 애들은 미리 외부에서 변수로 by animate... 으로 타입 설정을 해줘야함

    • 목표dp 까지 value가 업데이트 되는 객체를 반환
    • animationSpec 을 통해 세세하게 커스텀도 가능하다.

        val extraPadding by animateDpAsState(
                if (expanded) 48.dp else 0.dp,
        				// spring 애니메이션 효과가 적용되어 띠용띠용 흔들린다.
                animationSpec = spring(
                    dampingRatio = Spring.DampingRatioMediumBouncy,
                    stiffness = Spring.StiffnessLow
                )
            )
      
  2. [AnimatedVisibility()](https://developer.android.com/codelabs/jetpack-compose-animation?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fcompose%3Fhl%3Dko%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-animation#3) 는 if 문 대신 사용한다.

     AnimatedVisibility(
             visible = shown,
             enter = slideInVertically(
                 initialOffsetY = { fullHeight -> -fullHeight }
             ),
             exit = slideOutVertically(
                 targetOffsetY = { fullHeight -> -fullHeight }
             )
         )
    

    initialOffsetY, targetOffsetY 의 변화값으로 { fullHeight -> -fullHeight } 을 입력하였는데, 변화하는 범위이고 왼쪽인자를 기준으로 오른쪽 인자까지 움직인다.

    이때 주의할 것은 왼쪽인자의 값을 0으로 기준 세우기 때문에 오른쪽 값을 0 으로 지정하면 움직임이 없다.

  3. transition

    1 번째 방법과 유사하게 변수로 animate 타입을 설정하듯이 사용할 수 있다.

    큰 차이점은 transition으로 설정하는 값은 여러종류의 animation을 적용할 수 있다는 점

     // page의 타입에 따라 animation 설정
     val transition = updateTransition(tabPage, label = "Tab indicator")
         val indicatorLeft by transition.animateDp(label = "Indicator left") { page ->
             tabPositions[page.ordinal].left
         }
         val indicatorRight by transition.animateDp(label = "Indicator right") { page ->
             tabPositions[page.ordinal].right
         }
         val color by transition.animateColor(label = "Border color") { page ->
             if (page == TabPage.Home) Purple700 else Green800
         }
    
  • animationSpec 으로 프레임별로 세세하게 위치나 투명도, 색깔을 지정할 수 있음

      animationSpec = infiniteRepeatable(
                  animation = keyframes {
      								// 한바퀴 굴리는데 걸리는 시간(ms)
                      durationMillis = 1000
      								// 어느 시간때에 특정한 값을 갖는지
                      0.7f at 500
                      0.9f at 800
                  },
                  repeatMode = RepeatMode.Reverse
              )
    

3. TextField

editText 와 비슷하게 내부에 텍스트 입력을 받는다.

얘는 이자체만으로도 디자인을 끝낼 수 있다? 내부에 이미지 아이콘을 넣기 위해 ImageView를 추가로 넣거나 constrainlayout에 집어넣을 필요 없이 한 요소에서 처리가 가능하다.

TextField(value = "",
    onValueChange ={},
    modifier = modifier
        .fillMaxWidth()
        .heightIn(min = 56.dp),
		// icon을 속성으로 넣을 수 있어, 따로 지정해주지 않아도 된다.
    leadingIcon ={
Icon(
            imageVector = Icons.Default.Search,
            contentDescription = null)
},
    colors = TextFieldDefaults.textFieldColors(
        backgroundColor = MaterialTheme.colors.surface
    ),
    placeholder ={
Text(stringResource(R.string.placeholder_search))
},
)

4. Elevation

요소가 겹쳐있다면, 누가 제일 앞으로 나와있는지 결정하는 속성

Untitled

위의 이미지 처럼 설정하여, 그림자 효과처럼 사용할 수 있다.

버튼이나, 위에 떠있는 Float 요소에 적용하면 더욱 입체적인 화면 구성을 가져갈 수 있다.

5. androidView

특정 뷰를 화면 전체에 꽉채워서 보여준다.

위에 어떤 요소를 위치시키고 싶다면

AndroidView(
    factory = { context ->
        MapView(context).apply {
            // MapView 초기화 및 설정
        }
				TextView(modifier = Modifier.zIndex()) {}
    }
) 

동일 선상에서 위치시킨 뒤에, zIndex를 조정해주면 같은 레이어 위치할 수 있게 된다.