본문 바로가기
안드로이드 프로젝트/유튜브 음정 조절 어플리케이션

[Android] #9-3 레이아웃 갱신 이슈 일시적 해결(MotionLayout)

by joh9911 2023. 4. 23.

 

오류 상황을 찍어놓은 게 없어, 현재 영상으로 대체합니다.

 

 

 

동영상 플레이어 프레그먼트에서 채널을 클릭할 경우,

 

창이 최소화가 되며 채널 프레그먼트가 나타나야 합니다.

 

 

 

 

 

동영상 플레이어에서 채널을 여러 번 클릭할 경우

 

채널 프레그먼트의 영상이 보이지 않는 이슈가 발생했습니다.

 

 

 

 

 

 

프로그레스바가 없어진 것으로 보아 데이터는 받아와 졌지만,

 

리사이클러뷰 갱신이 되지 않는 것으로 보였습니다.

 

 

 

심각한 점은,  위의 장면에서 뒤로 가기를 누를 경우 쌓인 채널 프레그먼트가 제거되어 홈 프레그먼트가 나타나지만,

 

터치가 먹지 않았습니다.

 

로그창을 켜서 확인을 해보니, 

 

지워졌다고 생각했던 채널 프레그먼트에서 터치 이벤트를 모두 가져가 버리는 것을 확인할 수 있었습니다.

 

 

 

 

한 번도 겪어보지 못했던 이슈이기에, 원인을 찾는데 어려움이 많았습니다.

 

멘털이 나간채로 이것저것 해보다가, 원인을 발견할 수 있었습니다.

 

 

 

동영상 플레이어 창을 끌어당기며 채널을 클릭할 때마다 해당 이슈가 발생하는 것을 확인했습니다.

 

 

이런식으로

 

 

또한 해당 행동을 반복할 경우, 이전에 발생했던 이슈인 toolBar의 아이콘이 겹쳐지는 현상도 확인할 수 있었습니다.

 

 

 

예전 이슈

 

 

toolBar icon 겹침 이슈 해결: https://joh9911-programming-note.tistory.com/15

 

[Android] #2 toolBar의 아이콘이 밀리는 이슈 해결(motionLayout)

정확히 말하자면, toolBar의 아이콘들이 오른쪽으로 밀리는 이슈였습니다. 저의 툴바에는 아이템이 두 개가 존재하는데, 서치 아이콘이 밀려 겹쳐지는 현상이 발생하였습니다. 한창 삽질했을 때

joh9911-programming-note.tistory.com

 

 

 

MotionLayout에 관한 이슈라는 것을 깨달을 수 있었습니다.

 

따로 설정을 해주지 않으면, MotionLayout이 작동하는 와중에는 뷰의 갱신 요청이 무시됩니다.

 

 

 

 

위의 toolBar의 경우, MotionLayout 내에 있으므로, motion scene.xml 파일 내에서 수정을 해주면 해결이 됐었습니다.

 

 

하지만 채널 프레그먼트는 MotionLayout과 전혀 관계가 없었습니다.

 

MotionLayout이 작용하는 동영상 플레이어 프레그먼트와 MainActivity와의 접점이 없었습니다.

 

(채널 프레그먼트는 MainActivity 내의 HomeFragment  안에서 backStack으로 쌓입니다.)

 

 

 

 

해결 방법을 고민해 봤고, MotionLayout의 작동이 끝난 후 채널 프레그먼트를 띄우자는 결론에 도달했습니다.

 

MotionLayout의 TransitionListener를 이용하였습니다.

 

 

<PlayerFragment.kt>

// 두 개의 TransitionListener inner class를 만들어 주었습니다.
// onTransitionCompleted 매소드를 이용하여 transition이 끝났을 때 이벤트를 설정해줄 것입니다.
// 하나는 채널을 클릭했을 경우의 리스너이며, 다른 하나는 Default 리스너 입니다.
// 그냥 뒤로가기 버튼을 눌렀을 경우에도 MotionLayout이 작동하기 때문에 두 개로 나눴습니다.



