1

This is regarding something quite trivial that I have been fighting over in a code review. The scenario is such. There is a data binding layout:some_layout.xml, backed by a ViewModel.

[contents ofsome_layout.xml ]

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="android.view.View"/>
        <variable name="viewModel" type="some.long.package.name.SomeViewModel"/>
    </data>

    <!-- Other stuffs -->

    <TextView
        <!-- Other parameters-->>
        android:visibility="@{viewModel.someObservableBool?View.VISIBLE : View.GONE}"
    />
</layout>

The requirement, is such I want the TextView to be View.GONE (and not View.INVISIBLE), when the Boolean is false. The reviewer of my code is insistent of the fact,I should use the Boolean value directly instead of the ternary operator for android:visibility[android:visibility="@{viewModel.someObservableBool}"]

Now my understanding is that because of the implicit conversion of the truth values to int will cause the view to be Visible/Invisible but not Gone. As the attrs.xml definition states the following.

<!-- Controls the initial visibility of the view.  -->
<attr name="visibility">
    <!-- Visible on screen; the default value. -->
    <enum name="visible" value="0" />
    <!-- Not displayed, but taken into account during layout (space is left for it). -->
    <enum name="invisible" value="1" />
    <!-- Completely hidden, as if the view had not been added. -->
    <enum name="gone" value="2" />
</attr>

Is my understanding incorrect ?

Som
  • 83
  • 2
  • 9
  • Why don't you ask the reviewer to clarify - thats the point of the review - to have an open dialog. Personally xml should have no presentation logic, the contract IMO is that the ViewModel` should be a 1:1 representation of view state, which in this case it isn't. – Mark Feb 25 '20 at 00:11
  • I absolutely agree, but for some unfortunate reason this is one of those review iterations where every argument, ends at the point - "Please do this, because this is a convention we all agreed upon, and if you think it is so terrible please raise your concern in the appropriate room". I am just concerned if my understanding of the Bool to int conversion is correct. – Som Feb 25 '20 at 00:14
  • 1
    Sure - I'd argue the convension is also open for discussion and should not be set in stone, over time best practices can change/update. For this particular issue you should ask the reviwer for reference where this convension has been used with view states of `View.GONE` and `View.VISIBLE`. To me I'd provde the `int` value to the view directly, rather than a `boolean` as visibility, as you've pointed out is tri-state. – Mark Feb 25 '20 at 00:25
  • Thanks for your reply that sounds so much more sane! I had initially created an `enum` to reflect the visibility state (considering a more readable form), instead of passing a raw int. But I had to step down, as concerns were raised - that such a thing is a terrible over-complication. – Som Feb 25 '20 at 00:28

3 Answers3

1

There is no implicit type conversion in java, and integer values are not interpreted as booleans - it means that if you write

 <TextView
        <!-- Other parameters-->>
        android:visibility="@{viewModel.someObservableBool}"
    />

it just will not compile.

But if you add

@BindingConversion
fun booleanToVisibility(value: Boolean?) = if (value == true)View.VISIBLE else View.GONE

it should work. You can check related docs here

Yrii Borodkin
  • 772
  • 5
  • 7
  • Oh, but `android:visibility="@{viewModel.someObservableBool}"` this does compile without an error from the `DataBinding` compiler . I am not exactly sure of the reasoning for that, looks like there are some conversions happening in the generated binding code. BTW, Thanks for replying ! – Som Feb 25 '20 at 00:43
  • can you past compile stacktrace? – Yrii Borodkin Feb 25 '20 at 00:58
  • I stand corrected, there is an existing generic `BindingAdapter` [`@BindingAdapter("android:visibility") fun TextView.setVisibility(b: Boolean) = if (b) View.VISIBLE else View.GONE `]implemented in a common module. The generated data binding code is making a call to that with the `boolean` parameter. – Som Feb 25 '20 at 01:20
0

I would go with below approach provided the instance if viewModel and View are imported in XML

<TextView
    android:visibility="@{(viewModel.someObservableBool())? View.VISIBLE : View.GONE}" />

No need to import the Observable variable since the viewModel is already available in this context.

Faisal
  • 1,332
  • 2
  • 13
  • 29
-1

I am using it something like this way for email validation, something for visibility by ObservableBoolean:

XML:

<data>

    <import type="android.view.View" />

    <variable
        name="isProperEmail"
        type="androidx.databinding.ObservableBoolean" />
</data>

<!--    your other code here-->

    <androidx.appcompat.widget.AppCompatImageView
            ---------
            ---------
            android:visibility="@{isProperEmail ?View.VISIBLE : View.GONE}" />

and using it in class:

private val isProperEmail = ObservableBoolean(false)

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    -------------

    binding.isProperEmail = isProperEmail
}

Now, whenever you make changes to the value of this class' isProperEmail variable, then it will be changed and affected to the XML/Layout.

Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82