I have a fragment layout fragment_config.xml, which cointains the following:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/tools">
<data>
<import type="android.view.View"/>
<variable name="viewModel" type ="...GlobalConfigViewModel"/>
</data>
...
<ToggleButton
android:id="#+id/btnShowAdvanced"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textOff="Show Advanced"
android:textOn="Hide Advanced"
android:checked="#={viewModel.advancedShown}"/>
<com.minh.minh.pumpnotifier.global.configuration.AdvancedBox
android:id="#+id/advancedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{viewModel.advancedShown ? View.VISIBLE : View.GONE}"
app:viewModel = "#{viewModel}"/>
...
</layout>
What is supposed to happen is that the visibility of Advanced Box toggles with the "checked"-state of the toggle button. I already confirmed that the two-way-databinding in the toggle-button sets the boolean "advancedShown" in the viewModel correctly. The "setVisibility"-method however is never called in the AdvancedBox-class (which extends LinearLayout).
Something else I tried is to set the visibility binding in the root element of the advanced_box.xml, since it also has a reference to the viewModel:
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="...GlobalConfigViewModel" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/advancedSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="#{viewModel.advancedShown ? View.VISIBLE : View.GONE}"
android:orientation="vertical">
Both methods do not work out though. My question is why it does not work and what would be the correct way to use databinding in this case?
Your viewModel.advancedShown should be ObservableField or marked with #Bindable annotation (in the second variant you need manually invoke notifyPropertyChanged(BR.advancedShown) on your GlobalConfigViewModel to trigger changes). You can find more information in official docs
Related
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}`
In my recyclerview adapter cell layout, I'm passing the presenter as a binding and calling
android:onClick="#{(view) -> mainPresenter.showDetail(booksItem)}"
from my layout XML to trigger showDetails method in a presenter.
But I've been told the presenter should not be passed in Databinding. Is there a valid reason for not passing the presenter and what would be the alternative to binding click actions on row cells.
<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="mainPresenter"
type="com.noisyninja.androidlistpoc.views.main.IMainPresenter" />
<variable
name="booksItem"
type="com.noisyninja.androidlistpoc.model.skoobe.BooksItem" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="#{(view) -> mainPresenter.showDetail(booksItem)}"
android:padding="#dimen/text_margin">
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" />
?
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)}
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>