Android data Binding - ObservableArrayMap how to implement in a POJO - android

From the observable objects section of the android blog on data binding
The POJO class should extend BaseObservable unless your using ObservableFields. Let's look at my current POJO which is very simple and just stores an observableInt:
public class User {
public ObservableInt visible;
public User(int visible) {
this.visible=new ObservableInt(visible);
}
}
I want to add an Observable Collection to this pojo but im not clear how to layout the xml if i do that. How would i call it. Lets say i want to add the current arraymap to my POJO:
ObservableArrayMap<String, Object> myCollection = new ObservableArrayMap<>();
myCollection.put("firstName", "Google");
myCollection.put("lastName", "Inc.");
myCollection.put("age", 17);
How do i incorporate this into my xml for data binding. My xml file is simple and looks like this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.android.floatingactionbuttonbasic.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="my first textview"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="my second textview"
android:visibility="#{user.visible}"/>
</LinearLayout>
</layout>
I am confused how i would add variables for ObservableArrayMap into this xml file and be able to use it. Can someone help ?I feel i can pass it into the User POJO fine, its just from xml i dont know how i'd reference it? The reason im confused is because the ObservableArrayMap is going to be a attribute of the User class, how do i access it since its going to be a member of the User class ?

You can access the map values like any other public observable from your xml.
For example:
public class Model{
public ObservableArrayMap<Integer,String> MyMap=new ObservableArrayMap<>();
public Model(){
MyMap.put(1,"easy");
}
}
Access the value from your xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="model"
type="de.example.models.Model"/>
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="#dimen/padding"
android:text="#{model.MyMap.get(1)}"/>
</layout>

Related

*BindingImpl class : cannot inherit from final *Binding

I got this error in the generated BindingImpl class but the binding class is not final, it is abstract...
BindingImpl class :
public class FragmentImageBindingImpl extends FragmentImageBinding implements com.example.memoriserlesnombres.generated.callback.OnClickListener.Listener {
...
}
Binding class:
public abstract class FragmentImageBinding extends ViewDataBinding {
...
}
Any ideas ?
I already try to clean/rebuilt project, invalidate caches/restart and reinstall android studio without success. I saw another post saying to delete plugin files but they don't exist.
I'm new to android, but it seems very buggy to me :/
EDIT :
I still have the error (in the class) but my app works fine, so I think I can ignore it, this is just an Android Studio issue.
Here is the 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"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="view"
type="com.example.memoriserlesnombres.image.ImageViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".image.ImageFragment">
<TextView
android:id="#+id/current_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{Integer.toString(view.currentNumber)}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/current_number">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/associated_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/hint"
android:text="#={view.image}" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:contentDescription="#string/save_image"
android:onClick="#{() -> view.saveImage()}"
app:srcCompat="#drawable/ic_done_black_24dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
In one of your layout xml files, remove the <layout> tag and add it back.
This solved a similar issue in my project.
I had this error because I had multiple xml files for the same layout (e.g fragment_xxx, fragment_xxx_land, fragment_xxx_sw600dp etc.) changed one layout and forgot to update the other ones. So if you have multiple layouts, make sure that all have the same tags / are similar.
Also clean project and rebuild worked for me!
its because your viewmodel package is not same as the one it is in. like
the viewmodel class is laying somewhere else while the package inside the viewmodel class is pointing to somewhere else so please make it same as the location of viewmodel inside your viewmodel class
inshort recheck if your ImageViewModel is lying in same place as this one com.example.memoriserlesnombres.image.ImageViewModel
if the location is different from the package then change the package inside the ImageViewModel class to the location this class is in...
also change the ImageViewModel location in xml file too... and you are good to go...
this error sometime i mean very rarly arise when you copy a class from one package to another package inside the class...

Android: Pass different content from viewmodel to different include-tags

In my task app I want to use databinding for the task overview. I've grouped tasks in three categories: overdue, today and future.
My task_overview.xml defines the following viewmodel (which contains three arraylists with the different tasks)
<data>
<variable
name="viewModel"
type="de.taskapp.ui.taskOverview.viewmodel.TaskOverviewViewModel" />
</data>
A few steps later I include the layout for the three task lists with the include tag like this
<include layout="#layout/layout_task_list"
android:id="#+id/overdued_tasks_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/headline"/>
<include layout="#layout/layout_task_list"
android:id="#+id/todays_tasks_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/overdued_tasks_list"/>
<include layout="#layout/layout_task_list"
android:id="#+id/future_tasks_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/todays_tasks_list"/>
It would be awesome if I could do something like this to pass the different lists from the viewmodel to the layout_task_list.xml via the include tag
app:entries="#{viewModel.todaysTasks}" // for the todays task list case
My layout_task_list.xml looks like this
<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="tasks"
type="android.databinding.ObservableArrayList" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.AppCompatTextView
android:id="#+id/subheading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/SubheadlineWithBackground"
tools:text="Heute"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/task_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/spacing_large"
android:layout_marginRight="#dimen/spacing_large"
android:nestedScrollingEnabled="false"
app:entries="#{tasks}" //recyclerviewbinding is already there
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintTop_toBottomOf="#id/subheading"/>
</android.support.constraint.ConstraintLayout>
As you may suggest...the code is not working. This error message is shown
data binding error ****msg:Cannot find the setter for attribute 'app:entries' with parameter type android.databinding.ObservableList<de.taskapp.data.entity.Task> on de.molske.einfachmachen.databinding.LayoutTaskListBinding. file:/Users/app/src/main/res/layout/layout_task_overview.xml loc:38:40 - 38:60 ****\ data binding error ****
Okay yeah its telling me that he cannot find the setter for app:entries...but isn't there a "beautiful" way to pass the three lists to the three include-layouts? I don't like the idea to copy and paste the same layout three times just to be able to pass the different lists.
Thanks in advance.
(ps.: I know that there is the possibility to solve this by java / android code but I would like to know if there is a databind way to do this ;-)
Okay, classic me. After a few more googling I found the solution. Adding the next lines to the data-section of the included file everything works fine!
<import type="java.util.List"/>
<import type="de.molske.einfachmachen.data.entity.Task"/>
<variable name="title" type="java.lang.String"/>
<variable
name="tasks"
type="List<Task>"/>
It seems that it's important to define the exact type of the tasks variable. Because its type is a genereic < and > instead of < and > for defining the type.

Visibility not working in data binding android

I am using the latest data binding in android using android studio 2.1.
using the visibility tag as describe in the below code getting an error as
java.lang.RuntimeException: Found data binding errors.
/ data binding error ****msg:Identifiers must have user defined types from the XML file. View is missing it
file:D:\HP\HealthPortal_Android\Code\app\src\main\res\layout\cardview_image_twotextview.xml
loc:68:90 - 68:93
\ data binding error
<TextView
android:id="#+id/card_sub_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/card_title"
android:layout_marginLeft="#dimen/carview_margin"
android:layout_toRightOf="#+id/card_image"
android:text="#{toolsAndTrackersCards.subtitle}"
android:textColor="#color/black"
android:textSize="20sp"
android:visibility="#{toolsAndTrackersCards.subtitle.equals(#string/Empty_String) ? View.VISIBLE : View.GONE}"
/>
Done some google not abel to find the solution. The #string/Empty_String is define as empty string "" in string.xml file. where I am doing wrong.
Android data binding, Radio Button not updating
Add this to your cardview_image_twotextview.xml:
<data>
<import type="android.view.View" />
<!--your variables-->
</data>
To hide a view if the string is empty, use the below expression in data binding
<data>
<import type="android.view.View"/>
<variable
name="item"
type="com.test.model.Item" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{item.title}"
android:visibility='#{item.title.equals("") ? View.GONE : View.VISIBLE}'/>
NOTE: need to use outer single quote string in order to use double quote to
represent the empty string
If you want to check for null and empty, use the below code :
<data>
<import type="android.view.View"/>
<import type="android.text.TextUtils"/>
<variable
name="item"
type="com.test.model.Item" />
</data>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{item.title}"
android:visibility="#{TextUtils.isEmpty(item.title) ? View.GONE : View.VISIBLE}"/>
Zero or more import elements may be used inside the data element.
These allow easy reference to classes inside your layout file, just
like in Java.
you need to import View class inorder to use its properties.
<data>
<import type="android.view.View"/>
</data>
you can also refer official DataBinding guideline.

Android dataBinding - #BindingAdapter custom app namespace being ignored

