这实际上是2个问题。
Person
中,数据绑定(bind)不起作用。我设置的数据类name
参数为 val
而不是 var
.代码将中断并出现以下错误:error: cannot find symbol import com.example.android.aboutme.databinding.ActivityMainBindingImpl; ^ symbol: class ActivityMainBindingImpl location: package com.example.android.aboutme.databinding
为什么会这样?
invalidateAll()
在 doneClick()
?文档说它“使所有绑定(bind)表达式无效并请求新的重新绑定(bind)以刷新 UI”。数据绑定(bind)的目的不就是以这样一种方式连接数据和 View ,即对数据的更新会立即更新 View 吗? 主要 Activity :
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
val person = Person("Bob")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.person = person
binding.apply {
btnDone.setOnClickListener { doneClick(it) }
}
}
private fun doneClick(view: View) {
binding.apply {
person?.nickname = etNickname.text.toString()
invalidateAll()
etNickname.visibility = View.GONE
tvNickname.visibility = View.VISIBLE
btnDone.visibility = View.GONE
}
hideKeybord(view)
}
private fun hideKeybord(view: View) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
人:
class Person(var name: String, var nickname: String? = null)
Activity 主.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">
<data>
<variable
name="person"
type="com.example.android.aboutme.Person" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="@dimen/padding"
android:paddingEnd="@dimen/padding">
<TextView
android:id="@+id/tv_name"
style="@style/NameStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={person.name}"
android:textAlignment="center" />
<EditText
android:id="@+id/et_nickname"
style="@style/NameStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/what_is_your_nickname"
android:inputType="textPersonName"
android:textAlignment="center" />
<Button
android:id="@+id/btn_done"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/layout_margin"
android:fontFamily="@font/roboto"
android:text="@string/done" />
<TextView
android:id="@+id/tv_nickname"
style="@style/NameStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={person.nickname}"
android:textAlignment="center"
android:visibility="gone" />
<ImageView
android:id="@+id/star_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin"
android:contentDescription="@string/yellow_star"
app:srcCompat="@android:drawable/btn_star_big_on" />
<ScrollView
android:id="@+id/bio_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/layout_margin">
<TextView
android:id="@+id/bio_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="@dimen/line_spacing_multiplier"
android:text="@string/bio"
android:textAppearance="@style/NameStyle" />
</ScrollView>
</LinearLayout>
</layout>
最佳答案
问题一:
I noticed that databinding doesn't work if in the Person data class I set the name parameter to be val instead of var.
Why does it happen?
因为您使用的是 two-way databinding .
在您的布局中,您有以下内容:
<TextView
android:id="@+id/tv_name"
style="@style/NameStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={person.name}"
android:textAlignment="center" />
@=
在 android:text="@={person.name}"
,具体来说,告诉数据绑定(bind)“我想将 TextView
的文本设置为该人的 name
值,并且我想在 name
文本更改时更新该人的 TextView
”。当您使用
@=
数据绑定(bind)将为您正在分配的属性寻找一个 setter 。在这种情况下,它正在寻找 name
的 setter。 Person
上的属性类(class)。在 Kotlin 中,这意味着拥有一个名为 name
的属性。那是一个 var
.如果您不打算更新此人的
name
TextView
时的属性更改(我假设您没有,您通常会使用 EditText
),然后将该行更改为 @
(android:text="@{person.name}"
)。然后你可以制作name
一个 val
因为你只是从它读取数据绑定(bind)。问题二:
Why do I need to call invalidateAll() in doneClick()?
你其实不...
The documentation says that it "Invalidates all binding expressions and requests a new rebind to refresh UI". Isn't the purpose of databinding to connect data and views in such a way that an update to the data immediately updates the views?
是的,但是:数据绑定(bind)不是魔术。如果 UI 要更新,则必须告诉它这样做,并且更改数据不会神奇地告诉数据绑定(bind)它必须更新。有些东西必须告诉数据绑定(bind) a) 是时候更新了,b) 它需要更新什么。
所以你现在拥有
invalidateAll()
是霰弹枪的方法。您更新了 nickname
字段,然后您对数据绑定(bind)大喊“嘿,更新所有内容!”,因此它会根据 Person
的当前状态重新绑定(bind)所有 View 其中当然包括“昵称”,以便更新 View 。您要做的是仅更新绑定(bind)到
nickname
的字段。因为这是改变的一件事,最好是在 nickname
时自动完成。变化。为此,您需要观察 nickname
的状态。场并对其变化使用react。您可以通过以下几种方式做到这一点:
在这种方法中,您要绑定(bind)的模型字段为
LiveData
对象( val nickname = MutableLiveData<String>()
),然后添加 LifeCycleOwner
到绑定(bind),以便它可以观察 LiveData
对象。数据绑定(bind)设置为使用
LiveData
所以你的xml不需要改变。但是现在属性是可观察到的,当您在 Person
上更新名称时( person?.nickname?.value = "New Nickname"
) 数据绑定(bind)将被自动通知并更新相关 View 的状态。您不必调用
invalidateAll()
.这在概念上与 #1 相同,但在
LiveData
之前出现。被介绍了。现在你可以考虑弃用它并使用 LiveData
方法,但为了完整起见,我会提到它。再次,而不是拥有
String
类型的常规属性。您将该属性包装在一个可观察的数据结构( val nickname = ObservableString()
)中,当值发生更改时,它将通知数据绑定(bind)。同样,数据绑定(bind)已设置为与此一起使用,因此您不必更改 XML。使用此选项,您可以创建
Person
类(或者最好是 ViewModel
)扩展 Observable
并在字段更改时自行管理通知数据绑定(bind)。如果您在更新某些字段时必须发生特殊逻辑并且简单的“设置和通知”是不够的,那么您会走这条路。此选项要复杂得多,我将把它作为练习留给读者阅读文档以了解此选项的工作原理。对于绝大多数情况,您应该能够使用选项 #1 完成您需要的操作。在这条线上分手的想法:
person?.nickname = etNickname.text.toString()
如果您正确设置了数据绑定(bind),则不需要这样做。 :)如果您设置
etNickname
使用双向绑定(bind)并制作 person.nickname
可以正确观察到,person.nickname
属性将自动更新为 etNickname
中的文本值当它改变时!这就是数据绑定(bind)的美妙之处。
希望有帮助!
关于android - 使用数据绑定(bind)的问题 : val vs var and the use invalidateAll(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57011839/