Android

[Refactoring] 여러개의 CheckBox -> Rx 핸들링

그란. 2021. 8. 5. 19:00

상품등록시 이런 형태의 UI 에서 Rx Subject + LiveData 처리 하는 방법 

 

-> 해당 아이템이 선택됐을때를 Subject<List<T>> 으로 묶는게 최종 목적 

private val _onDealTypeChanged: Subject<List<UserProduct.DealType>>

 


 

1. (기존) 체크박스에 CheckedChangeListener를 연결하여 뷰모델에서 combineLatest 로 묶는 방법 

 <CheckBox
                android:id="@+id/cb_direct"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_stroke_verylightpink_dustyorange_checked_selector"
                android:text="직거래"
                android:checked="@{output.isDirect}"
                android:textColor="@color/selector_lightperiwinkle_dustyorange"
                bind:checkedChanges="@{input.onDirectChanged}" />


@BindingAdapter("checkedChanges")
fun CheckBox.bindCheckedChanges(subject: Subject<Boolean>) {
    this.setOnCheckedChangeListener { _, isChecked ->
        subject.onNext(isChecked)
    }
}
 Observable.combineLatest(
            _onDirectChanged,
            _onDeliveryChanged,
            _onPostChanged,
            { direct: Boolean, delivery: Boolean, post: Boolean ->
                Triple(direct, delivery, post)
            }
        ).map { (direct, delivery, post) ->
            val dealTypeList = mutableListOf<UserProduct.DealType>()
            if (direct) dealTypeList.add(UserProduct.DealType.DIRECT)
            if (delivery) dealTypeList.add(UserProduct.DealType.DELIVERY)
            if (post) dealTypeList.add(UserProduct.DealType.POST)
        }.subscribe(_onDealTypeChanged::onNext)
            .addDisposable()
            
_onDirectChanged.bind(_isDirect)
...

 

 

-> 문제점 : 상품을 등록할때는 문제가 없다. 하지만 상품을 수정할때 기존 선택된 항목들을 보여준다고 했을때 

_onDealTypeChanged
            .map{ dealType->
                if(dealType.contains(UserProduct.DealType.DIRECT)) _onDirectChanged.onNext(true)
                if(dealType.contains(UserProduct.DealType.DELIVERY)) _onDeliveryChanged.onNext(true)
                if(dealType.contains(UserProduct.DealType.POST)) _onPostChanged.onNext(true)
            }.subscribe()
            .addDisposable()

dealTypeChanged 변수에 기존에 선택했던 항목들의 리스트를 받아와서 

각각 항목이 포함되어 있으면 각 CheckedChangeListener에 걸려있는 Subject를 true로 방출 

 

-> 이렇게 되면 다시 위의 combineLatest 가 동작하면서 무한루프가 돌게됨

 


2. 리팩토링 : CheckBox에 onClickListener를 연결, 

 <CheckBox
                android:id="@+id/cb_direct"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/bg_stroke_verylightpink_dustyorange_checked_selector"
                android:text="직거래"
                android:checked="@{output.isDirect}"
                android:onClick="@{()->input.onDealTypeDirectClick()}"
                android:textColor="@color/selector_lightperiwinkle_dustyorange"/>
private val _onDealTypeClick: Subject<UserProduct.DealType> = PublishSubject.create()

override fun onDealTypeDirectClick() = _onDealTypeClick.onNext(UserProduct.DealType.DIRECT)
override fun onDealTypeDeliveryClick() = _onDealTypeClick.onNext(UserProduct.DealType.DELIERY)
override fun onDealTypePostClick() = _onDealTypeClick.onNext(UserProduct.DealType.POST)
        _onDealTypeChanged
            .takePairWhen(_onDealTypeClick)
            .map { (dealType, type) ->
                dealType.edit {
                    if (contains(type)) {
                        remove(type)
                    } else {
                        add(type)
                    }
                }
            }
            .subscribe(_onDealTypeChanged::onNext)
            .addDisposable()

        _onDealTypeChanged
            .map { it.contains(UserProduct.DealType.DIRECT) }
            .bind(_isDirect)
        
        ...

(takePairWhen = withLatestFrom 연산자, Click 했을때 동작 )

 

-> 이렇게 연결하면 기존 선택된 값을 가져올때도 정상적으로 동작한다 

 

 

결론 : CheckBox의 CheckedChangeListener는 Subject와 연결해서 사용하면 이런 이슈들이 자주 생기는것 같다. 

OnClickListener 으로 연결하는게 번거롭긴 하지만 유지보수면에선 유리한 것 같다.