Set ObservableArrayList<ModelClass> to RecyclerView using DataBinding - android

I want to update data items in the RecyclerView after API call. I have used ObservableArrayList. After api call, I have used notifyChange() in setter method. When I am passing the ObservableArrayList to the XML then I am getting error like this:
msg:Cannot find the setter for attribute 'bind:categories_list' with parameter type android.databinding.ObservableArrayList<T> on android.support.v7.widget.RecyclerView
XML is:
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="list"
type="ObservableArrayList" />
<import type="android.databinding.ObservableArrayList" />
</data>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/white">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
bind:categories_list="#{list}" />
</android.support.v7.widget.CardView>
</android.support.constraint.ConstraintLayout>
</layout>

You need to use the #BindingAdapter("categories_list") annotation where you are handling the list. Check this answer for more details
EDIT
Not sure if you followed the right approach. The syntax looks weird. Usually here:
<variable
name="list"
type="ObservableArrayList" />
goes a model class of your own, and importing it and using it with both declarations also look weird to me, did you try:
<variable
name="list"
type="android.databinding.ObservableArrayList" />
?

Related

Pass data to <include> layout using Databinding

I've been searching this a lot today and only found some unclear answers regarding this question.
I'm having multiple TextView and EditText in a form, all of them being customised by my theme and i'd like to reuse the same included layout each time, but having parameters to it like the text inside the TextView or hint inside the EditText.
My form.xml right now:
<LinearLayout android: orientation="horizontal">
<TextView android:text="Username"/>
<EditText android:id="edittext_username"
android:hint="Enter username..."
android:inputType="text"/>
</LinearLayout>
<LinearLayout android: orientation="horizontal">
<TextView android:text="Password"/>
<EditText android:id="edittext_password"
android:hint="Enter password..."
android:inputType="password"/>
</LinearLayout>
... other fields ...
What i'd like in the main form.xml:
<LinearLayout android:orientation="vertical" ...>
<include layout="form_edittext"
app:textview_text="#{`Username`}"
app:edittext_hint="#{`Enter username...`}"
... other parameters ...
/>
<include layout="form_edittext"
app:textview_text="#{`Password`}"
app:edittext_hint="#{`Enter password...`}"
... other parameters ...
/>
</LinearLayout>
also the form_edittext.xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="textview_text" type="java.lang.String"/>
<variable name="edittext_id" type="java.lang.String"/>
<variable name="edittext_inputType" type="java.lang.String"/>
<variable name="edittext_hint" type="java.lang.String"/>
</data>
<TextView android:text="#{textview_text}"/>
<EditText android:id="#{edittext_id}"
android:hint="#{edittext_hint}"
android:inputType="#{edittext_inputType}"/>
</layout>
I'm still a beginner and i do not know if this is possible.
On this post, the guy replied he used data binding (as in example shown by me)
How to Re-using Layouts with <include/> with parameters? (you may see the first answer).
However, using this method give me the error of XML not recognizing identifiers like "textview_text" (the variables that i'm accessing via #{...}. If you got other solutions i'd appreciate if you share them. Cheers!
UPDATE on iCantC's answer: (but the textview's text and the hint remain empty).
Main layout:
<?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">
...
<include layout="#layout/activity_login_layout_input"
android:id="#+id/activity_login_layout_input_emailAddress"
app:textviewt="#{#string/email}" //the string is "Email Address"
app:edittexth="#{#string/emailhint}" /> //the string is "Your email address..."
...
</layout>
Included layout:
<layout>
<data>
<variable
name="textviewt"
type="String"/>
<variable
name="edittexth"
type="String"/>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:showDividers="middle">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{textviewt}"
android:textColor="#color/c1"
android:textSize="12sp"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="26dp">
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/edittext"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_gravity="center|start"
android:layout_marginStart="26dp"
android:background="#drawable/activity_login_background"
android:hint="#{edittexth}"
android:padding="6dp"
android:textAlignment="center"
android:textColor="#color/c2"
android:textColorHint="#color/grey"
android:textSize="14sp"/>
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="start"
android:adjustViewBounds="true"
android:src="#drawable/activity_login_edittext_drawable" />
</FrameLayout>
</LinearLayout>
</layout>
It seems like you want to include a common layout and then pass dynamic parameters from the main layout to included layout, here are the steps,
Step 1: Enable DataBinding in your project
//In the build.gradle file in the app module, add this
android {
...
dataBinding {
enabled = true
}
}
Step 2: Create your common_layout_included.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="username"
type="String"/>
</data>
<LinearLayout
..
>
<TextView
android:id="#+id/tv_username"
android:text="#{username}"/>
</LinearLayout>
</layout>
Step 3: Include common_layout_included.xml in your main_layout.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
..
>
<include
android:id="#+id/includedLayout"
layout="#layout/common_layout_included"
app:username="#{#string/username}" // here we pass any String
/>
</LinearLayout>
</layout>
Step 4: Make sure you inflate the layout using DataBinding way
//In the onCreate of your Activity
val binding = DataBindingUtil.setContentView(this,R.layout.main_layout)
That's it, you are good to go. If still some error appears just do File -> Invalidate Caches/Restart
One last thing, I saw that you are assigning the id's to the view's dynamically android:id="#{edittext_id}", in all of my experience, I never really encountered a use case where I would be motivated to do this. I don't even know if it's possible and even if possible I doubt it's a good practice.

