Reputation: 1723
I'm having some issue with implementing two way binding with an Integer data type.
public class User {
private String firstName;
private String lastName;
private int age;
public User() {}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return this.firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return this.lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data class="UserDataBinding">
<variable
name="user"
type="com.databinding.model.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin">
<EditText android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={user.firstName}" />
<EditText android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={user.lastName}" />
<EditText android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={user.age}" />
</LinearLayout>
</layout>
Unfortunately, it gives me the error
"Error:(52, 17) Cannot find the getter for attribute 'android:text' with value type java.lang.Integer on android.support.design.widget.TextInputEditText. "
If I change the attribute text to
<EditText android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@={Integer.toString(user.age)}" />
then I get the error
"Error:cannot generate view binders java.lang.NullPointerException"
Appreciate any help on this.
UPDATE: It seems there was another error right after the error mentioned above.
cannot generate view binders java.lang.NullPointerException
Not sure why its giving me NPE even though the app hasn't started yet.
Upvotes: 47
Views: 39748
Reputation: 2811
The way of using @xdbas's solution
DataBindingConverter.kt
class DataBindingConverters {
companion object {
@InverseMethod("convertIntegerToString")
@JvmStatic
fun convertStringToInteger(value: String): Int? {
if (TextUtils.isEmpty(value) || !TextUtils.isDigitsOnly(value)) {
return null
}
return value.toIntOrNull()
}
@JvmStatic
fun convertIntegerToString(value: Int?): String {
return value?.toString() ?: ""
}
}
}
XML import
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.package.DataBindingConverter" />
</data>
.....
Bind to textView
<EditText
...
android:text="@={DataBindingConverter.convertStringToInteger(ViewModel.user.age)}" />
Maybe I should have edited his answer but i don't know if it didn't work for him.
Upvotes: 3
Reputation: 723
This might help some people who need to get this to work with two way databinding and kotlin.
DataBindingConverter.kt
class DataBindingConverter {
companion object {
@InverseMethod("convertStringToInteger")
@JvmStatic
fun convertIntegerToString(value: String): Int? {
if (TextUtils.isEmpty(value) || !TextUtils.isDigitsOnly(value)) {
return null
}
return value.toIntOrNull()
}
@JvmStatic
fun convertStringToInteger(value: Int?): String {
return value?.toString() ?: ""
}
}
}
import that class in your view
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.package.DataBindingConverter" />
</data>
.....
bind it to a textview
<EditText
...
android:text="@={DataBindingConverter.convertStringToInteger(ViewModel.user.age)}" />
Upvotes: 2
Reputation: 405
Add following in strings.xml:
<resources>
<string name="_int">%d</string>
</resources>
Then you can do:
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{@string/_int(user.age)}" />
Upvotes: 0
Reputation: 121
I managed to use Integer.toString(...), doing the import, 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>
<import type="java.lang.Integer" />
<variable ... />
</data>
Upvotes: 10
Reputation: 3802
Here is my solution. It's clean and simple. Simply if layout needs String, give it a String instead of int. All you have to do is create a setter and getter with String type and use them to bind to ui while normal setter and getter doing the normal thing!
A complete code !
My POJO class(Mydata.java). getAgeString
and setAgeString
are the ui methods, doing the conversion. Note that I put @Bindable
on getAgeString
. so ui will use ageString
package com.databindingnumber;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
public class MyData extends BaseObservable{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if(this.age != age) {
this.age = age;
notifyPropertyChanged(BR.ageString);//NOTE: ui is using ageString !
}
}
@Bindable
public String getAgeString() {
return Integer.toString(age);
}
public void setAgeString(String ageString) {
try {
int val = Integer.parseInt(ageString);
this.setAge(val);
}catch(NumberFormatException ex){
this.setAge(0);//default value
}
}
}
The Layout File(activity_main.xml). use normal two-way binding with @=
but use ageString
instead of age
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="foo" type="com.databindingnumber.MyData"/>
</data>
<EditText
android:layout_width="100dp"
android:layout_height="wrap_content"
android:inputType="numberSigned"
android:text="@={foo.ageString}" />
</layout>
MainActivity.java file
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setFoo(new MyData());
}
}
Hope this will help to someone!
Upvotes: 3
Reputation: 1139
Well, six months later but maybe i can help someone.
You can do this simple trick:
android:text="@={`` + mObject.someNumber}"
OBS.: You need at least Android Studio 2.3
Upvotes: 89
Reputation: 71
The previous answer, along with Roberto Leinardi's comment worked perfectly for me! I only have to add is that a null-check should be done to Roberto's check:
@BindingAdapter("android:text")
public static void setText(TextView view, int value) {
view.setText(Integer.toString(value));
}
@BindingAdapter("android:text")
public static void setText(TextView view, int value) {
if (view.getText() != null
&& ( !view.getText().toString().isEmpty() )
&& Integer.parseInt(view.getText().toString()) != value) {
view.setText(Integer.toString(value));
}
}
Upvotes: 7
Reputation: 1723
Somehow I got this to work by using BindingAdapter and InverseBindingAdapter.
public class User {
private String firstName;
private String lastName;
private int age;
public User() {}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return this.firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getLastName() {
return this.lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
@BindingAdapter("android:text")
public static void setText(TextView view, int value) {
view.setText(Integer.toString(value));
}
@InverseBindingAdapter(attribute = "android:text")
public static int getText(TextView view) {
return Integer.parseInt(view.getText().toString());
}
}
Hopefully this will help someone else as well.
Upvotes: 15