-
핵심만 골라 배우는 젯팩 컴포즈 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 튜토리얼
반응형'Android' 카테고리의 다른 글
핵심만 골라 배우는 젯팩 컴포즈 Study 5주차 (1) 2023.02.12 핵심만 골라 배우는 젯팩 컴포즈 Study 3주차 (0) 2023.01.28 핵심만 골라 배우는 젯팩 컴포즈 Study 2주차 (0) 2023.01.25 핵심만 골라 배우는 젯팩 컴포즈 Study 1주차 (0) 2023.01.25 Android) Kotlin Coroutines 1.6에서 도입된 테스트 API 사용해보기 (1) 2022.08.21 - 진입, 이탈 애니메이션 정의