ABOUT ME

-

  • 핵심만 골라 배우는 젯팩 컴포즈 Study 4주차
    Android 2023. 2. 8. 19:37


    Chapter 37 ~ 41

    Chapter 37

    AnimateVisibility

    • 진입, 이탈 애니메이션 정의
      • expandHorizontally()
      • expandVertically()
      • expandIn()
      • fadeIn()
      • fadeOut()
      • scaleIn()
      • scaleOut()
      • shrinkHorizontally()
      • 애니메이션 조합도 가능
    AnimatedVisibility(
                    visible = boxVisible,
                    enter = fadeIn() // + expandHorizontally(),
                    exit = slideOutVertically()
                ) {
    		...
    }
    
    • AnimationSpec : 애니메이션 유지 시간, 시작 지연, 스프링, 튕김 효과, 반복, 에니메이션 이징(애니메이션 속도 증가 또는 감소) 등 애니메이션 동작 설정 가능.
      • 애니메이션 유지 시간을 제어하려면 tween() 함수를 호출해 DurationBasedAnimationSpec 인스턴스를 생성하고 이를 애니메이션 효과 함수 호출 시 파라미터로 전달해야 함.
    enter = fadeIn(animationSpec = tween(durationMillis = 5000))
    
    • RepeatableSpec : 애니메이션 반복할 때는 RepeatableSpec 서브클래스 이용.
      • repeatable() 함수를 호출해서 얻을 수 있음.
      • 이 함수는 반복할 애니메이션과 RepeatMode 파라미터를 받음.
      • 이 파라미터는 애니메이션 순서를 **시작 → 끝(RepeatMode.Restart)**으로 적용할지, **끝 → 시작(RepeatMode.Reverse)**으로 적용할지를 지정.
    enter = fadeIn(
                    animationSpec = repeatable(
                        10,
                        animation = tween(durationMillis = 2000),
                        repeatMode = RepeatMode.Reverse
                    )
                )
    
    • AnimatedVisibility 호출 시 진입, 이탈 애니메이션 적용하면 모든 직접, 간접 자식에게 적용됨.
      • animateEnterExit() 모디파이어를 이용하면 자식별로 개별적인 애니메이션을 지정해서 적용 가능.
      • 모디파이어의 애니메이션만 이용하고 싶다면 부모인 AnimatedVisibility 인스턴스에서 EnterTransition.None, ExitTransition.None 지정.
      animateEnterExit(
                        enter = slideInVertically(animationSpec = tween(durationMillis = 5500)),
                        exit = slideOutVertically(animationSpec = tween(durationMillis = 5500))
                      )
                     
      
    • 애니메이션 자동 시작
      • MutableTransitionState 인스턴스를 전달하여 수행.
      • 특별한 목적을 가진 상태로 currentState, targetState 프로퍼티 포함.
      • 기본적으로 현재 상태, 대상 상태는 동일하게 설정되어 있음.
      • MutableTransitionState 인스턴스를 생성할 때 초기 상태가 설정.
      • 트래지션 상태 인스턴스가 생성될 때 targetState 프로퍼티를 true로 설정.
    val state = remember { MutableTransitionState(false)}
    state.apply { targetState = true }
    
    • 교차 페이딩 구현
      • Crossfade 함수 이용.
      • 이 함수에는 targetState를 전달, 이를 이용해 현재 표시된 컴포넌트를 대체할 컴포저블을 결정.
    Crossfade(targetState = boxVisible, animationSpec = tween(5000)) { visible ->
    

    Chapter 38

    상태 주도 애니메이션

    • animate*AsState 함수라 불림.
      • 와일드카드 문자(*)로 해당 애니메이션을 트리거하는 상태 유형으로 대체.
      • 이 함수들은 변경 결과를 하나의 상탯값으로 애니메이션 함.
      • targetValue를 지정하고, 현재 상탯값에서 대상 상탯값으로 변경을 애니메이션으로 표시.
      • animateColorAsState, animateFloatAsState, animateDpAsState..
    • 스프링 효과
      • damping ratio, stiffness를 파라미터로 받음.
      • 댐핑 비율은 튕김 효과가 감소하는 속도를 정의하며 부동 소수점 값으로 선언. (1.0은 튕김 없는 상태, 0.1은 가장 많이 튕기는 상태)
      • DampingRatioHighBouncy, StiffnessHigh 같은 상수 이용 가능.
      • 강도는 스프링의 세기를 정의. 강도가 낮을수록 튕김 효과에 의한 움직임의 범위가 커짐.
    animationSpec = spring(dampingRatio = DampingRatioHighBouncy, stiffness = StiffnessVeryLow)
    
    • 키 프레임
      • 애니메이션 타임라인의 특정한 지점에 다양한 유지 시간이나 이징값 적용 가능.
      • animationSpec 파라미터를 통해 애니메이션에 적용.
      • keyframes() 함수를 이용해 지정.
      • keyframes() 함수는 키 프레임 데이터를 포함한 람다를 전달받아 KeyframeSpec 인스턴스를 반환.
      • 애니메이션을 완료하는 데 필요한 전체 유지 시간을 선언하는 것으로 시작. 이후 전체 시간에 타임스탬프를 찍음.
    animationSpec = keyframes { 
            durationMillis = 1000
            100.dp.at(10)
            110.dp.at(500)
            200.dp.at(700)
        }
    
    • updateTransition()
      • 하나의 대상 상태를 기반으로 여러 애니메이션을 병렬로 실행 가능.
      • targetState를 전달하면 Transition 인스턴스를 반환.
      • targetState가 변경되며 이 트랜지션은 모든 자식 애니메이션을 동시에 실행.
      • label로 트랜지션 식별 가능.
      • Transition 클래스는 자식에 애니메이션을 추가하기 위해 이용되는 함수의 컬렉션을 포함.
      • 애니메이션의 단위 타임에 따라 animate<Type>() 이라는 이름 규칙을 이용.
      val transition = updateTransition(targetState = myState, label = "hi")
      
      val myAnimation: <Type> by transion.animate<Type>(
      		transitionSpec = {
      			// 애니메이션 스펙(tween, spring..)
      		}
      ) {
      	// 현재 상태를 기반으로 새로운 대상 상태를 식별할 코드
      }
      

    Chapter 39

    Canvas

    • 선 그리기와 캔버스 크기 얻기
      • drawLine()
      • 선의 시작점과 끝 점의 x, y 좌표를 알아야 함.
      • 선의 굵기와 색상도 전달해야 함.
      Canvas(modifier = Modifier.size(300.dp)) {
              val height = size.height
              val width = size.width
      
              drawLine(
                  start = Offset(x = 0f, y = 0f),
                  end = Offset(x = width, y = height),
                  color = Color.Blue,
                  strokeWidth = 16.0f
              )
          }
      
    • 점선 그리기
      • 점선을 그릴 때는 PathEffect 인스턴스의 dashPathEffect() 호출.
      • 여기에 부동 소수점 수 배열 전달.
      • 부동소수점 수는 선을 그리는 구간, 그리지 않는 구간을 픽셀 단위로 나타낸 것.
      • 이 배열에는 값이 최소 2개 이상 있어야 함. 구간값의 수는 짝수여야 함.
      pathEffect = PathEffect.dashPathEffect(floatArrayOf(30f, 10f, 10f, 10f), phase = 0f)
      
    • 사각형 그리기
      • drawRect()
      • 모서리 좌표 지정 가능.
      • inset()으로 캔버스의 가장자리에 다양한 설정 가능.
      • drawRoundRect()로 둥근 모서리를 가진 사각형 그릴 수 있음. cornerRadius 컴포넌트 전달해야 함.
      drawRect(color = Color.Blue, topLeft = Offset(x=350f, y=300f), size = size)
      
      drawRoundRect(
                  color = Color.Blue,
                  size = size,
                  topLeft = Offset(20f, 20f),
                  style = Stroke(width = 8.dp.toPx()),
                  cornerRadius = CornerRadius(x = 30.dp.toPx(), y = 30.dp.toPx())
              )
      
    • 회전 시키기
      • rotate()
      rotate(45f) {
                  drawRect(color = Color.Blue, topLeft = Offset(200f, 200f), size = size / 2f)
              }
      
    • 원과 타원 그리기
      • 원 그리기 - drawCircle()
      • 타원 그리기 - drawOval()
      drawCircle(color = Color.Blue, center = center, radisu = 120.dp.toPx())
      drawOval(color = Color.Blue, topLeft = Offset(x = 25.dp.toPx(), y = 90.dp.toPx())
      			
      
    • 그레디언트 그리기
      • Brush
      val brush = Brush.horizotalGradient(colors = colorList, startX = 0f, endX = 300.dp.toPx(), tileMode = TileMode.Repeated)
      
    • 부채꼴 그리기
      • drawArc()
      • Brush 또는 Color 설정과 함께 시작 각도 및 내각을 전달해야 함.
      drawArc(Color.Blue, startAngle = 20f, sweepAngle = 90f, useCenter = true, size = Size(250.dp.toPx(), 250.dp.toPx())
      
    • 경로 그리기
      • 경로는 본질적으로 캔버스 영역 안의 일련의 좌표들을 연결하는 선을 그린 것.
      • Path 클래스 인스턴스에 저장됨.
      • 정의된 경로를 drawPath()에 전달하면 Canvas 위에 경로가 그려짐.
      • moveTo() 함수를 호출해 첫 번째 선의 시작 지점을 정의.
      • 이후 lineTo() 또는 relativeLineTo() 호출해 다음 위치로 선을 연결.
      • lineTo()는 다음 x, y 좌표를 받아 부모 Canvas의 왼쪽 위 모서리를 기준으로 한 상대 좌표값.
      • relativeLineTo()는 이전 위치를 기준으로 하는 좌표를 받음.
      • close()를 호출해 그리기를 완료해야 함.
      val path = Path().apply {
                  moveTo(0f, 0f)
                  quadraticBezierTo(
                      50.dp.toPx(), 200.dp.toPx(),
                      300.dp.toPx(), 300.dp.toPx()
                  )
                  lineTo(270.dp.toPx(), 100.dp.toPx())
                  quadraticBezierTo(60.dp.toPx(), 80.dp.toPx(), 0f, 0f)
                  close()
              }
      
    • 점 그리기
      • drawPoints()를 이용해 Offset 인스턴스 리스트로 지정한 위치마다 점을 찍을 수 있음.
      • pointMode 파라미터를 이용해 각 점을 개별적으로 또는 Lines / Polygon 모드를 이용해 선으로 연결할 것인지 제어 가능.
      for (x in 0..size.width.toInt()) {
                  val y = (sin(x * (2f * PI / width)) * (height / 2) + (height / 2)).toFloat()
                  points.add(Offset(x.toFloat(), y))
              }
              drawPoints(
                  points = points,
                  strokeWidth = 3f,
                  pointMode = PointMode.Points,
                  color = Color.Blue
              )
      
    • 이미지 그리기
      • drawImage()로 이미지 리소스를 캔버스 위에 그를 수 있음.
      • 캔버스 영역의 왼쪽 위 모서리로부터 이미지 위치를 설정한 Offset 인스턴스와 함께 drawImage() 함수에 전달.
      drawImage(iamge = image, topLeft = Offset(x = 0f, y = 0f))
      

    Chapter 40

    ViewModel

    • 구성 변경에서 remeberSaveable로 데이터를 저장할 수 있지만, 컴포저블 내부에 로직을 유지해야 함.
    • 앱이 성장함에 따라 데이터와 로직을 컴포저블과 분리해야 함.
    • ViewModel 안에서 데이터를 관찰 하는 방법
      • 컴포즈의 상태 메커니즘 이용.
      class MyViewModel : ViewModel() {
          var customerCount by mutableStateOf(0)
      
          fun increaseCount() {
              customerCount++
          }
      }
      
      @Composable
      fun TopLevel(model: MyViewModel = MyViewModel()) {
          MainScreen(model.customerCount) {
              model.increaseCount()
          }
      }
      
      @Composable
      fun MainScreen(count: Int, addCount: () -> Unit = {}) {
          Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth()) {
              Text(text = "Total customers = $count", Modifier.padding(10.dp))
              Button(onClick = addCount) {
                  Text(text = "Add a Customer")
              }
          }
      }
      
      • 젯팩 LiveData 컴포넌트 이용.
        • **observeAsState()**는 LiveData<T>를 관찰. 변경될 때마다 State<T> 객체 반환.
        • **observaAsState()**는 컴포지션에 있는 동안에만 라이브데이터 관찰.
      @Composable
      fun TopLevel(model: MyViewModel = MyViewModel()) {
          var customerName : String by model.customerName.observeAsState("")
      }
      
    • ViewModel은 컴포저블 안에서 이용해야만 쓸모가 있음.
    • ViewModel 인스턴스를 컴포저블에 파라미터로 전달해, 컴포저블에서 상탯값과 함수에 접근할 수 있도록 해야함.
    • 컴포저블 계층 맨 위에 위치한 컴포저블에서 수행할 것을 권장.

    Chapter 41

    ViewModel 튜토리얼

    반응형

    댓글

Designed by Me.