// Default 리스너 등록
private fun setMotionLayoutListenerForInitialize() {
        binding.playerMotionLayout.setTransitionListener(null)
        binding.playerMotionLayout.setTransitionListener(TransitionListenerForInitialize())
}
// 채널 클릭용 리스너 등록
private fun setMotionLayoutListenerForChannelClick(){
    binding.playerMotionLayout.setTransitionListener(null)
    binding.playerMotionLayout.setTransitionListener(TransitionListenerForChannelClick())
}

//  채널 클릭용 리스너
inner class TransitionListenerForChannelClick: MotionLayout.TransitionListener{
    override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {
    }
    override fun onTransitionChange(
        motionLayout: MotionLayout?,
        startId: Int,
        endId: Int,
        progress: Float
    ) {
        (activity).also { main ->
            main.findViewById<MotionLayout>(mainBinding.mainMotionLayout.id).progress =
                abs(progress)
        }
    }
    override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
    
    	// 현재 보이는 fragment에 채널 프레그먼트를 추가해줌
        
        for (fragment: Fragment in activity.supportFragmentManager.fragments){
            if (fragment is HomeFragment && fragment.isVisible){
                fragment.childFragmentManager.beginTransaction()
                    .add(fragment.binding.searchResultFrameLayout.id,
                        ChannelFragment(channelDataMapper())
                    )
                    .addToBackStack(null)
                    .commit()
            }
            if (fragment is MyPlaylistFragment && fragment.isVisible){
                fragment.childFragmentManager.beginTransaction()
                    .add(fragment.binding.resultFrameLayout.id,
                        ChannelFragment(channelDataMapper())
                    )
                    .addToBackStack(null)
                    .commit()
            }
        }
		
        // Default 리스너 등록
        setMotionLayoutListenerForInitialize()
        
        /..
    }

    override fun onTransitionTrigger(
        motionLayout: MotionLayout?,
        triggerId: Int,
        positive: Boolean,
        progress: Float
    ) {
    }
}

// Default 리스너
inner class TransitionListenerForInitialize:
    MotionLayout.TransitionListener {
    override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
    }
    override fun onTransitionChange(
        motionLayout: MotionLayout?,
        startId: Int,
        endId: Int,
        progress: Float
    ) {
        (activity).also { main ->
            main.findViewById<MotionLayout>(mainBinding.mainMotionLayout.id).progress =
                abs(progress)
        }
    }
    override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
    	/..
        
    }
    override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
    }
}

 

 

동영상 플레이어 프레그먼트의 onCreateView에서 Default 리스너를 달아줍니다.

 

 

 

<PlayerFragment.kt>

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    fbinding = FragmentPlayerBinding.inflate(inflater, container, false)
    mainBinding = MainBinding.inflate(layoutInflater)
    val view = binding.root
    /..
    
    setMotionLayoutListenerForInitialize()
    
    return view
}

 

 

 

채널을 클릭했을 때, 채널 클릭용 리스너를 달아준 후 motionLayout을 최소화 상태로 동작시킵니다.

 

 

<PlayerFragment.kt>

relatedVideoRecyclerViewAdapter.setItemClickListener(object: RelatedVideoRecyclerViewAdapter.OnItemClickListener{
    // 채널 클릭
    override fun channelClick(v: View, position: Int) {
        setMotionLayoutListenerForChannelClick()
        binding.playerMotionLayout.transitionToState(R.id.start)
    }
    /..
    
    
}

 

 

 

문제를 해결할 수 있었습니다.

 

 

하지만, 여전히 동영상 플레이어 창을 끌어당기며 채널을 여러 번 클릭할 경우

 

해당 이슈가 나타납니다.

 

 

좀 더 근본적인 해결 방식을 고민해 봐야겠습니다.

 

 

문제 일시적 해결

 

댓글