简体   繁体   中英

Android two way binding with Integer type causes databinding does not exist

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.

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

android:text="@{String.valueOf(Integer)}"

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.

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>

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));
    }
}

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!

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)}" />

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.

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)}" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM