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.
Related
I want to set text using binding adapter.Here is the MyBindingAdapter.kt
#BindingAdapter("android:setTitle")
fun setTitle(textView : TextView,text: String){
textView.text = text
}
in activity_table.xml I used setTitle like this
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_column="1"
android:layout_weight="1"
android:gravity="center"
android:setTitle="Hello word"/>
and I getting below error in compile time
You need to change from #BindingAdapter("android:setTitle") to #BindingAdapter("setTitle") in your MyBindingAdapter.kt.
Also you need to change in xml file from android:setTitle="Hello word" to setTitle="Hello word"
It should work now!
Android studio will not consider #BindingAdapter("android:setTitle") while binding it, you need to write only #BindingAdapter("setTitle") to execute it perfectly, otherwise it will give that binding error while compile time.
First make sure you have to enclose you layout within the <layout> tag
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
..... </layout>
and #BindingAdapter("android:setTitle") change as #BindingAdapter("title")
it should work.
Help! I want to know whether we can create own custom Attributes for pre-existing android components like EditText, TextView, AutoCompleteTextView, MultiAutoCompleteTextView etc.
Using XML I need to implement a custom attribute/property to an autocompletetextview so that it do not show autocomplete suggestions if property is set false, and vice versa.
Yes you can. There is this thing that is called binding adapters, and you could use those as new xml attributes. Well, ofcourse, you have to enable data binding on your project to make the binding adapters to work.
Read more here: https://developer.android.com/topic/libraries/data-binding/binding-adapters
Bonus: If you are using kotlin, you could instead make these binding adapters into extension functions such that you could use them as an extension function for your objects.
Update
To xml attributes for pre-existing widgets, you first need to define a custom binding adapter. Here is an example of a custom binding adapter:
// This will change the text views background color and text when it is tapped
#BindingAdapter("changeBackgroundAndTextOnTap")
public static void changeBackgroundAndTextOnTap(final TextView view, boolean shouldChange) {
// The first parameter is the type of view this xml attribute will be available to
// The second is the value you will receive from the xml attribute
if (shouldChange) {
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
view.setBackgroundColor(Color.HSVToColor(new float[]{(int)Math.round(Math.random() * 360), 0.8f, 0.4f}));
view.setText("" + (Math.random() * 10000000000L));
}
});
}
}
But before we could use this, we should first tell android that we are using data binding, so in your app level build.gradle file, add this line:
android {
...
dataBinding {
enabled true
}
...
}
Next, to make data binding work on your xml files, you first have to wrap your layouts inside tags, like so:
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Don't Click Me!"
android:gravity="center"
android:textSize="32sp"
android:padding="24dp"
android:textColor="#dedede"
android:background="#000000"
tools:context=".MainActivity" />
</layout>
Then, on your activity, or fragment, you should set content view using databinding util:
private ActivityMainBinding mBinding; //Optional
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
Now that you are all set, you could now use your custom xml attribute a.k.a. data binding adapter on your layouts like this:
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Don't Click Me!"
android:gravity="center"
android:textSize="32sp"
android:padding="24dp"
android:textColor="#dedede"
android:background="#000000"
changeBackgroundAndTextOnTap="#{true}" // Note: the #{} is necessary
tools:context=".MainActivity" />
</layout>
For an example project, here is a github repo:
https://github.com/jianastrero/Android-Data-Binding-Example-In-Java
In Android's data binding documentation, it says that
You can also write adapters for android namespace.
I followed the sample code in that documentation and tried to bind to TextView's android:text setter.
This is the BinderAdapter I wrote:
#BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
view.setText(text + " - the BinderAdapter works!");
Log.d("MY_TAG", "the BinderAdapter works!");
}
and this is the layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is dummy text"/>
</RelativeLayout>
When I run the app I expect to see the text "This is dummy text - the BinderAdapter works!", however, the text I see is "This is dummy text". Moreover, looking at the logs I don't see the setText() being called.
I read (here) suggestions for using a namespace other than "android", however, it is important to me that I will not need to pay special attention and place different namespaces throughout my app.
What am I doing wrong?
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>
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.