这实际上是2个问题。
Person
数据类中将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()
?该文档说,它“使所有绑定表达式无效,并请求重新绑定以刷新UI”。数据绑定的目的不是以使数据更新立即更新视图的方式连接数据和视图吗?MainActivity:
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_main.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>
答案 0 :(得分:9)
问题1:
我注意到,如果在Person数据类中将name参数设置为val而不是var,则数据绑定将不起作用。
为什么会发生?
因为您使用的是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}"
告诉数据绑定“我想将TextView
的文本设置为该人的name
值,而我想name
文本更改时更新人员的TextView
”。
使用@=
时,数据绑定将为您要设置的属性寻找一个设置器。在这种情况下,它将为name
类上的Person
属性寻找一个设置器。在Kotlin中,这意味着拥有一个名为name
的属性var
。
如果您不打算在name
发生变化时更新此人的TextView
属性(我认为您没有这样做,通常应该使用EditText
进行此操作),然后将该行更改为@
(android:text="@{person.name}"
)。然后,您可以将name
设为val
,因为您只是从其中读取来进行数据绑定。
问题2:
为什么需要在doneClick()中调用invalidateAll()?
您实际上不...
文档说它“使所有绑定表达式无效,并请求重新绑定以刷新UI”。数据绑定的目的不是以一种使数据更新立即更新视图的方式连接数据和视图吗?
是,但是:数据绑定不是魔术。如果用户界面要更新,必须告知用户这样做,并且更改数据并不能神奇地告诉数据绑定它必须更新。 某事必须告诉数据绑定a)是时候更新了,b)需要更新什么。
因此,invalidateAll()
现在拥有的就是the弹枪方法。您更新了一个nickname
字段,然后对数据绑定大喊“嘿,更新所有内容!”,因此它根据Person
的当前状态重新绑定 all 视图,当然,包括“昵称”,以便更新视图。
您想要要做的是仅更新 绑定到nickname
的字段,因为这是更改的一件事,最好是,您想要nickname
更改时自动执行此操作。为此,您需要观察 nickname
字段的状态并对其进行反应更改。
您可以通过以下几种方式执行此操作:
在这种方法中,您要绑定的模型的字段是LiveData
个对象(val nickname = MutableLiveData<String>()
),并向绑定中添加了LifeCycleOwner
,以便可以观察到{{ 1}}个对象。
数据绑定设置为使用LiveData
,因此您的xml不需要更改。但是现在这些属性是 observable ,当您在LiveData
(Person
)上更新名称时,数据绑定将自动得到通知,并更新关联视图的状态。
您将不必致电person?.nickname?.value = "New Nickname"
。
从概念上讲,它与#1相同,只是在引入invalidateAll()
之前。如今,您可以考虑弃用此方法,并使用LiveData
方法,但是为了完整起见,我会提及它。
同样,您没有将该属性包含在类型为LiveData
的常规属性中,而是将该属性包装在 observable 数据结构(String
)中,该结构将在值更改时通知数据绑定。再次,设置了数据绑定以与此配合使用,因此您不必更改XML。
使用此选项,您可以使val nickname = ObservableString()
类(最好是Person
)扩展ViewModel
并随着字段的变化自行通知数据绑定。如果您在更新某些字段时必须要执行特殊的逻辑,而仅仅进行“设置并通知”是不够的,那么您将采用这种方法。这个选项要复杂得多,我将作为练习让读者阅读文档以了解此选项的工作原理。在绝大多数情况下,您应该可以使用选项#1进行所需的操作。
此行的分手想法:
Observable
如果您正确设置了数据绑定,则这不是必需的。 :)
如果将person?.nickname = etNickname.text.toString()
设置为使用双向绑定,并使etNickname
正确可见,则 person.nickname
属性将自动更新为person.nickname
中的文本值变化!
那是数据绑定的美。
希望有帮助!
答案 1 :(得分:0)