RecyclerView와 Fragment간 Transition Animation

송훈기·2022년 11월 6일
0

Android

목록 보기
10/10

출처: 안드로이드 공식 문서 (https://developer.android.com/guide/fragments/animate)

개요

Transition Animation은 기존에 떠있는 뷰가 다음 뷰의 위치와 연결되면서 움직이는 듯한 효과 를 얘기합니다. 마치 재활용 되는 듯한 느낌을 낼 수 있고, 보다 자연스럽게 화면 전환이 이루어지는 효과를 낼 수 있습니다.

이번 글에서는 Fragment에서 RecyclerView 아이템을 클릭했을 때 Transition Animation을 적용할 수 있는 방법에 대해 얘기할 것입니다.

원인

https://developer.android.com/guide/navigation/navigation-animate-transitions
위 링크에서 프래그먼트 대상으로 공유 요소 전환 이라는 섹션의 가이드를 따라해보면 이 방법이 RecyclerView에서 Fragment로의 작동되지 않는다는 것을 알 수 있습니다. (몸으로 꺠달았습니다)

이유는 다음과 같습니다

  • TransitionName을 Unique하게 지어야 한다.

RecyclerView의 각각의 item에 동일한 TransitionName을 적용시켰기 떄문에 어디서 어떻게 움직여야 하는지 알 수가 없게 되어 작동하지 않게 됩니다.

그래서 유니크한 이름을 정할 수 있도록 작업을 진행하고, 이를 받을 수 있도록 작업한다면 Transition Animation을 만들 수 있습니다.

해결

가장 먼저 유니크하게 값을 줄 수 있도록 xml resource를 선언해줍니다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="characterImageTransition">characterImage %s</string>
    <string name="characterNameTransition">characterName %s</string>
</resources>

이후 기본적으로 DataBinding을 사용하고 있다는 가정하에, RecyclerView의 각각의 아이템의 고유한 값을 적용시켜 StringValue를 완성시키면 됩니다.

    <data>

        <variable
            name="character"
            type="com.ssong_develop.core_model.Characters" />
    </data>
.... 중략 ....
    android:transitionName="@{@string/characterImageTransition(String.valueOf(character.id))}"

만약 앱이 API 14이하를 제공한다면, xml attribute가 아닌 직접 메서드를 호출하여 setTransitionName(name: String)적용시킵니다.

그 다음 Transition의 효과를 선언하기 위한 xml file을 하나 더 생성합니다

<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
    <changeBounds />
    <changeTransform />
    <changeClipBounds />
    <changeImageTransform />
</transitionSet>

changeBounds : 장면의 변경 전후에 대상 뷰의 경계를 캡처, 전환 중에 해당 내용을 애니메이션으로 만듬
changeTransform : 뷰의 스케일(scale) 또는 회전을 캡처하고 전환 중 해당 내용을 애니메이션으로 만듬
changeClipBounds : 변경 전후 getClipBounds() 를 캡처 및 전환 중 해당 내용을 애니메이션으로 만듬
changeImageTransform : ImageView의 매트릭스를 캡처 및 전환 중 해당 내용을 애니메이션으로 만듬

그외에도 여러 attribute가 존재하지만 검색하시면서 사용하시길 바랍니다

여기까지 됐다면 이젠 연결만 남았습니다.
기존의 Adapter와 click을 연결한 interface 혹은 lamda를 수정해 애니메이션을 적용하고자 하는 viewTransitionName을 인자로 넘겨줍니다

/** click interface **/ 
interface ItemClickDelegate {
    fun onItemClick(
        characterImageView: View,
        characterNameView: View,
        characterImageTransitionName: String,
        characterNameTransitionName: String,
        characters: Characters
    )
}
/** viewHolder **/
    override fun onClick(view: View) {
        itemClickDelegate.onItemClick(
            characterImageView = binding.ivCharacterImage,
            characterNameView = binding.tvCharacterName,
            characterImageTransitionName = binding.ivCharacterImage.transitionName,
            characterNameTransitionName = binding.tvCharacterName.transitionName,
            characters = characters
        )
    }

각각의 리사이클러뷰 구현이 다를테니 부분만 캡쳐해서 확인하였습니다.
이후엔 공식문서에 나와있는 대로 RecyclerView를 구현하고 있는 fragment에서 메서드를 호출하고FragmentNavigatorExtras를 적용해주면 끝이 납니다.

/** fragment **/
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        postponeEnterTransition()
        (binding.root.parent as? ViewGroup)?.doOnPreDraw {
            startPostponedEnterTransition()
        }
        ... 중략 ...
    }
    override fun onItemClick(
        characterImageView: View,
        characterNameView: View,
        characterImageTransitionName: String,
        characterNameTransitionName: String,
        characters: Characters
    ) {
        val bundle = Bundle()
        bundle.putParcelable(CHARACTER_KEY, characters)
        val extras = FragmentNavigatorExtras(
            characterImageView to characterImageTransitionName,
            characterNameView to characterNameTransitionName
        )
        findNavController().navigate(
            R.id.action_characterFragment_to_characterDetailFragment,
            args = bundle,
            navOptions = null,
            navigatorExtras = extras
        )
    }

결론

이와 같은 방법을 통해서 RecyclerView와 Fragment간의 Transition Animation 구현을 만들었습니다.

예시코드

https://github.com/SSong-develop/RickMorty

profile
안녕하세요 송훈기입니다.

0개의 댓글