본문 바로가기

기타/안드로이드

28. Woof App : 애니메이션, icon 추가

애니메이션 : icon 추가

Jetpack Compose를 사용한 간단한 애니메이션

 

Jetpack Compose를 사용한 간단한 애니메이션  |  Android Developers

Compose에서 앱에 간단한 스프링 애니메이션을 추가하는 방법을 알아보세요.

developer.android.com

오늘은 위의 페이지를 참고하여 저번에 만들었던 Woof앱에 애니메이션을 추가해보자

 

 

Icon 추가

만든 어플들을 빌드할 때마다 gradle이라는 친구를 본 적 있을 것이다

gradle은 빌드를 도와주는 빌드 툴이다

compile, test, packaging, deploy & run과 같은 작업들을 자동화시켜준다

 

material design에는 icon이 있는데

이 icon들을 사용하려면 gradle에 dependency를 추가해줘야 한다

 

위와 같이 build.gradle (Module. Woof.app)에 들어가서

밑에 있는 dependencies 안에 

implementation "androidx.compose.material:material-icons-extended:$compose_version"

을 입력해주자

그리고 위에 뜨는 Sync Now를 클릭해서 적용해주자

 

그리고 MainActivity.kt로 돌아와서

DogItem composable 밑에 DogItemButton composable을 추가하고

IconButton이라는 composable을 이용해서 다음과 같이 작성해보자

@Composable
private fun DogItemButton(
    expanded: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
){
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.ExpandMore,
            tint = MaterialTheme.colors.secondary,
            contentDescription = stringResource(R.string.expand_button_content_description)
        )
    }
}

전에 Icon이 Image와 뭐가 다른가 의아했었는데

vector 이미지를 담을 수도 있다는 걸 알 수 있다

 

그리고 이제 DogItem에 버튼이 클릭되었는지 상태를 저장하는 remember 변수 하나랑

아이콘을 띄우는 구문, 그리고 버튼을 오른쪽 끝으로 정렬할 수 있게 Spcaer를 추가해보자

@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
    var expanded by remember { mutableStateOf(false) }
    Card(
        modifier = Modifier.padding(8.dp),
        elevation = 4.dp
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp)
        ) {
            DogIcon(dog.imageResourceId)
            DogInformation(dog.name, dog.age)
            Spacer(Modifier.weight(1f))
            DogItemButton(
                expanded = expanded,
                onClick = {}
            )
        }
    }
}

Modifier.weight를 정말 오랜만에 보는 것 같은데

여기서 가중치가 들어간 건 Spacer 하나이기 때문에

다른 모든 애들의 크기가 유지되는 선에서 모든 부피를 차지하게 된다

 

결과화면

 

아이콘 클릭 시 나올 화면 구성

클릭하면 강아지의 취미 정보를 담은 Text를 보여줘야 한다

다음과 같은 DogHobby composable을 작성한다

@Composable
fun DogHobby(@StringRes dogHobby: Int, modifier: Modifier = Modifier){
    Column(
        modifier.padding(
            start = 16.dp,
            top = 8.dp,
            bottom = 16.dp,
            end = 16.dp
        )
    ){
        Text(
            text = stringResource(R.string.about),
            style = MaterialTheme.typography.h3
        )
        Text(
            text = stringResource(dogHobby),
            style = MaterialTheme.typography.body1
        )
    }
}

 

그리고 이 DogHobby를 DogItem에 넣어야 하기 때문에

Row와 동등한 관계에 DogHobby를 위치시키고 Column으로 감싸준다

@Composable
fun DogItem(dog: Dog, modifier: Modifier = Modifier) {
    var expanded by remember { mutableStateOf(false) }
    Card(
        modifier = Modifier.padding(8.dp),
        elevation = 4.dp
    ) {
        Column {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp)
            ) {
                DogIcon(dog.imageResourceId)
                DogInformation(dog.name, dog.age)
                Spacer(Modifier.weight(1f))
                DogItemButton(
                    expanded = expanded,
                    onClick = {}
                )
            }
            DogHobby(dog.hobbies)
        }
    }
}

 

결과화면

 

버튼 기능 추가

버튼 기능을 추가하려면 expanded 변수만 고민해보면 된다

일단 DogItemButton의 onclick에서 expanded의 참 거짓을 바꿀 수 있게 설정해주고

DogHobby가 expanded가 참일 때만 보이게 하고

expanded가 참일 때에는 아래를 가르키는 화살표 아이콘(ExpandMore)이 아니라

위를 가르키는 화살표 아이콘(ExpandLess)이 되도록 해줘야 한다

 

DogItem의 Column안에서 onClick과 DogHobby 출력 여부 결정

Column {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp)
    ) {
        DogIcon(dog.imageResourceId)
        DogInformation(dog.name, dog.age)
        Spacer(Modifier.weight(1f))
        DogItemButton(
            expanded = expanded,
            onClick = { expanded = !expanded }
        )
    }
    if(expanded) { DogHobby(dog.hobbies) }
}

 

DogItemButton의 Icon에서의 imageVector

Icon(
    imageVector = if(expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
    tint = MaterialTheme.colors.secondary,
    contentDescription = stringResource(R.string.expand_button_content_description)
)

 

결과화면

애니메이션 추가

DogHobby의 높이가 변경되는 애니메이션을 추가하고 싶은 것이기 때문에

animateContentSize를 이용한다

애니메이션 사양을 설정할 수 있는 AnimationSpec도 이용한다

우리는 DogHobby가 spring처럼 튀는 느낌의 애니메이션을 추가할 것이다

 

DogItem의 Column 안에 다음과 같이 작성한다

Column(
    modifier.animateContentSize(
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        )
    )
)

dampingRatio는 용수철의 탄성(=얼마나 통통 튀는가)

stiffness는 용수철의 속도(=얼마나 빨리 돌아오는가)

를 결정하는 인자이다

 

결과화면

 

+ 색이 변하는 애니메이션 추가하기

animate어쩌구AsState는 최종값을 넣어주면

현재값에서 최종값으로의 애니메이션이 적용된다

 

DogItem에 다음과 같이 remember랑 비슷하게 만들어주고

val expandedColor by animateColorAsState(
    targetValue = if(expanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface
)

DogItem의 Column안에서 background 색으로 지정해주면 된다

Column(
    modifier
        .animateContentSize(
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        )
    )
        .background(expandedColor),
)

 

결과화면