Android databinding: set default visibility in xml - android

I show items in recyclerview and use databinding. In xml layout I has such view:
<include
android:visibility="#{viewmodel.expandable ? View.VISIBLE : View.GONE}"
bind:viewmodel="#{viewmodel}"
layout="#layout/full_station_layout"/>
It works well but I has one issue: while recyclerview initializing and bind items to views this layout flashes once on the screen although initial value viewmodel.expandable is false. So, I decided temporary hide this layout and tried using default-parameter in xml like this:
<include
android:visibility="#{viewmodel.expandable ? View.VISIBLE : View.GONE, default=View.GONE}"
bind:viewmodel="#{viewmodel}"
layout="#layout/full_station_layout"/>
But something went wrong:
error: 'View' is incompatible with attribute android:visibility (attr) enum [gone=2, invisible=1, visible=0].
So, or I incorrectly use this parameter or Google remove this keyword from xml databinding rules (I've seen example of usage default-keyword in xml on Google developers before, but now I couldn't)

You can set gone, visible, invisible in default property. Replace with below.
<include
android:visibility="#{viewmodel.expandable ? View.VISIBLE : View.GONE, default=gone}"
bind:viewmodel="#{viewmodel}"
layout="#layout/full_station_layout"/>

Check if you have already imported the View class.
<data>
<import type="android.view.View"/>
<variable ..... />
</data>
Also, the default correct syntax for default value for visibility is default=gone, no default=View.GONE

Related

How to hide views in layout editor using data binding in android

I am using data binding in my xml layout, I need to hide some views when user click on a button and its working perfectly fine.
Problem is that I need to place other views to places where hidden views were placed. But while editing layout in layout editor its showing all the layouts. How can I force editor to hide all those views that are bind with dynamic visibility, so I can place new views.
Here is what i am doing in my layout
<data>
<import type="android.view.View" />
<variable
name="isInEditState"
type="Boolean" />
</data>
this is how I set visibility which is working fine when I run app
android:visibility="#{isInEditState ? View.VISIBLE : View.GONE}"
Use tools:visibility="invisible" or tools:visibility="gone" to the views whose visibility is bound to databinding to not to show up in the layout editor
Ps: On adding this attribute if you get any error, just add the required namespace(toolsNs) by pressing alt+enter

How do I set the layout for ShimmerLayout programmatically in Kotlin?