I have created a custom bindingAdapter in android and when i pass in the color i want the color to change, this is actually for a test im working on just to make sure it works. Here is the code:
here is my view Model for the data binding:
public class User {
public ObservableInt visible;
public User(int visible) {
this.visible=new ObservableInt(visible);
}
#BindingAdapter({"app:bindColor"}) //notice the bindColor custom attribute
public static void setTextColor(TextView view,String color) {
if("green".equals(color))
view.setTextColor(Color.parseColor("#63f421"));
}
}
Now in my xml file which is binded to this model im expected to pass in a color so that the setTextColor method can use it:
<?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 class="MainActivityBinder">
<variable name="user" type="com.example.android.floatingactionbuttonbasic.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:id="#+id/tv_one"
android:text="my first textview"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_two"
android:text="my second textview"
android:visibility="#{user.visible}"
app:bindColor="#{'green'}" //see im passing in the green string here
android:textColor="#android:color/holo_green_dark"/>
</LinearLayout>
</layout>
I am getting the error at runtime time of:
Error:(27, 65) error: package com.example.android.floatingactionbuttonbasic.databinding does not exist
Warning:Application namespace for attribute app:bindColor will be ignored.
Error:(24, 33) Identifiers must have user defined types from the XML file. een is missing it
Error:Execution failed for task ':Application:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
if i take out the bindingAdapter stuff it works perfectly with the other databinding stuff. Its just this custom binding thats not working. My project is titled floatingactionbuttonbasic btw.
I was able to get this to work. The issue was how i was passing in the string. It should have the `` around it.
app:bindColor="#{`green`}"
you can also do this :
app:bindColor='#{"green"}'
But what seems to not be allowed is this notation:
app:bindColor="#{'green'}"
i wrote a blog about it to help others if interested.

Android data binding not working with View 'android:tag' property

Trying to use the new Android Data Binding in my project, but I'm getting an error when trying to set the 'android:tag' property to some custom variable.
My menu_item.xml file:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="menuItem"
type="com.example.MenuItem" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:tag="#{menuItem}"
tools:ignore="UseCompoundDrawables">
<!--suppress AndroidUnknownAttribute -->
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:imageResource="#{menuItem.itemType.drawableId}" />
<TextView
android:id="#+id/displayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{menuItem.itemType.displayNameId}" />
</LinearLayout>
</layout>
My MenuItem class:
public class MenuItem {
public final ItemType itemType;
public MenuItem(ItemType itemType) {
this.itemType = itemType;
}
}
Part of the genetated MenyItemBinding.java:
public MenuItemBinding(View root) {
super(root, 0);
final Object[] bindings = mapBindings(root, 3, sIncludes, sViewsWithIds);
this.displayName = (android.widget.TextView) bindings[2];
this.displayName.setTag(null);
this.icon = (android.widget.ImageView) bindings[1];
this.icon.setTag(null);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(root.getResources().getString(com.myApp.R.string.#{menuItem}));
setRootTag(root);
invalidateAll();
}
And the error is in the generated class, when trying to set the Tag of the bound view.
Any ideas how to get around this? Preferably, not to use a custom LinearLayout to support this.
That is a bug. We haven't tried data binding tags, mostly because tags are special.
When targeting devices pre-ICS, Android data binding takes over the tag of the outermost element of the layout. This tag is used for mostly for binding lifecycle and is used by DataBindingUtil.findBinding() and DataBindingUtil.getBinding().
So, since data binding isn't working on tags, the only work-around is to not supply a tag to your LinearLayout or supply a fixed tag or resource string. If you are targeting ICS and above, it is valid to reassign the tag after binding the layout:
MenuItemBinding binding = MenuItemBinding.inflate(layoutInflater);
binding.getRoot().setTag(menuItem);
You can also create a BindingAdapter for a new attribute:
#BindingAdapter("specialTag")
public static void setSpecialTag(View view, Object value) {
view.setTag(value);
}
and then use it in your layout:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:specialTag="#{menuItem}"
tools:ignore="UseCompoundDrawables"/>
This will allow you to use findViewByTag() and all of the other things you expect.
However, this will NOT work if you target Honeycomb and earlier devices. There is no getting around that. You may be tempted to do something like this:
#BindingAdapter("specialTag")
public static void setSpecialTag(View view, Object value) {
view.setTag(R.id.app_tag, value);
}
You won't be able to use findViewByTag with that approach, but it will store whatever value you want when you use your view. But the reason we don't use ID'd tags with Honeycomb and earlier is that there is a memory leak when using ID'd tags, so don't do it.
I hope this helps. I'll file a bug internally to support data bound android:tags.

Categories

Resources