I'm using databinding with a layout that include another, and I would like to pass the ressource id of layout to include as a parameter, so the included layout can be changed programmatically.
#layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="layoutResId"
type="int" />
<import type="android.view.View"/>
</data>
<android.widget.LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<include layout="#{layoutResId}"/>
</android.widget.LinearLayout>
</layout>
But i have this issue:
layout attribute must start with "#layout/."
You can add the id in the container layout where your include tag resides. For example:
<FrameLayout>
...
<include layout="#layout/main"
android:id="#+id/main_container" --> add id here
/>
</FrameLayout>
Related
A layout to be include is like this, child.xml:
<layout>
<data>
<variable
name="click"
type=""> <!-- What type should be here? -->
</data>
<LinearLayout>
<View onClick="#{click}"/>
</LinearLayout>
</layout>
To include this, parent.xml:
<layout>
<data>
<variable
name="viewModel"
type="ViewModel"/>
</data>
<LinearLayout>
<View onClick="()->viewModel.click1()"/>
<include
bind:click="()->viewModel.click2()"/>
layout="#layout/child"/>
</LinearLayout>
</layout>
So how can I pass the clickEvent to the child.xml only. Since different parent.xml has different ViewModel, I think I shouldn't pass the viewModel to the child.xml. But I don't know how to pass method to child.xml.
The solution now I'm doing is setOnClickListener in Java File.I doubt if
dataBinding can make it easier.
in child.xml
<variable
name="click"
type="android.view.View.OnClickListener"/>
<View onClick="#{click}"/>
in parent.xml
<include
layout="#layout/row"
app:click="#{()-> viewModel.click2()}"
/>
There can be many ways to pass variables in include tag, read #my answer.
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.
Android Studio 3.0
classpath 'com.android.tools.build:gradle:3.0.1'
set
dataBinding {
enabled = true
}
I want to use data binding.
Here my xml layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<data>
<variable
name="offer" type="com.myproject.customer.Offer" />
</data>
</android.support.constraint.ConstraintLayout>
But I get error:
Attribute is missing the Android namespace prefix
Your data-binding XML root should be layout tag
From Docs
Data-binding layout files are slightly different and start with a root tag of layout followed by a data element and a view root element. This view element is what your root would be in a non-binding layout file.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<android.support.constraint.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.constraint.ConstraintLayout>
</layout>
Context
XML layouts in Android can get complicated. Hence, it is a good practice to break them down into conceptually independent modules. Consider the following example:
Main layout:
<layout>
<data>
<variable name="someVar" type="some.custom.Type"/>
</data>
<SomeLayout
...
android:someAttribute="#{someVar.someProperty}" />
<include layout="#layout/some_other_layout />
</layout>
and some_other_layout.xml:
<SomeOtherLayout
...
android:someOtherAttribute="#{someVar.someOtherProperty}" />
Problem
Is it possible to use the same data-binding context (whatever is inside <data>) in two separated layouts (like in the given example)?
Doing this naively results in java.lang.IllegalStateException.
From the Data Binding Library documentation:
Variables may be passed into an included layout's binding from the containing layout by using the application namespace and the variable name in an attribute:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/name"
bind:user="#{user}"/>
<include layout="#layout/contact"
bind:user="#{user}"/>
</LinearLayout>
</layout>
With Android data binding it is possible to set a variable on a an included layout like so (from the documentation):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/name"
bind:user="#{user}"/>
<include layout="#layout/contact"
bind:user="#{user}"/>
</LinearLayout>
</layout>
I've tried doing the same thing to pass in variables when using a ViewStub, but it doesn't work. Why don't ViewStubs work like include layouts?
Passing data to ViewStubs is working as expected. You define your namespace and pass the variable in that namespace, and accept it as a regular <variable> in your ViewStub layout, as follows:
main_layout.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:my-namespace="http://schemas.android.com/apk/res-auto">
<data>
<variable name="myData" type="com.example.SomeModel" />
</data>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ViewStub
android:id="#+id/view_stub"
android:inflatedId="#+id/view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="#layout/another_layout"
my-namespace:data="#{myData}"
/>
</RelativeLayout>
</layout>
another_layout.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- No need to declare my-namespace here -->
<data>
<variable name="data" type="com.example.SomeModel" />
</data>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{data.someValue}" />
</RelativeLayout>
</layout>
ViewStubs are called out as being different in the Data Binding Library documentation.
ViewStubs
ViewStubs are a little different from normal Views. They start off invisible and when they either are made visible or are explicitly told to inflate, they replace themselves in the layout by inflating another layout.
Because the ViewStub essentially disappears from the View hierarchy, the View in the binding object must also disappear to allow collection. Because the Views are final, a ViewStubProxy object takes the place of the ViewStub, giving the developer access to the ViewStub when it exists and also access to the inflated View hierarchy when the ViewStub has been inflated.
When inflating another layout, a binding must be established for the new layout. Therefore, the ViewStubProxy must listen to the ViewStub's ViewStub.OnInflateListener and establish the binding at that time. Since only one can exist, the ViewStubProxy allows the developer to set an OnInflateListener on it that it will call after establishing the binding.