Двусторонняя привязка Android с целочисленным типом приводит к тому, что привязка данных не существует

У меня возникла проблема с реализацией двусторонней привязки с типом данных Integer.

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>

К сожалению, это дает мне ошибку

«Ошибка: (52, 17) Не удается найти получатель для атрибута 'android: text' с типом значения java.lang.Integer в android.support.design.widget.TextInputEditText.»

Если я изменю текст атрибута на

       <EditText android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@={Integer.toString(user.age)}" />

тогда я получаю сообщение об ошибке

«Ошибка: не удается создать привязки представлений java.lang.NullPointerException»

Цените любую помощь в этом.

ОБНОВЛЕНИЕ: похоже, возникла еще одна ошибка сразу после ошибки, упомянутой выше.

не может сгенерировать привязки представлений java.lang.NullPointerException

Не уверен, почему он дает мне NPE, хотя приложение еще не запущено.


person ads    schedule 17.08.2016    source источник
comment
Возможно, переключение Integer на int может решить эту проблему из-за автоматического преобразования с String в int или чего-то еще. Хакерский способ - сохранить возраст в виде String, а затем преобразовать его.   -  person Vucko    schedule 17.08.2016
comment
В моем объекте User изменено Integer на int, но по-прежнему возникает та же ошибка.   -  person ads    schedule 17.08.2016
comment
Попробуйте заменить Integer на ObservableInteger. Но тогда вам придется использовать метод .set(SOMENUMBER). См. этот пост для справки. Не забудьте использовать метод Integer.toString(), тогда   -  person Rittel    schedule 17.08.2016


Ответы (9)


Что ж, полгода спустя, но, может быть, я смогу кому-нибудь помочь.

Вы можете проделать такой простой трюк:

android:text="@={`` + mObject.someNumber}"

OBS.: вам нужна как минимум Android Studio 2.3.

person Eduvm    schedule 15.02.2017
comment
Мне пришлось очень долго искать это решение. Это, безусловно, самый простой способ, и он должен быть на высоте. Вы знаете, где я могу найти документацию по этому поводу? - person xerotolerant; 28.03.2017
comment
Извините, @xerotolerant Как и вы, я не нашел никакой документации по этому поводу. - person Eduvm; 29.03.2017
comment
Работает ли это, например, со значениями типа double и int одновременно? - person Draško; 13.05.2017
comment
Будет ли это работать для двустороннего бинидинга? Будет ли введенная строка преобразована в целое число? - person ArJ; 16.06.2017
comment
Отличный совет! Спасибо. На всякий случай, если некоторые из них похожи на меня, обратите внимание, что эти символы являются обратными кавычками (верхний левый угол вашей клавиатуры), а не одинарными кавычками. - person Hong; 17.11.2017
comment
Примечание: это работает только с примитивными типами, а не, например, с BigDecimal. - person Mikel; 17.01.2018
comment
Это должен быть принятый ответ, но, как только что сказал Хонг, имейте в виду, что вы не можете использовать ' или " внутри экранированного кода в XML, должны использовать обратную кавычку ` - person barnacle.m; 21.08.2018
comment
Недокументированный, работает только с некоторыми типами, требует обратного тика, не работает при двусторонней привязке данных в этих 7 условиях, решаемых путем написания @BindingAdapters и / или пользовательских методов конвертера или целых классов. Требуется конкретная версия Studio, новый XML-макет, <variable> или <import> узлы в зависимости от @jvmStatic, компилятор привязки данных для Kotlin, androidx библиотеки ... Мой БОГ. Android уже был худшим SDK в истории ДО того, как Google выпустил совершенно новый SDK поверх него. - person rmirabelle; 20.05.2019
comment
Для меня это не позволяло очистить значение и ввести. Последняя цифра остается в редактировании - person Tejasvi Hegde; 18.12.2019
comment
Можете ли вы мне помочь с этим: stackoverflow.com/questions/64596538/ Спасибо. - person Sam Chen; 29.10.2020

Каким-то образом я заставил это работать, используя BindingAdapter и 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());
    }
}

Надеюсь, это поможет и кому-то другому.

person ads    schedule 18.08.2016
comment
Привет, спасибо за это решение, но у меня все еще есть проблема: каждый символ, который я ввожу в EditText, сбрасывает курсор в начале текста. У вас такая же проблема? Какое решение этой проблемы? - person Roberto Leinardi; 21.09.2016
comment
Хорошо, я нашел решение: установите новый текст только в том случае, если текст view отличается от value в @BindingAdapter("android:text"). - person Roberto Leinardi; 21.09.2016

Мне удалось использовать Integer.toString (...), выполнив импорт, например:

<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>

person Fernando Carvalho    schedule 27.10.2018

Предыдущий ответ вместе с комментарием Роберто Лейнарди отлично сработал для меня! Я только должен добавить, что проверка Роберто должна быть нулевой:

@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));
    }
}
person Daniel Cantoreanu    schedule 29.12.2016
comment
Если вы попытаетесь начать писать отрицательное число, оно выйдет из строя (вы не можете преобразовать - в целое). - person Kamil; 06.10.2019

Вот мое решение. Это чисто и просто. Просто, если макету требуется String, дайте ему String вместо int. Все, что вам нужно сделать, это создать сеттер и получатель с типом String и использовать их для привязки к пользовательскому интерфейсу, в то время как обычные сеттер и получатель делают обычные вещи!

Полный код!

Мой класс POJO (Mydata.java). getAgeString и setAgeString - это методы пользовательского интерфейса, выполняющие преобразование. Обратите внимание, что я поставил @Bindable на getAgeString. поэтому пользовательский интерфейс будет использовать 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
        }
    }
}

Файл макета (activity_main.xml). используйте обычную двустороннюю привязку с @=, но используйте ageString вместо 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

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

Надеюсь, это кому-то поможет!

person mili    schedule 04.07.2017

Это может помочь некоторым людям, которым нужно заставить это работать с двусторонней привязкой данных и котлином.

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() ?: ""
        }
    }
}

импортировать этот класс в вашем представлении

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.package.DataBindingConverter" />
    </data>
.....

привязать его к текстовому представлению

<EditText
    ...
    android:text="@={DataBindingConverter.convertStringToInteger(ViewModel.user.age)}" />
person xdbas    schedule 19.03.2020
comment
Вот что я получаю, пожалуйста: cannot find method convertIntegerToString(java.lang.Integer) in class com.package.DataBindingConverters - person nyxee; 16.05.2020
comment
DataBindingConverter.INSTANCE.convertStringToInteger (ViewModel.user.age) - person Mervin Hemaraju; 23.08.2020

Способ использования решения @xdbas

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 импорт

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.package.DataBindingConverter" />
    </data>
.....

Привязать к textView

<EditText
    ...
    android:text="@={DataBindingConverter.convertStringToInteger(ViewModel.user.age)}" />

Возможно, мне следовало отредактировать его ответ, но я не знаю, не сработало ли это для него.

person nyxee    schedule 16.05.2020

Добавьте в strings.xml следующее:

<resources>
    <string name="_int">%d</string>
</resources>

Тогда вы сможете:

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{@string/_int(user.age)}" />
person Mathias Jeppsson    schedule 06.01.2019

person    schedule
comment
Это должно быть принято. Работает отлично и просто! - person stegnerd; 27.11.2019
comment
Это не позволяет редактировать. Вопрос касался двусторонней привязки. Он также должен поддерживать @ = {...} - person Tejasvi Hegde; 18.12.2019