NOTE: My question is different from this.
I want to set the layout of ShimmerLayout programmatically in Kotlin code. Is that possible to do?
Currently, I can set the layout via XML like this:
<com.facebook.shimmer.ShimmerFrameLayout
android:id="#+id/shimmerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{showSimmerBanner ? View.VISIBLE : View.GONE, default=gone}">
<LinearLayout
android:id="#+id/shimmerOrientation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="#layout/shimmer_banner" />
<include layout="#layout/shimmer_banner" />
<include layout="#layout/shimmer_banner" />
</LinearLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
Can we set the layout (#layout/shimmer_banner) from the Kotlin code?
So, the xml is like this:
<com.facebook.shimmer.ShimmerFrameLayout
android:id="#+id/shimmerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{showSimmerBanner ? View.VISIBLE : View.GONE, default=gone}"/>
And in Kotlin code call this xml like this: shimmerLayout.set = R.layout.shimmer_banner, but it doesn't have a function to set layout from Kotlin code programmatically.
How to do that via Kotlin code with still have a LinearLayout with vertical + can add more 1 <include> tag?
A layout included via an <include> tag is just inflated into the parent, so you can simply use a LayoutInflater to do it.
Assuming (from your question) that you have a reference to the shimmer layout as shimmerLayout, you can just do:
val inflater = LayoutInflater.from(shimmerLayout.context)
inflater.inflate(R.layout.shimmer_banner, shimmerLayout)
To change it if you already have views in it, you'd need to do shimmerLayout.removeAllViews(), then do the above.

Android databinding importing view into XML

I have seen 2 approaches for setting Visibility in XML using Databinding
<variable
name="vm"
type="com.example.myapp.viewmodel.MyViewmodel" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
style="#style/ProgressBarMediumWhite"
android:visibility="#{vm.showLoader ? View.VISIBLE : View.GONE}" />
<variable
name="vm"
type="com.example.myapp.viewmodel.MyViewmodel" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
style="#style/ProgressBarMediumWhite"
android:visibility="#{vm.showLoader}" />
I want to know which one is the better approach or both seems to be good.
How bad it is to Import android view in to the XML file.
The first one is the better.
Checks if showLoader is true then sets the views visibility to visible else gone.
The second one is passing a boolean showLoader as the view's visibility which should end up throwing an error.
Both approaches are the same, there is no difference.
However, there is one more way to create binding adapter and use it in xml layout files for changing visibility.
For example:
Create BindingAdapters.kt file and put this code in it
#BindingAdapter("goneUnless")
fun goneUnless(view: View, visible: Boolean) {
view.visibility = if (visible) VISIBLE else GONE
}
Now you can use this binding adapter like this
<ProgressBar
style="#style/ProgressBarMediumWhite"
app:goneUnless="#{vm.showLoader}" />
In most cases, both approaches work just fine.
However, when you have to make a choice like this, always choose the option that does not involve passing a view as a parameter. Unexpected events from the source (where the parameter comes from) might cause your app to misbehave or cause the view to render wrongly.
Therefore, the first approach seems to be more reliable.
android:visibility="#{vm.showLoader ? View.VISIBLE : View.GONE}"

Android Databinding: include forces <layout> tag?

I'm currently struggling with <include> of layouts and the obligatory <layout> tag for those.
I have a library which defines a layout of the Toolbar which should be used by other artifacts, regardless if they are using Databinding or not.
For the Databinding to work, the layout of the Toolbar needs to be wrapped inside a <layout> Tag.
Therefore I created two different includes of the Toolbar Layout.
include_toolbar.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
.../>
I tried to reuse the defined Toolbar and wrap it with layout tags like this
include_toolbar_binding.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<include
android:id="#+id/wrapper"
layout="#layout/include_toolbar"/>
</layout>
But that's not working, as this include needs to be wrapped again, right?
Is there any solution which does not require to define the Toolbar again in the (wrapped) binding include?
Android doesn't support include tags as the root. I can't remember whether or not data binding supports merge tags with include flags in the root, but I don't think so. You could try it and see. If they aren't supported, it is only data binding because Android normally does support it.
You can use:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android=...>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
.../>
</layout>
for your toolbar. You can then choose to load it with the normal data binding loader:
IncludeLoaderBinding binding = IncludeLoaderBinding.inflate(inflater, ...);
or you can use non-data binding inflation:
View included = inflater.inflate(...);
Android Data Binding will properly strip the layout file so that it can be used without data binding. You may see some problems if you use string tags as they are replaced and any binding expression will be stripped. If you're using a plain layout like you mentioned, you won't have any trouble including it from both a data binding layout file and a non-data binding layout file.
-- edit --
Based on the comments I understand that one application doesn't have data binding enabled and this won't be supported. If you're looking to get field access to the Toolbar through data binding and support non-data binding projects, this combined layout won't work. If you don't need field access to the toolbar, you can just include a non-data binding layout file from a data binding layout file.
You should consider using <merge>.
your toolbar.xml would look like this:
<merge>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
.../>
</merge>
Source: https://developer.android.com/training/improving-layouts/reusing-layouts.html
conclusion: it's not possible to use <include> as the root tag. (also not if the parent is <merge>).
therefore i need to define the Toolbar in both layouts separately.

Is it possible to conditionally show an XML include based on a classes method response?

I'm wondering if it's possible to use DataBinding to do conditionally show a layout based on a methods boolean response. Here's what I'm trying to do
XML Layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="View"
type="android.view.View"/>
<variable
name="App"
type="com.app.JTApp"/>
</data>
<include layout="#layout/view_checkout_total_cardview"
android:visibility="#{App.isLandscape() ? View.GONE : View.VISIBLE}" />
</layout>
JTApp class:
public class JTApp {
public boolean isLandscape() {
Timber.d("putty-- isLandscape: --------------------------");
return getResources().getBoolean(R.bool.is_landscape);
}
…
}
Currently this doesn't work. Am I missing something or is this not possible? I'm coming from the web where this is possible with frameworks like Angular.
Yes, using a conditional statement within XML is possible. I am not too familiar with data binding library, but a similar functionality is used in the documentation:
Zero or more import elements may be used inside the data element.
These allow easy reference to classes inside your layout file, just
like in Java.
<data>
<import type="android.view.View"/>
</data>
Now, View may be used within your binding expression:
<TextView
android:text="#{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{user.isAdult ? View.VISIBLE : View.GONE}"/>
I believe the only issue with your code is that you are using the View as a variable instead of as an import in your <data> element.
You can do this way easier with resource-modifiers:
have one layout in layout-land that does not contain the cardview
have another layout in layout that does contain the cardview
so you will get this effect and in landscape it is not even inflated and then set invisible

Categories

Resources