android 与自定义视图的数据绑定
android data binding with a custom view
Android data binding guide 讨论了 activity 或片段中的绑定值,但是有没有办法通过自定义视图执行数据绑定?
我想做这样的事情:
<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>
虽然似乎可以通过在自定义视图上设置自定义属性来做到这一点,但如果要绑定很多值,这很快就会变得很麻烦。
有什么好的方法可以完成我想做的事情吗?
在您的自定义视图中,按照您通常的方式扩展布局,并为您要设置的属性提供 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。
首先,如果此自定义视图已经 <include>
在另一个布局中,例如 activity 等,请不要执行此操作。您只会得到有关标签为意外值的异常.数据绑定已经 运行 绑定,所以你已经设置好了。
您是否尝试过使用 onFinishInflate
到 运行 绑定? (Kotlin 示例)
override fun onFinishInflate() {
super.onFinishInflate()
this.dataBinding = MyCustomBinding.bind(this)
}
请记住,如果您在视图中使用绑定,将无法以编程方式创建它,至少即使您可以同时支持两者也会非常复杂。
android studio 中的图形编辑器不再能够呈现自定义视图。原因是,在以下代码中实际上没有膨胀视图:
public MyCustomView(...) {
...
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = MyCustomViewBinding.inflate(inflater);
}
我想绑定会处理 inflation,但是图形编辑器不喜欢它。
在我的特定用例中,我想绑定单个字段而不是整个视图模型。我想出了(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()
}
}
赞按钮由图像和文本视图组成。文本视图包含喜欢的数量,我想通过数据绑定设置它。
通过使用 setter for numberOfLikes 作为以下 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
这里已经有一些很好的答案,但我想提供我认为最简单的答案。
使用围绕它的布局标记创建您的自定义控件,就像任何其他布局一样。例如,请参见以下工具栏。这被用于每个 activity classes
<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()
}
请注意,我在设置父项的同时设置了子项的内容,这都是通过点符号访问完成的。只要将文件用布局标签包围起来,并命名,就很简单了。
现在,如果自定义 class 有它自己的关联代码 inflation,那么它可以很容易地在它的 onCreate 或构造函数中进行自己的绑定,但你明白了。如果您有自己的 class,只需在构造函数中添加以下内容以匹配其命名绑定 class。它遵循布局文件pascal cased的命名约定,所以很容易找到和自动填充。
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = NameOfCustomControlBinding.inflate(inflater);
希望对您有所帮助。
数据绑定即使合并也能正常工作,只有父级必须是 "this" 并附加到父级 true。
binding = DataBindingUtil.inflate(inflater, R.layout.view_toolbar, this, true)
今天,我想在自定义视图上使用数据绑定 class。但我不知道如何创建数据绑定到我的 class。所以我在 Whosebug 上搜索答案。
首先我尝试回答:
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();
}
希望对你有用!
当我尝试将子视图添加到主机片段(嵌套片段 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
用于 two-way 数据绑定
在Kotlin中我们可以直接使用ViewBinding:
class BenefitView(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {
init {
val binding = BenefitViewBinding.inflate(LayoutInflater.from(context), this, true)
val attributes = context.obtainStyledAttributes(attrs, R.styleable.BenefitView)
binding.image.setImageDrawable(attributes.getDrawable(R.styleable.BenefitView_image))
binding.caption.text = attributes.getString(R.styleable.BenefitView_text)
attributes.recycle()
}
}
Android data binding guide 讨论了 activity 或片段中的绑定值,但是有没有办法通过自定义视图执行数据绑定?
我想做这样的事情:
<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>
虽然似乎可以通过在自定义视图上设置自定义属性来做到这一点,但如果要绑定很多值,这很快就会变得很麻烦。
有什么好的方法可以完成我想做的事情吗?
在您的自定义视图中,按照您通常的方式扩展布局,并为您要设置的属性提供 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。
首先,如果此自定义视图已经 <include>
在另一个布局中,例如 activity 等,请不要执行此操作。您只会得到有关标签为意外值的异常.数据绑定已经 运行 绑定,所以你已经设置好了。
您是否尝试过使用 onFinishInflate
到 运行 绑定? (Kotlin 示例)
override fun onFinishInflate() {
super.onFinishInflate()
this.dataBinding = MyCustomBinding.bind(this)
}
请记住,如果您在视图中使用绑定,将无法以编程方式创建它,至少即使您可以同时支持两者也会非常复杂。
public MyCustomView(...) {
...
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = MyCustomViewBinding.inflate(inflater);
}
我想绑定会处理 inflation,但是图形编辑器不喜欢它。
在我的特定用例中,我想绑定单个字段而不是整个视图模型。我想出了(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()
}
}
赞按钮由图像和文本视图组成。文本视图包含喜欢的数量,我想通过数据绑定设置它。
通过使用 setter for numberOfLikes 作为以下 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
这里已经有一些很好的答案,但我想提供我认为最简单的答案。
使用围绕它的布局标记创建您的自定义控件,就像任何其他布局一样。例如,请参见以下工具栏。这被用于每个 activity classes
<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()
}
请注意,我在设置父项的同时设置了子项的内容,这都是通过点符号访问完成的。只要将文件用布局标签包围起来,并命名,就很简单了。
现在,如果自定义 class 有它自己的关联代码 inflation,那么它可以很容易地在它的 onCreate 或构造函数中进行自己的绑定,但你明白了。如果您有自己的 class,只需在构造函数中添加以下内容以匹配其命名绑定 class。它遵循布局文件pascal cased的命名约定,所以很容易找到和自动填充。
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = NameOfCustomControlBinding.inflate(inflater);
希望对您有所帮助。
数据绑定即使合并也能正常工作,只有父级必须是 "this" 并附加到父级 true。
binding = DataBindingUtil.inflate(inflater, R.layout.view_toolbar, this, true)
今天,我想在自定义视图上使用数据绑定 class。但我不知道如何创建数据绑定到我的 class。所以我在 Whosebug 上搜索答案。 首先我尝试回答:
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();
}
希望对你有用!
当我尝试将子视图添加到主机片段(嵌套片段 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
用于 two-way 数据绑定
在Kotlin中我们可以直接使用ViewBinding:
class BenefitView(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) {
init {
val binding = BenefitViewBinding.inflate(LayoutInflater.from(context), this, true)
val attributes = context.obtainStyledAttributes(attrs, R.styleable.BenefitView)
binding.image.setImageDrawable(attributes.getDrawable(R.styleable.BenefitView_image))
binding.caption.text = attributes.getString(R.styleable.BenefitView_text)
attributes.recycle()
}
}