melatonina
07/13/2021, 9:32 AMgildor
07/13/2021, 9:32 AMmelatonina
07/13/2021, 9:35 AMclass RingViewModeSelector @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.ringViewModelSelectorStyle
) : LinearLayout(context, attrs, defStyleAttr), DefaultLifecycleObserver {
val binding = RingViewModeSelectorBinding.inflate(layoutInflater, this)
val modeStateFlow = MutableStateFlow(RingDisplayMode.Progress)
var mode by modeStateFlow
init {
loadAttributes(attrs, defStyleAttr)
binding.dotsIndicator.initDots(RingDisplayMode.values().size)
binding.root.setOnTouchListener(object : OnSwipeTouchListener(context) {
override fun onSwipeLeft() {
modeStateFlow.cycleForward()
}
override fun onSwipeRight() {
modeStateFlow.cycleBackward()
}
})
}
private fun loadAttributes(attrs: AttributeSet?, defStyleAttr: Int) {
context.useStyledAttributes(
attrs,
R.styleable.RingViewModeSelector, defStyleAttr,
R.style.RingViewModeSelector
) { array ->
}
}
fun registerLifecycleOwner(lifecycle: Lifecycle){
lifecycle.addObserver(this)
}
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
owner.lifecycleScope.launch {
modeStateFlow.collect {
updateDotSelection(it)
listeners.forEach { listener -> listener.onChange() }
}
}
}
val listeners = mutableListOf<InverseBindingListener>()
private fun updateDotSelection(mode: RingDisplayMode) {
binding.dotsIndicator.setDotSelection(mode.ordinal)
}
}
@BindingAdapter("ringDisplayMode")
fun <T> setRingDisplayMode(view: RingViewModeSelector, data: RingDisplayMode) {
view.mode = data
}
@InverseBindingAdapter(attribute="ringDisplayMode")
fun getRingDisplayMode(view: RingViewModeSelector) : RingDisplayMode =
view.mode
@BindingAdapter("ringDisplayModeAttrChanged")
fun setListeners(
view: RingViewModeSelector,
attrChange: InverseBindingListener
) {
view.listeners.add(attrChange)
}
The application does not compile if I don't provide all that.melatonina
07/13/2021, 9:37 AMCannot find a getter for <x.y.z.RingViewModeSelector app:ringDisplayMode> that accepts parameter type 'x.y.z.RingDisplayMode'
melatonina
07/13/2021, 9:41 AMapp:modeStateFlow="@={triadPlaybackAppModel.ringDisplayModeStateFlow}"
and
app:ringDisplayMode="@={triadPlaybackAppModel.ringDisplayModeStateFlow}"
with
@BindingAdapter("ringDisplayMode")
fun <T> setRingDisplayMode(view: RingViewModeSelector, data: StateFlow<RingDisplayMode>) {
view.mode = data.value
}
@InverseBindingAdapter(attribute="ringDisplayMode")
fun getRingDisplayMode2(view: RingViewModeSelector) : StateFlow<RingDisplayMode> =
view.modeStateFlow
melatonina
07/13/2021, 9:42 AMStateFlow
involved are MutableStateFlow
.gildor
07/13/2021, 9:43 AMmelatonina
07/13/2021, 9:44 AMgildor
07/13/2021, 9:44 AMgildor
07/13/2021, 9:45 AMgildor
07/13/2021, 9:45 AMfun getRingDisplayMode2(view: RingViewModeSelector) : StateFlow<RingDisplayMode>
should be
fun getRingDisplayMode2(view: RingViewModeSelector) : RingDisplayMode
gildor
07/13/2021, 9:46 AMmelatonina
07/13/2021, 9:58 AMCould not find event 'ringDisplayModeAttrChanged' on View type 'x.y.z.RingViewModeSelector'
I have to provide all the boilerplate binding implementation.melatonina
07/13/2021, 9:59 AMmelatonina
07/13/2021, 10:00 AMgildor
07/13/2021, 10:01 AMthen two-way databinding with StateFlow is not supportedOf course they are not supported, two-way bindings supported on MutableStateFlow
melatonina
07/13/2021, 10:01 AMmelatonina
07/13/2021, 10:01 AMgildor
07/13/2021, 10:02 AMI have to provide all the boilerplate binding implementation.No, if you bind to property which is standard, but in your case it’s not standard as I see
gildor
07/13/2021, 10:04 AMLook at the long code above. It works. It’s lots of code for each property that I have to bind two-way.Yes, you need this for every cuystom view property, it’s also true for all standard binding adapters
melatonina
07/13/2021, 10:04 AM[Mutable]StateFlow<T>
"
What standard property in any standard view on Android is implemented as MutableStateFlow<T>
?gildor
07/13/2021, 10:04 AMWhat standard propertyLike checked in Checkbox, text in TextView etc
gildor
07/13/2021, 10:05 AMgildor
07/13/2021, 10:05 AMgildor
07/13/2021, 10:05 AMmelatonina
07/13/2021, 10:06 AMgildor
07/13/2021, 10:07 AMgildor
07/13/2021, 10:10 AMgildor
07/13/2021, 10:13 AMCannot find a getter for <x.y.z.RingViewModeSelector app:ringDisplayMode> that accepts parameter type ‘x.y.z.RingDisplayMode’I think you have this issue because you don’t have event in your InverseBindingAdapter
gildor
07/13/2021, 10:13 AMmelatonina
07/13/2021, 10:30 AMMutableStateFlow<T>
. You have to write all the adapters and the notification by yourself, even if the property is observable.
It's a shortcoming of the databinding system.melatonina
07/13/2021, 10:31 AMmelatonina
07/13/2021, 10:41 AM@BindingAdapter("somethingAttrChanged")
fun setListeners(
view: View,
attrChange: InverseBindingListener
) {
// Your implementation
}
What if you bind and unbind multiple listeners? There is no way to remove a listener, unless you implement the notification mechanism as "single listener only", in which case you can swap a listener for another, but can't server multiple listeners.gildor
07/13/2021, 12:33 PMmelatonina
07/13/2021, 6:07 PMgildor
07/14/2021, 3:43 AMI know thatYour listener is non-nullable and it may crash on runtime If you have multiple listeners and want to unregister old one, there are also ways to do that, you can receive “old” version of param, see official doc, there is an example of
android:onLayoutChange
binding, which is exactly your caser: https://developer.android.com/topic/libraries/data-binding/binding-adapters#custom-logicgildor
07/14/2021, 3:47 AMThere is no specific support for two-way databindings on properties that are implemented as. You have to write all the adapters and the notification by yourself, even if the property is observable.MutableStateFlow<T>
It’s a shortcoming of the databinding system.I’m not quite sure that understand what you trying to achieve here.
the notification by yourselfBindings (starting from AGP 7.0) subscribe on notifications on your StateFlow and automatically update UI. For two-way bindings you need adapter for every property, there is no shortcuts there. Maybe I just don’t understand your case, but from what I see above you need a standard 2-way binding adapter
gildor
07/14/2021, 3:50 AM