How to set a layout default value from parent layout using databinding and <include> tag in order to see it in preview?

I'm trying to use a view with databinding using the include tag multiple times.
It works great when the app runs, but in preview I don't manage to make it work in the parent.
Instead, I can do it on the view itself, but it implies that if I use it 5 times in the same layout, I'll have 5 exact copies. It's not a blocking issue, but for debugging/ UI, it makes things a bit more complicated.
Here's the code of the replicated view:
<?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>
<import type="android.view.View"/>
<import type="android.text.TextUtils"/>
<variable
name="title"
type="String" />
<variable
name="value"
type="String" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/white"
android:text="#{(!TextUtils.isEmpty(title) ? title : `Title`).concat(`:`), default=`Title:`}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#{!TextUtils.isEmpty(value) ? #color/darkBlue : #color/red, default=#color/red }"
android:text="#{(!TextUtils.isEmpty(value) ? value : `Unknown`), default=`Unknown`}" />
</LinearLayout>
</layout>
And here's how it's included:
<include layout="#layout/titled_entry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
bind:title="#{`Firstname`}"
bind:value="#{user.firstName}" />
In Android it looks like this
But in preview it looks like this
Does anyone know how to fix it ?
Thanks, and have a good day in those complicated times.
Store your string in string.xml
And set that as default value
Like below
android:text='#{viewModel.firstName, default=#string/firstName}`

How to initialise click listener on Recycler view adapter using Databinding?

I am going to make an universal adapter for all dynamic layouts , normally i handled all this things but i got stuck that how to initialise click listener using interface so that i define in whatever xml and get event in my class.
i am following this link:
https://developer.android.com/topic/libraries/data-binding/index.html
suppose this is my root xml of recycler view:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.MyHandlers"/>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{user.firstName}"
android:onClick="#{handlers::onClickFriend}"
//>>> i want to initialise interface for position/view instead of #{handlers::onClickFriend}.
/>
</LinearLayout>
</layout>
Please give me link and solution , i will be thankful to you.
You can pass either user/position. If you want to pass position inside clickListener you must have to pass it as variable in xml same as user, and then
android:onClick="#{() -> handlers.onClickFriend(user)}
Or
<variable name="position" type="Integer"/>
and then
android:onClick="#{() -> handlers.onClickFriend(position)}

Breaking down complex XML layouts using the same data-binding context

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>

How to bind layout with several qualifiers

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).

Categories

Resources