Commit
02.13 카메라 화면 전환 기능 추가.

cameraSelector를 FRONT와 BACK CAMERA를 두어 UI 내 카메라 전환 버튼을 클릭 시, 전환되게끔 구현.
val cameraSelector = if(!cameraConverter) {
CameraSelector.DEFAULT_BACK_CAMERA
} else {
CameraSelector.DEFAULT_FRONT_CAMERA
}
//버튼클릭 시 카메라 셀렉터 전환. (BACK <-> FRONT) , false->BACK, true->FRONT
private val _cameraConverter = MutableLiveData<Boolean>(false)
val cameraConverter : LiveData<Boolean>
get() = _cameraConverter

02.21~02.26 즐겨찾기 기능 추가.


해당 프래그먼트에서만 나타나는 바텀 네비게이션을 사용하여 즐겨찾기 버튼을 노출.
즐겨찾기 버튼을 누르면 현재 보고 있는 포토티켓을 업데이트. -> Room Database Dao Update 수행.

상단우측에 위치한 Filter 버튼을 클릭하면 팝업 메뉴가 나타난다.
포토티켓을 최신순 또는 즐겨찾기 만으로 나열할 수 있는 옵션을 선택할 수 있다.
즐겨찾기 기능 구현에서의 문제점과 해결과정
즐겨찾기 기능을 구현하는데 있어서 가장 어려웠던 점은 database로부터 즐겨찾기만 표시된 데이터를 얻었을 때
//최신순 정렬
@Query("SELECT * FROM photo_ticket_table ORDER BY id DESC")
fun getAllPhotoTicket() : LiveData<List<PhotoTicket>?>
//즐겨찾기 표시된 포토티켓만 정렬
@Query("SELECT * FROM photo_ticket_table WHERE favorite == :favorite ORDER BY id DESC")
fun getFavoritePhotoTicket(favorite:Boolean) : LiveData<List<PhotoTicket>?>
얻은 LiveData<List<PhotoTicket>>을 곧바로 현재 UI에 적용하여 보여주는 것 이었다.
구현하기 전에는 상당히 간단했을 것 같은 문제가 생각한데로 이루어지지 않아 시간이 많이 걸렸다.
다음은 이를 구현하기 위해 했던 실패 사례를 적어보겠다.
- viewModel 내에서 val photoTickets 프로퍼티를 var photoTickets으로 읽고 쓰기가 가능한 프로퍼티로 수정하고 팝업 메뉴의 선택 옵션에 따라 database.dao를 통해 반환되는 LiveData<List<PhotoTicket>> 값으로 변환하는 방식.
실패 이유 : 선택 옵션에 따라 photoTickets 프로퍼티에 값을 할당하는 것 까진 성공하였으나 해당 프로퍼티의 데이터를 Observe하고 있던 관찰자는 활동하지 않는다. -> 이전에 할당되어있던 LiveData의 값이 변경된 경우가 아니기에 작동하지 않음.
따라서 해당 fragment가 재구성되어서 viewModel 값을 다시 얻어오는 경우에만 새로운 프로퍼티 값을 관찰하기 시작하여 원하는 데이터를 UI에 display - MediatorLiveData 사용 - 처음부터 최신순LiveData와 즐겨찾기LiveData 프로퍼티를 만들고, MediatorLiveData에 add하여 사용하는 방식.
실패 이유: 결국 LiveData는 내부의 데이터값이 변경되어야 Observer가 변경 여부를 알아채고 action을 취하는건데, 두 개의 LiveData를 모두 관찰한다고 하더라도, 팝업 메뉴의 옵션 선택은 결국 LiveData의 데이터를 변경시키는 것이 아니기 때문에 원하는 데로 작동되지 않는다.
-> 이와 같은 문제가 계속 영향을 미침. - LiveData<LiveData<List<PhotoTicket>>>을 사용? 만약 LiveData<List<PhotoTicket>을 관찰하는 LiveData가 있다면 해당 값인 LiveData<List<~>>가 옵션 선택에 의해 변경되면서 가능하지 않을까? 라는 생각에서 구현.
실패 이유: LiveData<List<~>>값이 변경되는 것 까지는 관찰이 가능했다. 따라서 LiveData<LiveData<List<PhotoTicket>을 observe하여 해당 값이 바뀔 때 adapter.submitList() 를 호출하여 LiveData<List<PhotoTicket>의 value를 인자로 넘겨주는 방식을 구현하였다.
될 줄 알았는데 LiveData<List<PhotoTicket>의 value값이 계속해서 null 이 나오는 것을 Log 기록을 통해 확인하였다. 왜 null값이 나오는지는 잘모르겠다. LiveData.value값을 분명 외부에서도 사용할 수 있는 것으로 아는데.. List<> 여서 였을까? 이유는 모르겠지만 어쨌든 실패.
-> 다음에 왜 안됐었는지 좀 더 확인해봐야겠다.
대표적으로 위와 같은 실패 사례 외에도 여러 방식으로 시도를 하였으나 (인터넷 검색 포함) 원하는 결과를 찾지 못하였다.
그러던 중 SharedViewModel과 관련한 포스팅을 통해 해결책을 찾을 수 있었다.
해결과정
viewModel에 저장된 값을 이용해 프래그먼트끼리 데이터를 공유.
최신순 정렬을 하는 viewPager를 가진 프래그먼트
즐겨찾기 정렬을 하는 viewPager를 가진 프래그먼트
2개의 프래그먼트를 팝업 메뉴의 옵션 선택에 따라 이동하면서 프래그먼트를 재구성한다면 위 실패사례 중 1번의 방식을 사용하면서 item list를 보여줄 수 있다.
따라서 다음과 같은 프래그먼트 구조를 설계하였다.

