使用数据绑定的问题:val vs var以及使用invalidateAll()的问题

问题描述 投票:2回答:2

这实际上是2个问题。

  1. [我注意到,如果在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

为什么会发生?

  1. 为什么我需要在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>
android kotlin android-databinding
2个回答
0
投票
  1. Val =不可变变量=可变

完整答案Val and Var in Kotlin

  1. 这是因为属性没有内置的机制来通知UI他们已更改。因此,您必须手动调用它。解决此问题的方法是使用LiveData或MutableLiveData。

0
投票

问题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并且我想在[C0 ]文字更改“。

[当您使用name时,数据绑定将为您要设置的属性寻找一个设置器。在这种情况下,它正在寻找TextView类上@=属性的设置器。在Kotlin中,这意味着具有名为name的属性,即Person

[如果您不打算在name发生更改时更新此人的var属性(我认为您没有这样做,通常应该使用name进行此操作),然后将该行更改为[ C0](TextView)。然后,您可以将EditText设为@,因为您只是从[进行数据绑定。


问题2:

为什么我需要在doneClick()中调用invalidateAll()?

您实际上不...

该文档说,它“使所有绑定表达式无效,并请求重新绑定以刷新UI”。数据绑定的目的不是以使数据更新立即更新视图的方式连接数据和视图吗?

是,

但是

:数据绑定不是魔术。如果UI是要更新必须通知这样做,并且更改数据并不能神奇地告诉数据绑定它必须更新。 Something必须告诉数据绑定a)是时候更新了,而b)需要更新什么。所以您现在使用android:text="@{person.name}"拥有的是the弹枪方法。您更新了一个name字段,然后对数据绑定大喊“嘿,更新所有内容!”,因此它根据val的当前状态重新绑定

all

视图,其中当然包括“昵称”,因此该视图得到更新。

想要

要做的是更新only绑定到notifyAll()的字段,因为这是一件事,最好是当nickname改变时您要自动执行。为此,您需要观察 Person字段的状态,并且反应对其进行更改。您可以通过几种方式执行此操作:

    nickname
  • 在这种方法中,您要绑定的模型的字段为nickname对象(nickname,然后将Use LiveData添加到绑定中,以便可以观察LiveData对象。

    数据绑定设置为使用val nickname = MutableLiveData<String>(),因此无需更改您的xml。但是现在属性是

    observable

  • ,并且当您在LifeCycleOwnerLiveData)上更新名称时,数据绑定将自动得到通知,并将更新关联视图的状态。您不必致电LiveData

      Person
  • 从概念上讲,它与#1相同,但是在引入person?.nickname?.value = "New Nickname"之前出现。如今,您可以考虑弃用此方法,并使用invalidateAll()方法,但为了完整起见,我将其提及。

    同样,您没有将该属性包装为Use Observable Fields类型的常规属性,而是将该属性包装在

    observable

  • 数据结构(LiveData)中,该数据结构将在值更改时通知数据绑定。再次,设置了数据绑定以使其与此兼容,因此您不必更改XML。
      使用LiveData
  • 使用此选项,您可以使String类(最好是val nickname = ObservableString())扩展为Observable Objects,并在字段更改时自行管理通知数据绑定。如果您在更新某些字段时必须执行特殊的逻辑,而仅通过简单的“设置并通知”是不够的,则可以采用这种方法。该选项要复杂得多,我将其作为练习让读者阅读文档以了解此选项的工作原理。在大多数情况下,您应该可以使用选项#1进行所需的操作。


    在这条线上分开的想法:

    Person

    如果您正确设置了数据绑定,则

    这没有必要

  • 。 :)如果将ViewModel设置为使用双向绑定并使Observable正确可见,person?.nickname = etNickname.text.toString() 属性将在更改时自动更新为etNickname中的文本值这就是数据绑定的美。

    希望有帮助!

    © www.soinside.com 2019 - 2024. All rights reserved.