尝试将数据绑定提供的上下文变量与 BaseObservable 一起使用时出错

Error when attempting to use the data binding provided context variable with BaseObservable

根据 and the Android Data Binding tutorial 我应该在数据绑定时有一个可用的 context 变量:

A special variable named context is generated for use in binding expressions as needed. The value for context is the Context from the root View's getContext(). The context variable will be overridden by an explicit variable declaration with that name.

我正在使用 class 扩展 BaseObservable 来设置我的数据,但无论我如何尝试在我的 @Bindable 方法中引入 context 变量我收到编译时错误:

java.lang.RuntimeException: failure, see logs for details. @Bindable associated with method must follow JavaBeans convention getStyledName(android.content.Context)

据我所知,我遵循 JavaBeans 约定。我已将错误追踪到 this line in BrUtil,但这也没有给我太多帮助。

我试过更改我的方法和参数名称,我试过为 context 添加 <variable> 和为 android.content.Context 添加 <import>,但是none 其中有任何不同。我下面的 User.java 有两个 @Bindable 方法,裸 getName() 可以正常工作,但是尝试使用 context 变量的方法没有,所以我认为这不是我的其余数据绑定设置也有问题。

相关代码:

activity_main.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="user" type="com.kasra.androidsandbox.User" />
    </data>

    <LinearLayout
        android:id="@+id/main_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/main_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        <TextView
            android:id="@+id/main_textview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="@{user.getStyledName(context)}" />
    </LinearLayout>
</layout>

User.java:

public class User extends BaseObservable {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Bindable
    public String getName() {
        return this.name;
    }

    @Bindable
    public CharSequence getStyledName(Context ctx) {
        int color = ctx.getResources().getColor(R.color.branded_pink);

        Spannable textSpan = new SpannableString(name);
        textSpan.setSpan(new ForegroundColorSpan(color), 0, textSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        return textSpan;
    }

}

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    User user = new User("Kasra");
    binding.setUser(user);

    Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
    setSupportActionBar(toolbar);
}

我不知道这是一个错误还是有意为之的行为,但我自己观察到了这种行为,所以你没有疯。

一种解决方法是在您的用户中创建一个未标记 @Bindable 的方法——需要上下文的方法。此方法不受 JavaBeans 命名约定的约束。

重要:为了确保它得到适当更新,它应该将任何可能更改的@Bindable 字段作为输入。

User.java

public class User extends BaseObservable {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Bindable
    public String getName() {
        return this.name;
    }

    public CharSequence getStyledName(Context ctx, String name) {
        // construct styled name here
    }
}

在您的布局中:

<TextView
        android:id="@+id/main_textview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@{user.getStyledName(context, user.name)}" />

Android 2.3 起,您可以在 @Bindable 注释中声明可绑定依赖的字段。

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Bindable {
  String[] value() default {};
}

在你的例子中,你会这样写你的函数

@Bindable("name")
public CharSequence getStyledName(Context ctx) {
    // your existing code
}

https://medium.com/google-developers/android-data-binding-dependent-properties-516d3235cd7c

我的实验是你只能在不接受参数的方法中使用@Bindable。例如:

@Bindable
public String getName() {
...
}

<TextView>
android:text="@{myClass.name}"
</TextView>

当我的方法看起来像这样时,我遇到了这个问题

    @Bindable
    public Boolean isRead() {
        return read;
    }

当我将函数的 return 类型更改为基本类型时,它解决了。也许对某人有用。

    @Bindable
    public boolean isRead() {
        return read;
    }

我喜欢这个

class MyApplication : Application() {

override fun onCreate() {
    super.onCreate()
    singleton = this
}

companion object {
    private var singleton: MyApplication? = null

    fun getInstance(): MyApplication? {
        return singleton
    }
}}

清单文件中的更改

<application
    android:name=".other.MyApplication">
  
</application>

在 class 中定义 baseurl 等的创建方法。

fun getResourceString(resourceId: Int): String? {
        return MyApplication
                .getInstance()!!
                .resources
                .getString(resourceId)
    }

现在在BaseObservable中使用了这个方法class

@Bindable
var email:String?=""
    set(value) {
        field = value
        notifyPropertyChanged(BR.email)
        notifyPropertyChanged(BR.emailError)
    }

@get:Bindable
val emailError: String?
    get() = if (email == null || email.toString().trim().isEmpty() || !Patterns.EMAIL_ADDRESS.matcher(email.toString().trim()).matches())
    {
        AppUtil.getResourceString(R.string.enter_valid_email)
    } else {
        null
    }

并在 xml 文件中

 <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/txtLoginEmail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:errorTextAppearance="@style/error_appearance"
        android:hint="@string/email"
        android:text="@={loginUser.email}"
        app:error="@{loginUser.emailError}"/>

希望这篇能帮到你..!