【问题标题】:android data binding with a custom viewandroid数据绑定与自定义视图
【发布时间】:2016-04-20 08:54:40
【问题描述】:

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>

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

有什么好方法可以完成我想做的事情吗?

【问题讨论】:

  • 您是否尝试在扩展客户视图后将数据绑定到 MyCustomView 类中?
  • 我尝试了一些方法来实现这一点,但绑定函数似乎需要包含在活动或片段中的信息。他们的示例没有提供有关如何在自定义视图中完成此操作的示例。非常感谢您提供有关如何完成此操作的简短代码 sn-p。
  • 仅供参考:我认为 xml 布局文件按照惯例用下划线表示法命名。
  • 是的,为了清楚起见更正了文件名。
  • 这篇文章解释了带有自定义视图和自定义属性的 2-way 数据绑定过程:medium.com/@douglas.iacovelli/…

标签: android data-binding custom-view


【解决方案1】:

在您的自定义视图中,按照您通常的方式扩展布局,并为您要设置的属性提供一个设置器:

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。

【讨论】:

  • MyCustomViewBinding 类型是在哪里创建的?
  • MyCustomViewBinding 作为数据绑定框架的一部分通过注释处理器生成。 xml 文件(Dave 使用了 MyCustomView.xml,但他真正的意思是 my_custom_view.xml,因为资源文件的大小写应该全部小写)导致默认的 Binding 类名称 MyCustomViewBinding(驼峰式,以 Binding 为后缀)。也可以自定义Binding类名:developer.android.com/tools/data-binding/…
  • stackoverflow.com/a/36068337/1369016(也由 George Mount 发布)中描述的解决方案对我有用。
  • 使用LayoutInflater.from(context),它更好——更短,而且您不必进行类型转换。
  • 另外,请记住定义并附加到根目录:inflate(inflater, this, true)。我需要它来显示自定义视图。
【解决方案2】:

首先,如果这个自定义视图已经在另一个布局中是&lt;include&gt;,请不要这样做,例如活动等。你只会得到一个关于标签是意外值的异常。数据绑定已经在其上运行了绑定,所以你已经设置好了。

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

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

请记住,如果您在视图中使用绑定,将无法以编程方式创建它,至少即使您可以同时支持两者也会非常复杂。

【讨论】:

  • 上帝保佑你伙计:))
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

    like 按钮由一个图像和一个文本视图组成。文本视图包含我想通过数据绑定设置的点赞数。

    通过使用 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

    【讨论】:

    【解决方案5】:

    今天,我想在我的自定义视图类上使用 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();
    }
    

    希望对你有用!

    【讨论】:

      【解决方案6】:

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

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

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

      希望对您有所帮助。

      【讨论】:

        【解决方案7】:

        当我尝试将子视图添加到主机片段(嵌套片段 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 进行双向数据绑定

        【讨论】:

          猜你喜欢
          • 2016-06-05
          • 1970-01-01
          • 1970-01-01
          • 2018-10-13
          • 2013-01-06
          • 2015-09-02
          • 1970-01-01
          • 1970-01-01
          • 2015-11-30
          相关资源
          最近更新 更多