Fragment 전환에 공유 요소 애니메이션을 적용하는 방법을 살펴본다. 기본적인 sharedElementViews 구성부터 Chris Banes의 ColumnedChangeBounds를 활용한 확장 효과까지 다룬다.
일반적으로 Fragment 전환 시 아무런 전환 효과를 주지 않습니다. commit과 동시에 콘텐츠 화면이 심심하게 바뀝니다. Fragment도 Activity에서처럼 전환 효과를 주기위해서는 sharedElementViews를 이용하여 전환하고 싶은 View를 추가하면 됩니다. 그리고 트랜잭션 최적화 작업을 위해 setReorderingAllowed() 속성을 허용합니다.
supportFragmentManager.beginTransaction()
.setReorderingAllowed(true)
.replace(R.id.container, GridFragment.newInstance(count))
.addToBackStack("detail")
.apply {
val topFragment: Int = supportFragmentManager.fragments.size
val fragment: GridFragment = supportFragmentManager.fragments[topFragment - 1] as GridFragment
val recyclerView: RecyclerView = fragment.recyclerview
var viewCount = 0
for (itemView in recyclerView.children) {
addSharedElement(itemView, viewCount.toString())
viewCount++
}
}
.commit()
위의 코드에서 addSharedElement를 통해 현재 보여지고 있는 View에 각각 전환될 이름을 부여 해줍니다. 그럼 Fragment가 전환된 View에 해당 전환된 이름의 View를 찾아 트랜지션 됩니다.
문제점
새로운 Fragment가 생성되면 데이터를 가져오거나 이미지를 불러오고 레이아웃을 구성하고 그리는 시간이 걸리기 때문에 전환 효과는 자연스럽지 않습니다. 이런 구성을 하는 동안 애니메이션이 끝나게 되는 경우 아무런 효과를 보지 못할 수 있습니다. 그렇기 때문에 장면 효과를 데이터나 뷰를 구성하기 전까지 지연할 필요가 있습니다.
postponeEnterTransition(), startPostponedEnterTransition()을 이용하면 장면 효과를 지연하고 시작할 수 있습니다. 우리는 뷰가 그려진다는 사실을 OnPreDrawListener를 통해 인지 할 수 있습니다. 최근에 지원된 코틀린 라이브러리 KTX를 이용한다면 OnPreDrawListener 대신 doOnPreDraw를 통해 쉽게 콜백을 받을 수 있습니다.
postponeEnterTransition()
recyclerView.doOnPreDraw {
startPostponedEnterTransition()
이제 Fragment 전환 효과를 위한 준비는 모두 끝났습니다. 어떻게 트랜지션이 이루어 질지에 대한 정의만 해주면 되는데 기본적으로 몇 가지를 지원하는데 Bounds가 변경되기 때문에 ChangeBounds() 트랜지션을 설정 하도록 하겠습니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sharedElementEnterTransition = ChangeBounds()
}
좀 더 멋진 효과를 보기 위해는 Chris Banes과 같이 ColumnedChangeBounds를 직접 구현하면 됩니다. 이 글에서 구현된 모든 코드는 https://github.com/kmshack/FragmentTransitions에 공개됩니다.