Use data binding - change visibility using boolean operator - android

So I have a view:
<include
android:id="#+id/root"
layout="#layout/layout_awesome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isVisible="#{viewModel.itemA != null}" />
But in my case, root view visibility should be determined based on two model values in my viewmodel, so I would like to achieve something like this:
<include
android:id="#+id/root"
layout="#layout/layout_awesome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isVisible="#{viewModel.itemA != null && viewModel.itemB == null}" />
However, this does not work. Is there an issue with the syntax? I could not find an example where operators are used.

You should replace the logical operators like this :
'&' --> '&'
'<' --> '<'
'>' --> '>'
So, the correct xml will be this :
<include
android:id="#+id/root"
layout="#layout/layout_awesome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:isVisible="#{viewModel.itemA != null && viewModel.itemB == null}" />
reference :
android databinding using "&&" logical operator

Related

Android Databinding default variable value

I have this layout.
<data>
<import type="android.view.View" />
<variable
name="shouldBeVisible"
type="java.lang.Boolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="#+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="#={shouldBeVisible}" />
<LinearLayout
android:layout_width="40dp"
android:layout_height="30dp"
android:background="#dd0"
android:visibility="#{shouldBeVisible ? View.VISIBLE : View.GONE}" />
</LinearLayout>
shouldBeVisible is by default false. Is there a way to make shouldBeVisible true? I want to have LinearLayout visible in this one case.
So far I'm using binding.shouldBeVisible=true
To solve that error you can use several ways:
You can use primitive boolean type with inverted logic (shouldBeVisible -> shouldBeHidden)
Second way is to use boxed Boolean type (as you have now) and set default value in expressions
Third way - manually set it after inflating of binding (as you already do now)
binding.shouldBeVisible=true
Use default keyword in databining value
Choose the one best fits your needs.

How to use data binding to set layout_centerVertical in relativelayout

Say I have view that might be center vertical aligned in RelativeLayout. I want to use data binding to achieve this.
android:layout_centerVertical="#{data.shouldCenter ? true : false}"
I'm getting data binding error ****msg:Identifiers must have user defined types from the XML file. center is missing it ****\ data binding error **** using the above. How should I get this to work?
<?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="data"
type="com.test.MainViewModel" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{data.title}"
android:layout_centerVertical="#{data.shouldCenter ? true : false}"
android:textColor="#{data.titleTextColor}"
android:visibility="#{data.title != null ? View.VISIBLE : View.GONE}" />
<TextView
android:id="#+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/title"
android:layout_marginLeft="#dimen/marginLarge"
android:layout_marginTop="#dimen/marginSmall"
android:text="#{data.message}"
android:visibility="#{data.message != null ? View.VISIBLE : View.GONE}" />
</RelativeLayout>
</layout>
I have another solution, that is using BindingAdapter
#BindingAdapter(" android:layout_centerVertical")
public static void setCenterVertical(View view, boolean isCenterVertical) {
RelativeLayout.LayoutParams layoutParams =
(RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,
isCenterVertical ? RelativeLayout.TRUE : 0);
view.setLayoutParams(layoutParams);
}
and using it as:
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{data.title}"
android:layout_centerVertical="#{data.shouldCenter}"
android:textColor="#{data.titleTextColor}"
android:visibility="#{data.title != null ? View.VISIBLE : View.GONE}" />
hope this helps
You have to call method. Don't use direct variable in databinding. So you have to replace your code with below lines.
android:layout_centerVertical="#{data.shouldCenter() ? true : false}"
and you have to create getter method on your model like below :
boolean shouldCenter;
public boolean shouldCenter() {
return shouldCenter;
}
It will works for me. Check it and tell if it will not works !

Two-way binding Attribute is missing the Android namespace prefix <Data> <Variable>

