【问题标题】:How to use android data binding to bind an interface?如何使用android数据绑定绑定一个接口?
【发布时间】:2017-06-01 08:08:56
【问题描述】:

我的情况是我有一个片段,其文本字段将由 X1Object 或 X2Object 填充,它们都实现接口 IXObject 并扩展 Android DataBinding 库提供的 BaseObservable 类,但包含其他不同的字段和行为。 IXObject 包含 getter 和 setter 的方法。

public interface IXObject {
    void setName(String name);
    String getName();
}

public class X1Object extends BaseObservable implements IXObject {
    private String name;

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

    @Override
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
}

public class X2Object extends BaseObservable implements IXObject {...}

然后我尝试使用 Android DataBinding 为片段使用单个布局文件。布局如下所示:

<?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="xObject" type="com.test.x.model.IXObject"/>
</data>

<LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{xObject.name}"/>

</LinearLayout>
</layout>

在片段类中,我正在应用绑定:

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    MyFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false);

    View view = binding.getRoot();

    IXObject xobj = new X1Object();
    binding.setXObject(xobject);

    return view;
}

不幸的是,使用 IXObject 作为数据绑定引用,BaseObservable 类中的“addOnPropertyChangedCallback”方法永远不会被调用。

在布局文件和绑定类中直接使用X1Object进行绑定,一切正常。

您能帮我实现使用接口进行绑定的目标吗?

谢谢。

【问题讨论】:

    标签: android data-binding


    【解决方案1】:

    我也找不到让它与接口一起工作的方法,但我认为你可以像这样使用抽象类。

    public abstract class IXObject extends BaseObservable {
        public abstract void setName(String name);
    
        @Bindable
        public abstract String getName();
    }
    

    .

    public class X1Object extends IXObject {
        private String name;
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public void setName(String name) {
            this.name = name;
            notifyPropertyChanged(BR.name);
        }
    }
    

    .

    final IXObject xobj = new X1Object();
    xobj.setName("nguyen");
    binding.setXObject(xobj);
    

    【讨论】:

    • 如果你犯了和我一样的错误,得到“方法必须遵循 JavaBeans 约定”——getter 需要“get”前缀
    【解决方案2】:

    当你在你的布局文件中声明一个变量时,你必须从字面上理解它。接口不是对象。我知道 Java 支持通用内联对象,您可以在其中将接口声明为对象,但通常它们不是对象。

    所以当你对布局文件说使用接口类型的变量时,它真的不能做太多,因为它不是一个对象。

    您会注意到并讨厌 android 数据绑定的一件事是它无法推断类型。因此,如果您尝试使用实现类设置变量,但希望使用接口来设置变量,您将不会有任何运气。

    您需要指定将使用的类,而不是这些类继承自的接口。正如另一个答案所指出的,如果您的基类具有必要的方法,您可以使用基类。

    还请注意,您可以简单地将绑定放在私有成员变量上,它仍会自动通过 getter 和 setter。

    您唯一可以做的另一件事是创建一个适配器,该适配器采用接口或您的接口,甚至是带有强制转换的 Any,并为您设置名称。这需要在您已经增长的适配器类中再添加一个适配器,但至少它们是直截了当的。

    【讨论】:

      【解决方案3】:

      我想我找到了问题的根源。根据数据绑定文档,布局中设置的每个变量都必须实现 Observable 接口:

      变量类型是在编译时检查的,所以如果一个变量实现了 Observable 或者是一个 observable 集合,那应该反映在类型中。如果变量是未实现 Observable* 接口的基类或接口,则变量不会被观察到!

      所以没有机会使用接口。我必须使用上面答案中提到的抽象类。

      【讨论】:

        【解决方案4】:

        只需在界面中使用@get:Bindable 而不是@Bindable。 而是改用 LiveData:https://developer.android.com/topic/libraries/data-binding/architecture

        【讨论】:

          猜你喜欢
          • 2016-04-15
          • 1970-01-01
          • 2017-07-28
          • 2020-01-14
          • 2017-05-10
          • 1970-01-01
          • 2016-04-24
          • 1970-01-01
          • 2019-08-22
          相关资源
          最近更新 更多