android数据绑定与自定义视图

时间:2016-01-14 20:59:49

标签: android data-binding custom-view

Android data binding guide讨论活动或片段中的绑定值,但有没有办法使用自定义视图执行数据绑定?

我想做点什么:

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

    <com.mypath.MyCustomView
        android:id="@+id/my_view"
        android:layout_width="match_parent"
        android:layout_height="40dp"/>

</LinearLayout>

my_custom_view.xml

<layout>

<data>
    <variable
        name="myViewModel"
        type="com.mypath.MyViewModelObject" />
</data>

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{myViewModel.myText}" />

</LinearLayout>

</layout>

虽然可以通过在自定义视图上设置自定义属性来实现此目的,但如果要绑定很多值,这很快就会变得很麻烦。

有没有一种好方法可以完成我想要做的事情?

7 个答案:

答案 0 :(得分:52)

在自定义视图中,按照通常的方式对布局进行充气,并为要设置的属性提供一个setter:

private MyCustomViewBinding mBinding;
public MyCustomView(...) {
    ...
    LayoutInflater inflater = (LayoutInflater)
        context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mBinding = MyCustomViewBinding.inflate(inflater);
}

public void setMyViewModel(MyViewModelObject obj) {
    mBinding.setMyViewModel(obj);
}

然后在布局中使用它:

<layout xmlns...>
    <data>
        <variable
            name="myViewModel"
            type="com.mypath.MyViewModelObject" />
    </data>

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

        <com.mypath.MyCustomView
            android:id="@+id/my_view"
            app:myViewModel="@{myViewModel}"
            android:layout_width="match_parent"
            android:layout_height="40dp"/>

    </LinearLayout>
</layout>

在上面,为app:myViewModel创建了一个自动绑定属性,因为有一个名为setMyViewModel的setter。

答案 1 :(得分:6)

首先,如果此自定义视图在另一个布局中已经是<include>(例如活动等),请不要执行此操作。您只会获得有关标记为意外值的异常。数据绑定已在其上运行绑定,因此您已设置。

您是否尝试使用onFinishInflate来运行绑定? (Kotlin例子)

override fun onFinishInflate() {
    super.onFinishInflate()
    this.dataBinding = MyCustomBinding.bind(this)
}

请注意,如果您在视图中使用绑定,则无法以编程方式创建绑定,至少即使您可以支持这两种绑定也会非常复杂。

答案 2 :(得分:3)

Following the solution presented by george android studio中的图形编辑器无法再呈现自定义视图。原因是,在以下代码中实际上没有任何视图被夸大:

public MyCustomView(...) {
    ...
    LayoutInflater inflater = (LayoutInflater)
        context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mBinding = MyCustomViewBinding.inflate(inflater);
}

我认为绑定处理通货膨胀,但图形编辑器不喜欢它。

在我的特定用例中,我想绑定单个字段而不是整个视图模型。我提出了(kotlin传入):

class LikeButton @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    val layout: ConstraintLayout = LayoutInflater.from(context).inflate(R.layout.like_button, this, true) as ConstraintLayout

    var numberOfLikes: Int = 0
      set(value) {
          field = value
          layout.number_of_likes_tv.text = numberOfLikes.toString()
      }
}

“喜欢”按钮由图像和文本视图组成。文本视图包含我想通过数据绑定设置的喜欢的数量。

通过将numberOfLikes的setter用作以下xml中的属性,数据绑定会自动进行关联:

<views.LikeButton
  android:id="@+id/like_btn"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:numberOfLikes="@{story.numberOfLikes}" />

进一步阅读:https://medium.com/google-developers/android-data-binding-custom-setters-55a25a7aea47

答案 3 :(得分:3)

即使仅合并父级也必须是“ this”并附加到父级true时,数据绑定才有效。

binding = DataBindingUtil.inflate(inflater, R.layout.view_toolbar, this, true)

答案 4 :(得分:3)

今天,我想在Custom View类上使用dataBinding。但是我不知道如何创建与我的班级的数据绑定。所以我在StackOverflow上搜索答案。 首先,我尝试答案:

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
BottomBarItemCustomViewBinding binding = BottomBarItemCustomViewBinding.inflate(inflater);

