MotionLayout의 setTransitionListener를 사용한 후 UI가 안보이는 에러가 발생했다. UI가 안보일뿐만 아니라 해당 UI의 기능들도 작동하지않았다.
구현하고자 했던 기능은 Fragment에서 MotionLayout의 이벤트가 발생할때 MainActivity의 MotionLayout의 이벤트도 일어나게 하는 것이었다.
아래 코드가 setTransitionListener를 사용했을 때 에러가 났던 코드다.
Fragment.kt
binding.motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionStarted(
motionLayout: MotionLayout?,
startId: Int,
endId: Int
) {
}
override fun onTransitionChange(
motionLayout: MotionLayout?,
startId: Int,
endId: Int,
progress: Float
) {
// MainActivity 모션 레이아웃에 값을 전달
binding.let {
(activity as MainActivity).also { mainActivity ->
mainActivity.findViewById<MotionLayout>(mainBinding.mainMotionLayout.id).progress = abs(progress)
}
}
}
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
}
override fun onTransitionTrigger(
motionLayout: MotionLayout?,
triggerId: Int,
positive: Boolean,
progress: Float
) {
}
})
fragment에서 일어난 transition은 완료가 되었지만, fragment에서 호출한 MainActivity에서의 transition이 완료가 안되었기에 발생한 에러다.
확인하기 위해서 각 motionLayout에 setTransitionListener를 달아주고 로그로 transition이 제대로 종료되는지 확인해본다.
아래 코드는 데이터바인딩된 MainActivity에 쓴 코드다.
MainActivity.kt
binding.mainMotionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionStarted(
motionLayout: MotionLayout?,
startId: Int,
endId: Int
) {
Log.d("MainActivity", "onTransitionStarted")
}
override fun onTransitionChange(
motionLayout: MotionLayout?,
startId: Int,
endId: Int,
progress: Float
) {
Log.d("MainActivity", "onTransitionChange")
}
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
Log.d("MainActivity", "onTransitionCompleted")
}
override fun onTransitionTrigger(
motionLayout: MotionLayout?,
triggerId: Int,
positive: Boolean,
progress: Float
) {
Log.d("MainActivity", "onTransitionTrigger")
}
})
해결하는 방법은 MainActivity의 MotionLayout transition을 호출하는 Fragment의 MotionLayout setTransitionListener에서 호출한 MAinActivity의 transition을 임의로 종료해주는 것이다.
다양한 방법이 있겠지만, 나는 Fragment의 setTransitionListener에서 상속받은 onTransitionCompleted()에서 transition의 process를 동일하게 해주었다.
아래 코드가 위 내용으로 해결한 부분이다.
이렇게 한 이유는 MainActivity와 Framgnet의 transition의 start와 end의 같아야했기 때문이다. 이런게 필요없다면 transitionToStart()
나 transitionToEnd()
를 onTransitionCompleted에 넣어줘도 된다.
Fragment.kt
binding.motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionStarted(
motionLayout: MotionLayout?,
startId: Int,
endId: Int
) {
Log.d(TAG, "onTransitionStarted")
}
override fun onTransitionChange(
motionLayout: MotionLayout?,
startId: Int,
endId: Int,
progress: Float
) {
// MainActivity 모션 레이아웃에 값을 전달
binding.let {
// Fragment 는 자기 단독으로 존재할 수 없기 때문에 activity가 존재할 수밖에 없고
// Activity를 가져오면 해당 Fragment가 attacj 되어있는 액티비티를 가져온다.
(activity as MainActivity).also { mainActivity ->
mainActivity.findViewById<MotionLayout>(R.id.main_motion_layout).progress = abs(progress)
}
}
Log.d(TAG, "onTransitionChange")
}
override fun onTransitionCompleted(motionLayout: MotionLayout, currentId: Int) {
(activity as MainActivity).also { mainActivity ->
mainActivity.findViewById<MotionLayout>(mainBinding.mainMotionLayout.id).progress = motionLayout.progress
}
Log.d(TAG, "SoundPlayerFragment : ${binding.motionLayout.currentState}," +
"MainActivity : ${(activity as MainActivity).also { mainActivity -> mainActivity.findViewById<MotionLayout>(mainBinding.mainMotionLayout.id).currentState }}")
}
override fun onTransitionTrigger(
motionLayout: MotionLayout?,
triggerId: Int,
positive: Boolean,
progress: Float
) {
Log.d(TAG, "onTransitionTrigger")
}
})