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 !
Related
I'm using Lottie and Epoxy in my Android project with DataBinding.
I would like to start animation with LottieImageView when RecyclerView is snapped, but LottieImageView does not start animation, while it is set to autoPlay=true.
The Epoxy controller is below:
class MainController : EpoxyController() {
var currentSnappedPosition = 0
set(value) {
field = value
requestModelBuild()
}
override fun buildModels() {
repeat(10) {
ItemBindingModel_()
.id(modelCountBuiltSoFar)
.isSnapped(currentSnappedPosition == modelCountBuiltSoFar)
.addTo(this)
}
}
}
currentSnappedPosition is updated when RecyclerView item is snapped, and then rquestModelBuild() is called.
The layout file is below:
<?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>
<import
type="android.view.View" />
<variable
name="isSnapped"
type="boolean" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.airbnb.lottie.LottieAnimationView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="#{isSnapped ? View.VISIBLE : View.GONE }"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="#raw/lunar_new_year"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:text="not snapped..."
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{isSnapped ? View.GONE : View.VISIBLE }"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</FrameLayout>
</layout>
LottieAnimationView is set visible when the item is snapped.
The whole project is on GitHub:
https://github.com/okuzawats/lottie-animation-with-epoxy
Does anyone know how to auto start when LottieAnimationView set visible?
I am trying to replicate this answer: Setting attribute of child element of included layout
I have a simple custom_edit_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="hint123" type="String" />
</data>
<android.support.design.widget.TextInputLayout
android:id="#+id/emailInputLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatEditText
android:id="#+id/emailField"
android:layout_width="275dp"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:paddingTop="14dp"
android:hint="#{hint123}"
android:textCursorDrawable="#null"
android:background="#drawable/edit_text_background"
android:fontFamily="#font/eina_regular"
android:textColor="#color/edit_text_color"
android:textColorHint="#color/edit_text_color"
android:textSize="15sp"
/>
</android.support.design.widget.TextInputLayout>
</layout>
And I include it in another file:
<?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">
<include
layout="#layout/custom_edit_text"
app:hint123="Email"/>
</layout>
However the project refuses to compile after a clean & rebuild with the error:
AAPT: error: attribute hint123 (aka inc.company.appname:hint123) not found.
Any ideas?
I also have
dataBinding {
enabled = true
}
enabled in the app level build.gradle
I think I've hit upon the solution. To activate data binding, you need to use a #{} expression, and what's in the braces must be valid Java code. So a literal string must be enclosed in quotes... which must be encoded as " inside an XML attribute value. Put it all together and you get:
<include
layout="#layout/custom_edit_text"
app:hint123="#{"Email"}"/>
Data binding does work for include files, it's just that the syntax for a literal is a bit convoluted. I had the same issue and this form is working in my project.
Bind the variables using #{ }
activity_main.xml
</layout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".simple.MainActivity">
<include layout="#layout/content_main_data_binding"
bind:name="#{ "Hello World" }" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
content_view.xml
</layout>
<data>
<variable
name="name"
type="String" />
</data>
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{ name }"/>
</layout>
GL
Problem is in the included layout. You can not set attribute hint123 in it.
Also, take a note that include is not supported as a direct child of layout tag.
Update your included XML code as below:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/custom_edit_text"
layout="#layout/custom_edit_text" />
</android.support.constraint.ConstraintLayout>
</layout>
To set hint using databinding, you have to set it in your java or kotlin file.
Here is java code:
public class YourActivity extends AppCompatActivity {
YourActivityBinding mBinding;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.your_activity);
mBinding.customEditText.setHint123("Email");
mBinding.customEditText.executePendingBindings();
}
}
I am using Android 3.1.1. And the following code is working for me, and if you can use it you will be able to reuse "hint" as you desired. I have a slightly altered layout file (custom_edit_text.xml) as follows.
<?xml version="1.0" encoding="utf-8"?>
<data>
<variable name="cName" type="String" />
<variable name="user" type="your.package.name.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.email}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.mobile}" />
<android.support.design.widget.TextInputLayout
android:id="#+id/emailInputLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.v7.widget.AppCompatEditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="#{cName.toString()}"
android:paddingBottom="16dp"
android:paddingTop="14dp"
android:textSize="15sp" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
Above the second "type" is package name + User class name.
I create the "User" class in a separate file as follows.
public class User {
String email;
String mobile;
User(String email, String mobile) {
this.email = email;
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public String getMobile() {
return mobile;
}
}
Inside MainActivity inside onCreate() I create the user object create the string and bind them.
String email = "xyz#yahoo";
String mobile = "9999";
User user = new User(email,mobile);
CustomEditTextBinding binding = DataBindingUtil.setContentView(this,R.layout.custom_edit_text) ;
binding.setCName("Yam May");
binding.setUser(user);
And I enabled binding in the app level build.gradle as you did.
A very detailed description about data binding can be found in https://www.vogella.com/tutorials/AndroidDatabinding/article.html
Just in case anyone misses one detail like me: you must enclose your parent layout content and the included layout content within "" and "". Without that binding is not used in a layout. I had the same issue when I had not enclosed my parent layout with these tags.
There are 2 alternatives to the quote-escaping solution #big_m provides.
You can single-quote the entire expression and use double-quotes around the string:
<include
layout="#layout/custom_edit_text"
app:hint123='#{"Email"}'/>
Or you can use backticks around the string:
<include
layout="#layout/custom_edit_text"
app:hint123="#{`Email`}"/>
I have a XML field that I want to change based on an 'ObservableBoolean' like this:
<?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>
<variable
name="isColored"
type="androidx.databinding.ObservableBoolean"
/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.consoleco.console.customView.PerTextView
android:layout_width="0dp"
android:layout_height="48dp"
android:background="#{isColored ? ?attr/colorAccent : #color/grayBackground"
android:gravity="center"
android:text="#string/save"
android:textColor="#color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
In this line of code above:
android:background="#{isColored ? ?attr/colorAccent : #color/grayBackground"
we can't use two '?' mark (IDE give error: expected, got ?'
How can I resolve this problem?
You can use a BindingAdapter for this (add this in a class):
#BindingAdapter("isColored")
public static void isColored(View view, boolean isColored){
TypedValue typedValueColor = new TypedValue();
view.getContext().getTheme().resolveAttribute(R.attr.colorAccent, typedValueColor, true);
if(isColored){
view.setBackground(typedValueColor.data);
} else {
view.setBackground(view.getContext().getResources().getColor(R.color.grayBackground));
}
}
And in your xml layout replace this:
android:background="#{isColored ? ?attr/colorAccent : #color/grayBackground"
With this:
app:isColored="#{isColored}"
Yes that is true. you can not use ?attr in your expression.
For now only work you can do is use #color/colorAccent instead of ?attr/colorAccent
Full answer:
1. Enable data binding in app/build.gradle:
dataBinding {
enabled true
}
2. Use DataBindingUtil to set content view
java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataBindingUtil.setContentView(this, R.layout.activity_engineering_mode_main);
}
3. Child item layout
You will see that I define 2 new attributes
values/bools.xml
<variable
name="textTitle"
type="String" />
<variable
name="buttonVisibility"
type="boolean" />
With textTitle, you can use any string from resource by #string/string_name
With buttonVisibility, you have to define bool resource type, and use #bool/bool_name
layout/item_engineering_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="textTitle"
type="String" />
<variable
name="buttonVisibility"
type="boolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="#dimen/engineer_actionbar_height"
android:background="#color/engineer_background_color"
android:orientation="horizontal">
<TextView
android:id="#+id/engineer_txtName"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="#dimen/engineer_text_margin"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="#{textTitle}"
android:textColor="#color/engineer_text_color"
android:textSize="#dimen/engineer_title_font_size" />
<Button
android:id="#+id/engineer_btnNext"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="#dimen/engineer_text_margin"
android:text="BACK"
android:visibility="#{buttonVisibility ? View.VISIBLE : View.GONE, default=gone}" />
</LinearLayout>
</layout>
4. Boolean resource file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="item_button_visibility_default">false</bool>
<bool name="item_button_visibility_on">true</bool>
<bool name="item_button_visibility_off">false</bool>
</resources>
5. Parent layout, which includes some children and passes value to new attributes
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/includedLayout0"
layout="#layout/item_engineering_list_row"
app:buttonVisibility="#{#bool/item_button_visibility_on}"
app:textTitle="#{#string/app_name}" />
<include
android:id="#+id/includedLayout1"
layout="#layout/item_engineering_list_row"
app:buttonVisibility="#{#bool/item_button_visibility_default}"
app:textTitle="#{#string/app_name}" />
</LinearLayout>
</layout>
Original Question:
I am new to android and I've been working with QML in QT for a time.
I wonder how I can make a layout more easier by applying params in XML in compound view components.
I have a custom layout item in xml and want to pass some attributes from a parent to its children, and I also want to initialize parent's attribute with new values to customize its children too.
My concept is as below:
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
!!!! how to declare a new attribute here !!!
| like this:
| textTitle="New Title" // default value for child
| buttonVisibility="visible" // default value for child
">
<TextView
android:id="#+id/engineer_txtTitle"
android:text= textTitle <--- use parent's />
<Button
android:id="#+id/engineer_btnBack"
android:visibility= buttonVisibility <== use parent's />
</LinearLayout>
CLICK HERE TO SEE IMAGE: base Item
main.xml
<LinearLayout>
<include
android:id="#+id/item1"
layout="#layout/item"
textTitle= "FIRST"
// buttonVisibility not set here, use default as visible
/>
<include
android:id="#+id/item2"
layout="#layout/item"
textTitle= "SECOND"
buttonVisibility = "gone" // dont show button
/>
</LinearLayout>
CLICK HERE TO SEE IMAGE: apply with param
You can use Data Binding of Architecture component. Here is an sample of your requirement.
Recently I answered a question related to this.
Clean answer
This example shows pass value to <include.
I have a common view layout_common.xml, I want to pass String to included layout. I will create a variable of type String. Refer that String to your TextView. I created passedText for example.
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
>
<data>
// declare fields
<variable
name="passedText"
type="String"/>
</data>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{passedText}"/> //set field to your view.
</layout>
Now you can pass passedText field to your <include tag.
activity_main.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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include
android:id="#+id/includedLayout"
layout="#layout/layout_common"
app:passedText="#{#string/app_name}" // here we pass any String
/>
</LinearLayout>
</layout>
Note that both layouts (parent & included) should be binding layout, i.e. wrapped with <layout> and </layout> tags
Thanks #Khemraj to show the keyword "Data Binding" in Android :)
I have found the answer for me. It includes Khemraj's answer and some small code added to values resource.
I posted it in my question for others to find it easily.
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());
}