SharedViewModel 개념을 이용하여 2개의 프래그먼트가 같은 viewModel을 공유하고, 팝업 메뉴의 옵션 선택에 따라
"최신순 또는 즐겨찾기"을 선택 -> viewModel 내에서 현재 photoTickets의 프로퍼티 변경 -> FragmentLately or Favorite 생성 -> viewModel로 부터 현재 photoTickets.value 값을 받아와 adpater의 item으로 설정
와 같은 과정을 거치게 된다.
-SharedViewModel 정리
https://ranseo.tistory.com/289
[Android/Kotlin] SharedViewModel (ViewModel을 이용해 데이터 공유)
개요 개인 프로젝트 Solaroid 앱 개발을 하면서 프래그먼트간 데이터 공유가 필요한 상황이 발생. 이를 위해 viewModel을 이용한 프래그먼트간 데이터 공유를 사용하였고, 이에 대해 간략하게 정리하
ranseo.tistory.com
이 후 ViewModelStore역할을 할 FragmentContainer를 추가하여 containerView 와 ChildFragmentManager, FragmentTransaction을 이용하여 Fragment전환.
이에 따라 PopUp Menu기능 또한 FragmentContainer에 구현.

https://ranseo.tistory.com/290
[Android/Kotlin] FragmentTransaction 프래그먼트 트랜젝션
최근 상위/하위 프래그먼트를 다루기 위해 Fragment에 관한 공부를 다시 하던 중 프래그먼트 매니저와 프래그먼트 트랜젝션에 관한 내용이 잘 기억이 나지않아 간단하게 다시 포스팅하고자 한다.
ranseo.tistory.com
또한 다음은 프래그먼트 트랜젝션을 이용하여 FragmentContainer내에서 두 개의 Fragment를 교체하는 코드이다.
viewModel.naviToLately.observe(viewLifecycleOwner, Observer {
if(it) {
val lately = childFragmentManager.findFragmentByTag(TAG_L)
if(lately==null) {
childFragmentManager.commitNow {
add<SolaroidFrameLately>(R.id.fragment_frame_container_view, TAG_L)
}
childFragmentManager.commit {
replace<SolaroidFrameLately>(R.id.fragment_frame_container_view)
}
}
viewModel.doneNavigateToLately()
}
})
viewModel.naviToFavorite.observe(viewLifecycleOwner, Observer {
if(it) {
val favorite = childFragmentManager.findFragmentByTag(TAG_F)
if(favorite==null) {
childFragmentManager.commitNow {
add<SolaroidFrameFavorite>(R.id.fragment_frame_container_view, TAG_F)
}
childFragmentManager.commit {
replace<SolaroidFrameFavorite>(R.id.fragment_frame_container_view)
}
}
viewModel.doneNavigateToFavorite()
}
})
'개발 > 쏠라로이드(Solaroid)' 카테고리의 다른 글
| 중간 점검. (0) | 2022.04.04 |
|---|---|
| 03.14 개발현황 : EDIT 기능, FAB 추가, view elevation 설정, 향후 계획 (0) | 2022.03.14 |
| 02.12.진행상황 - SwipeView (ViewPager2) // CameraX // Glide // icon 추가 (Image Asset) // Navigation 변경 (0) | 2022.02.12 |
| [Kotlin/Android/Project/Solaroid] 진행 상황, RoomDatabs, Fragment & ViewModel, RecyclerView, 향후 일정 (0) | 2022.01.24 |
| [Kotlin/Android/Project/Solaroid] 쏠라로이드 첫 개발 시작, 어플리케이션 설명 (0) | 2022.01.22 |