
얼마전에 위 사진에서 토글 버튼에 따른 가운데 EditText의 단위 업데이트 관련해서 질문드렸었는데요
해결은 했는데, 음 좀 석연치않은 부분이 있어서 질문드립니다.
두가지 방법이 있구요. 이 두방법다 장단점(?) 이 있는것같아요..
제가 아직 코딩실력이 부족해서 어떻게 해결하면 더 깔끔한지 궁금합니다.
공통코드
class WriteDetailFragment : Fragment() {
private var _binding : FragmentWriteDetailBinding? = null
private val binding get() = _binding!!
val args: WriteDetailFragmentArgs by navArgs()
lateinit var workout: String
private lateinit var adapter: DetailAdapter
private val vm : DetailViewModel by viewModels { DetailViewModelFactory() }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWriteDetailBinding.inflate(inflater, container, false)
adapter = DetailAdapter()
binding.rv.adapter = adapter
binding.apply {
rv.adapter = adapter
rv.itemAnimator = null
header.workout.text = args.workout
header.add.setOnClickListener {
vm.addDetail()
}
header.delete.setOnClickListener {
vm.deleteDetail()
}
header.toggleButton.addOnButtonCheckedListener { _, checkedId, isChecked ->
if(isChecked) {
when(checkedId) {
R.id.kg -> vm.changeUnit("kg")
R.id.lb -> vm.changeUnit("lbs")
}
}
}
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
vm.items.observe(viewLifecycleOwner) { newList ->
adapter.submitList(newList) // 새로운 리스트를 넘김.
}
}
}
DiffUtill
class DetailDiffCallback : DiffUtil.ItemCallback<RoutineItem2.Detail>() {
override fun areItemsTheSame(
oldItem: RoutineItem2.Detail,
newItem: RoutineItem2.Detail
): Boolean {
return (oldItem.id == newItem.id)
}
override fun areContentsTheSame(
oldItem: RoutineItem2.Detail,
newItem: RoutineItem2.Detail
): Boolean {
return oldItem == newItem
}
}
1번방법

RoutineItem
sealed interface RoutineItem {
val id: String
class Detail(
override var id: String = UUID.randomUUID().toString(),
val unit: String = "kg",
val set: Int,
var weight: String = "",
var reps: String = "",
) : RoutineItem
}
ViewModel
class DetailViewModel : ViewModel() {
private val _items: MutableLiveData<List<RoutineItem.Detail>> = MutableLiveData()
val items = _items
private val list: List<RoutineItem.Detail>
get() = _items.value ?: emptyList()
fun changeUnit(unit: String) {
if(list == null) { // 리스트가 없을때 토글 값을 설정하는 부분
// 어떻게 클래스의 unit 값을 설정해야할까?
return
} else {
val updatedList = list.map {
it.copy(unit = unit)
}
_items.postValue(updatedList)
}
}
}
1번 방법은 위 움짤을 보시면 아시겠지만 초기에 아이템이 추가되지 않은상태에서
토글 버튼을 lbs로 설정 해놓고 아이템을 추가했을때 그대로 kg으로 셋팅됩니다.
비정상적이죠.
1번방법에서는 RoutineItem.Detail의 unit이 프로퍼티로 들어가있습니다.
즉 추가 되는 각각의 아이템마다 unit 값을 가진다는 소리겠죠.
이제 ViewModel에서 맨처음 리스트가 존재하지 않을때 토글값을 설정해야하는데..
(if 구문 list == null 에 해당하는 부분)
이 부분을 어떻게 설정할 수 있을까요? 리스트라는 인스턴스가 생성되지도 않았을때
아이템이 추가되어야 하므로 클래스의 인스턴스가 생성되기전에 unit 값의 변경이 이루어져야하는데
어떻게 클래스의 unit 프로퍼티 값을 변경할 수있을까요?
2번방법

RoutineItem
sealed interface RoutineItem2 {
val id: String
data class Detail(
override var id: String = UUID.randomUUID().toString(),
val set: Int,
var weight: String = "",
var reps: String = "",
) : RoutineItem2 {
companion object {
var unit: String = "kg"
}
}
}
ViewModel
class DetailViewModel : ViewModel() {
private val _items: MutableLiveData<List<RoutineItem2.Detail>> = MutableLiveData()
val items = _items
private val list: List<RoutineItem2.Detail>
get() = _items.value ?: emptyList()
fun changeUnit(unit: String) {
if(list == null) {
RoutineItem.Detail.unit = unit
return
} else {
RoutineItem.Detail.unit = unit
val updatedList = list.map {
it.copy(id = unit) // id 가 UUID 이어야하는데 말이 안됨.
}
_items.postValue(updatedList)
}
}
}
두번째 방법입니다.
앱은 정상적으로 동작합니다. 초기 토글버튼에 따라 값이 잘 설정되어 나옵니다.
위 방법은 RoutineItem.Detail의 unit을 companion object로 설정했습니다.
생각해보니 unit 값은 각 아이템의 다른 프로퍼티와 달리 모든 아이템이 unit 값이 똑같이 일정하고 변경되기 때문에
클래스당 하나만 존재하고 모든 인스턴스가 공유하는 값이라고 생각했기때문에 companion object로 설정했습니다.
따라서 unit 값이 변경될때는 클래스 이름으로 접근을 했습니다. (-> RoutineItem.Detail.unit)
문제는 ViewModel 클래스에서 list를 copy하는 부분에 있습니다. 핵심이 변경된 값으로 copy 하여
새로운 리스트를 생성하고 postValue하여 observe하게 하는게 핵심인데,
val updatedList = list.map {
it.copy(id = unit) // id 가 UUID 인데 unit 값 주입은 굉장히 말이 안됨.
}
에서 id = unit 인게 좀 잘못되었죠.
새로운 리스트를 억지로 생성한다고 UUID로 설정되어있는 id 값에 억지로 unit 값을 넣어 새로운 리스트를
생성하는게 어색한 코드입니다.. 제가 동작의 실행을 위해 이렇게 해놓긴했는데
unit 값을 RoutineItem.Detail.unit으로 변경하는 이 방법에서 어떻게
새로운 리스트를 copy하여 생성할 수 있을까요?
꼭 copy가 아니더라도 새로운 리스트를 생성하고 postValue하여 observe에게 알릴수 있을까요?
둘중 어떠한 방법을 수정해서 사용하는것이 더 좋을까요?
++) 그리고 companinon object도 지금 var로 설정되어있는데, val 로 바꾸면서 값을 변경 할 수 있는 방법 있을까요? get 또는 set 함수를 만들어서 설정해야하 할까요?