I have a custom layout that I want to reuse in several places, so I want to be able to pass a title to it, since that's the only value that will actually change. I know I can do that by binding data, but I can't get the data to render.
In my activity.main I have:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.r.e.MainActivity">
<include
layout="#layout/switch_preference_custom_title"
app:passedTitle="#{#string/hello_world}" />
</RelativeLayout>
</layout>
I have defined my custom layout as follows:
<?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="passedTitle"
type="String"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{passedTitle}" />
</RelativeLayout>
</layout>
I also added
dataBinding {
enabled = true
}
in my build.gradle. My project compiles and runs fine, and the layout does render (I can tell by giving it a background), but the text is an empty string.
It looks like one final linking piece was missing. Instead of having
setContentView(R.layout.activity_main);
in MainActivity, one needs to declare
DataBindingUtil.setContentView(this, R.layout.activity_main);
This connects the Data Binding library with the root layout, without which none of your data bindings will actually render.
Related
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 for the very first time. Everything was working as expected till I decided to create separate layout for landscape mode. Now, I have two layouts for main activity, respectively for portrait and landscape modes:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--Some Views-->
</LinearLayout>
</layout>
activity_main.xml (land):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.constraint.ConstraintLayout
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--Some Views-->
</android.support.constraint.ConstraintLayout>
</layout>
The problem is that I am unable to use binding.mainLayout as a ViewGroup because it is declared as a view in ActivityMainBinding.java:
public final android.view.View mainLayout;
Shouldn't it be declared as a ViewGroup, seeing that both LinearLayout and ConstraintLayout directly extend ViewGroup? Am I doing something wrong? Is there a logical reason behind this or is it just an oversight?
Together with ActivityMaindBinding, you should get autogenerated implementation for default & land layouts. They should be called ActivityMainBindingImpl and ActivityMainBindingLandImpl respectively.
In the runtime you can check with instanceof operator which one of the implemenations is in the use and access mainLayout with a proper type.
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.
I have 2 layouts: one for v19+ and another for earlier versions. They contain different views with different ids. How can I say Android DataBinding framework that I want to work with both layouts? It generates views only for one layout (selects randomly).
layout/temp_view.xml :
<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="block"
type="ru.temp.model.content.blocks.WebMediaBlock" />
<import type="ru.temp.model.Types.ProviderTypes" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:background="#android:color/white">
<ImageView
android:id="#+id/provider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingTop="#dimen/size5"
android:src="#{ProviderTypes.fromString(block.provider).getResId()}" />
</FrameLayout>
</layout>
layout-v19/temp_view.xml :
<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="block"
type="ru.temp.model.content.blocks.WebMediaBlock" />
<import type="ru.temp.model.Types.ProviderTypes" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:background="#android:color/white">
<ru.temp.utils.EmbedView
android:id="#+id/media_embed"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ru.temp.structure.static_material.CreditsView
android:id="#+id/credits_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</layout>
Update:
Finally I've found out the root of the problem. I don't know why but it does not generate *BindingImpl files when use minSdkVersion 21. If specify earlier versions it works as said #yigit
Data binding will generate a base class that serves as a common interface for both (the variables). Data binding will take care of creating the right instance when you call DataBindingUtil.setContentView(activity, R.layout.tmp_view).
For instance, in your example, it will generate
TempViewBinding, TempViewBindingImpl and TempViewBindingV19Impl.
You can check these classes inside <app module>/build/intermediates/classes/<your package>/databinding/ after you compile.
TempViewBinding is the base class and it will have the combination of variables and views for each layout.
If you are not seeing them in the IDE, might be an auto-completion bug in AS (please file a bug).
I Googled my problem but I can't find a solution.
When I try to create a signed APK, I get this error:
Error:(6) Error: Suspicious namespace and prefix combination [NamespaceTypo]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Explanation for issues of type "NamespaceTypo":
track these down.
xmlns:app="http://schemas.android.com/tools"
obscure error messages. This check looks for potential misspellings to help
Accidental misspellings in namespace declarations can lead to some very
This is the fragment of this layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/tools"
app:layout_behavior="#null"
android:layout_gravity="bottom|right">
change the code xmlns:app="http://schemas.android.com/tools" with this:
xmlns:app="http://schemas.android.com/apk/res-auto"
It made mine work.
Your first two lines of the xml code are incorrect. The whole xml file should look as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/tools"
app:layout_behavior="#null"
android:layout_gravity="bottom|right">
The first 2 lines are the declaration of the xml file. Although you are able to view the actual layout of the page in the design view, the layout itslef would still have issues when being built since it needs the xml tools tag.
The purpose of this namespace is to be able to record information in XML files, and have that information stripped when the application is packaged such that there is no runtime or download size penalty. It is a dedicated Android XML namespace.
Hope this helps :)
The tools namespace should be used for the preview tools of the xml on android studio. For example, if you are testing a view that is hidden by default, but you want to see it on your preview you should use tools:visibility=visible.
The app namespace, as far as I know, is used to add your custom views and layouts to the namespace of the xml you want to add your views.
So all your answers are correct, but I think no one explained what the namespaces do. So for convention I recommend to use them like this:
xmlns:yourAppName="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
I had this same error. My problem was that Android Studio automatically put an xmlns into my layout tab instead of the root view tag when using data binding.
In other words when I have Android Studio resolve the app prefix it did this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/tools"> <!-- added namespace here ... -->
<data>
<variable
name="viewModel"
type="com.example.ViewModel"/>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="..."
android:layout_height="..."
android:orientation="vertical"
app:backgroundResource="#{viewModel.someResource}"> <!-- ... when trying to resolve app -->
...
when it should have done this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:custom="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.ViewModel"/>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/tools" <!-- should have added here -->
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="..."
android:layout_height="..."
android:orientation="vertical"
app:backgroundResource="#{viewModel.someResource}">
...
change the attributes of tools in parent layout work for me like below**
xmlns:app="http://schemas.android.com/tools"
change this to the below
xmlns:app="http://schemas.android.com/apk/res-auto"