Trying to implement two way binding
As mentioned:
Data Binding Library
Two-way Android Data Binding - How to use two-way Data Binding to manage a layout
2-way Data Binding on Android!
However on
<variable type="com.example.gideonsassoon.avariel.datamodels.Player" name="name"/>
I am getting the following message.:
"Attribute is missing the Android namespace prefix less... (Ctrl+F1)
Most Android views have attributes in the Android namespace. When
referencing these attributes you must include the namespace prefix, or
your attribute will be interpreted by aapt as just a custom attribute.
Similarly, in manifest files, nearly all attributes should be in the
android: namespace."
If I try to build it puts all the R. in my file in red and state they don't exist etc.
I have edited my build gradle file to have
dataBinding.enabled = true
Full code up to the point relevant below
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:windowSoftInputMode="adjustPan"
tools:context="com.example.gideonsassoon.avariel.ui.MainFragmentActivity">
<data>
<variable type="com.example.gideonsassoon.avariel.datamodels.Player" name="name"/>
</data>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v4.view.PagerTabStrip
android:id="#+id/viewpagerStrip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"/>
</android.support.v4.view.ViewPager>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="7dp"
android:layout_marginTop="34dp">
<TextView
android:id="#+id/tv_character_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/name_colon"
android:textAppearance="#style/TextAppearance.AppCompat.Title"
android:textIsSelectable="false" />
<EditText
android:id="#+id/et_character_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#={player.name}"
android:layout_alignBaseline="#+id/tv_character_name"
android:layout_marginStart="15dp"
android:layout_toEndOf="#+id/tv_character_name"
android:ems="12"
android:hint="#string/character_name"
android:inputType="textPersonName" />
Ahh it appears to be that I have it as relativeLayout. It can't be "binding" so it can only be layout. Not sure how that's gonna affect my files as a whole but I guess we'll just have to see how it plays out. Here's my source. However if anyone wants to answer to that effect it would be most appreciated.
Using data binding in Android - Tutorial

Cannot find the getter for attribute 'android:tag' - Android

I am using data binding, Here I am getting this issue:
Error:(252, 21) Cannot find the getter for attribute 'android:tag'
with value type java.lang.String on com.hdfcfund.investor.views.EditText.
Although, text attribute working fine but getting error while using tag element.
<?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">
<data>
<variable
name="presenter"
type="com.hdfcfund.investor.folio.step4addnominee.AddNomineePresenter" />
<variable
name="nominee"
type="com.hdfcfund.investor.folio.step1.model.NewInvestorFolioRequest.Nominee" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:clickable="true">
<com.hdfcfund.investor.views.EditText
android:id="#+id/et_country"
style="#style/EditTextStyleRegularGrey15"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableRight="#drawable/ic_arrow_input"
android:focusableInTouchMode="false"
android:hint="#string/label_country_1"
android:inputType="text"
android:onClick="#{()-> presenter.onSpinnerClick(spinnerCountry)}"
android:tag="#={nominee.nomineeAddress.countryCode}"
android:text="#={nominee.nomineeAddress.countryName}" />
</RelativeLayout>
</layout>
The android:tag attribute doesn't support two-way binding by default. This is because there is no event mechanism to notify when the attribute changes.
You probably intended to use one-way binding:
android:tag="#{nominee.nomineeAddress.countryCode}"
There is no way for the user to change the tag value, so two-way really isn't of a lot of use with that attribute, anyway.
You need to define #InverseBindingAdapter to return value from property:
#InverseBindingAdapter(attribute = "android:tag")
public static String getStringTag(EditText view) {
return String.valueOf(view.getTag());
}

Kotlin synthetic extension and several include same layout

How to access to view using kotlin synthetic extension if I have a layout like below:
file:two_days_view.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="#+id/day1"
layout="#layout/day_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
android:id="#+id/day2"
layout="#layout/day_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
file: day_row.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/dayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
How to access to dayName? I looked for some like this:
day1.dayName.text = "xxx"
day2.dayName.text = "sss"
I see in Studio that I have access to dayName but to which one of dayName TextView is reference to?
Normal if I have only one included layout it works fine. But now I have multiple times included same layout.
of course I can always do:
day1.findViewById(R.id.dayName).text = "xxx"
but I'm looking for nice solution. :)
As a general rule of thumb, you should not construct layouts that end up having multiple views with the same id - for this very reason.
But, to solve your problem:
Instead of importing
kotlinx.android.synthetic.main.layout.day_row.*
you can import
kotlinx.android.synthetic.main.layout.day_row.view.* (Notice the additional .view at the end).
This will import the views not as properties on the Activity/Fragment level, but instead as extension properties for View. That way, you can do it the way you want, assuming that day1 and day2 contain the views you want:
day1.dayName.text = "xxx"
day2.dayName.text = "sss"
In this case, use:
(day1 as TextView).text = ""
(day2 as TextView).text = ""

Categories

Resources