但是,我发现这不适用于我的代码

所以我改变了另一种方法:

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
BottomBarItemCustomViewBinding binding = DataBindingUtil.inflate(inflater, R.layout.bottom_bar_item_custom_view, this, true);

为我工作。

完整的代码是: bottom_bar_item_custom_view.xml

<data>

    <variable
        name="contentText"
        type="String" />

    <variable
        name="iconResource"
        type="int" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center">

    <ImageView
        android:id="@+id/bottomBarItemIconIv"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginTop="2dp"
        android:src="@{iconResource}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/bottomBarItemContentTv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="@{contentText}"
        android:textColor="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/bottomBarItemIconIv" />


</androidx.constraintlayout.widget.ConstraintLayout>

BottomBarItemCustomView.java

public class BottomBarItemCustomView extends ConstraintLayout {

public BottomBarItemCustomView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
    //use dataBinding on custom view.
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    BottomBarItemCustomViewBinding binding = DataBindingUtil.inflate(inflater, R.layout.bottom_bar_item_custom_view, this, true);

    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BottomBarItemCustomView);
    int iconResourceId = typedArray.getResourceId(R.styleable.BottomBarItemCustomView_bottomBarIconResource, R.drawable.my_account_icon);
    binding.setIconResource(iconResourceId);

    String contentString = typedArray.getString(R.styleable.BottomBarItemCustomView_bottomBarContentText);
    if (contentString != null) {
        binding.setContentText(contentString);
    }

    typedArray.recycle();
}

希望对您有用!

答案 5 :(得分:0)

这里已经有一些不错的答案,但是我想提供我认为最简单的答案。

使用周围的布局标签创建自定义控件,就像其他任何布局一样。例如,请参见以下工具栏。在每个活动类别中都会使用它

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
    <variable name="YACustomPrefs" type="com.appstudio35.yourappstudio.models.YACustomPreference" />
</data>

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/YATheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            app:popupTheme="@style/YATheme.PopupOverlay"/>

    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>

现在,此自定义布局是每个Activity的子级。您只需在onCreate绑定设置中将其视为此类。

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.yaCustomPrefs = YACustomPreference.getInstance(this)
    binding.toolbarMain?.yaCustomPrefs = YACustomPreference.getInstance(this)
    binding.navHeader?.yaCustomPrefs = YACustomPreference.getInstance(this)

    binding.activity = this
    binding.iBindingRecyclerView = this
    binding.navHeader?.activity = this

    //local pointer for notify txt badge
    txtNotificationCountBadge = txtNotificationCount

    //setup notify if returned from background so we can refresh the drawer items
    AppLifeCycleTracker.getInstance().addAppToForegroundListener(this)

    setupFilterableCategories()
    setupNavigationDrawer()
}

注意,我在设置父级对象的同时设置了子级对象的内容,这都是通过点符号访问来完成的。只要文件被布局标签包围并且您为它们命名,就很容易做到。

现在,如果自定义类具有自己的关联代码膨胀,那么可以轻松地在onCreate或构造函数中进行自己的绑定,但是您会得到图片。如果您有自己的类,只需在构造函数中抛出以下内容以匹配其命名绑定类。它遵循pascal大小写的布局文件的名称约定,因此易于查找和自动填充。

    LayoutInflater inflater = (LayoutInflater)
    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = NameOfCustomControlBinding.inflate(inflater);

希望有帮助。

答案 6 :(得分:0)

当我尝试在主机片段(嵌套片段UI / UX)中的LinearLayout中添加子视图时,我遇到了相同的问题

这是我的解决方案

var binding: LayoutAddGatewayBinding? = null
binding = DataBindingUtil.inflate(layoutInflater, R.layout.layout_add_gateway,
            mBinding?.root as ViewGroup?, false)
binding?.lifecycleOwner=this
val nameLiveData = MutableLiveData<String>()
nameLiveData.value="INTIAL VALUE"
binding?.text=nameLiveData

这里 mBinding 是子片段 ViewDataBinding 对象,我已经使用 nameLiveData 了两个双向数据绑定

相关问题