【问题标题】:Data binding LiveData from Transformation - Android Kotlin来自 Transformation 的数据绑定 LiveData - Android Kotlin
【发布时间】:2018-09-08 03:54:08
【问题描述】:

我正在学习 kotlin 和 android 架构组件。 我有一个谷歌地图上的地图切换按钮的简单用例。

我想使用数据绑定将地图切换按钮标签绑定到我的 ViewModel 中的 MutableLiveData 字段。

我通过 Activity 中的 onCreate 方法在 MapViewModel 中设置 mapType val。如果我理解正确,这应该会触发 mapLabel val 由于使用 Transformations.map 而发生变化。

它不工作...为什么?

这是我的版本:

  • Android Studio 3.2 Canary 4
  • kotlin_version = '1.2.21'
  • 支持=“27.1.0”
  • arch_core = "1.1.0"
  • 数据绑定 = "3.2.0-alpha04"

MapViewModel.kt

class MapViewModel : ViewModel() {

    val mapType: MutableLiveData<MapType> = MutableLiveData()

    val mapLabel: LiveData<String> = Transformations.map(mapType, {
        if (it == MapType.MAP) "SAT" else "MAP"
    })
}

enum class MapType {
    SAT, MAP
}

activity_maps.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"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="vm"
            type="uk.co.oliverdelange.wcr_android_kt.ui.map.MapViewModel" />
    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MapsActivity">

            <Button
                android:id="@+id/map_toggle"
                style="@style/Wcr_MapToggle"
                android:layout_marginTop="110dp"
                android:layout_marginEnd="12dp"
                android:layout_marginBottom="7dp"
                android:layout_gravity="top|end" 
                android:text="@{vm.mapLabel}" />
        </fragment>    
    </FrameLayout>
</layout>

MapsActivity.kt

class MapsActivity : AppCompatActivity(), OnMapReadyCallback {

    private lateinit var mMap: GoogleMap
    private lateinit var viewModel: MapViewModel
    private lateinit var binding: ActivityMapsBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_maps)
        viewModel = ViewModelProviders.of(this).get(MapViewModel::class.java)
        binding.vm = viewModel

        val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)

        // I can do it this way, but I don't want to. 
        // viewModel.mapLabel.observe(this, Observer { map_toggle.text = it })

        // Here is where i'm setting the MapType on the ViewModel.
        viewModel.mapType.value = MapType.MAP
    }

    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap
    }
}

我已经使用 MutableLiveData 对象测试了绑定,我在该对象中设置了活动中的字符串,它工作正常。问题似乎出在 Transformations.map 上——我是不是理解错了?

另外,在调试时,我看到我的 ViewModel 中的 mapType val 没有观察者(不确定这是对还是错,只是很有趣)

【问题讨论】:

    标签: android kotlin android-architecture-components android-livedata android-viewmodel


    【解决方案1】:

    它不工作...为什么?

    当您更改mapType 的值时,mapLabel 的值不会立即更改。您需要再次设置mapLabel 的值。例如,设置mapType 的值后,调用方法更新mapLabel 的值

    // Here is where i'm setting the MapType on the ViewModel.
    viewModel.mapType.value = MapType.MAP
    viewModel.updateMapLabel()
    

    您的视图模型:

    fun updateMapLabel() {
        mapLabel.value =
            if (it == MapType.MAP) "SAT" else "MAP"
    }
    

    【讨论】:

    • 这似乎没有回答我的问题。你只是在解决这个问题。正如我在问题中描述的那样,我可以通过多种其他方式做到这一点。但是我想了解 Transformations.map 是如何工作的,以及为什么它在这种情况下不起作用。
    • 你为什么不去地图的源代码阅读呢?容易理解更清楚。刚刚看了Transformations.map的源码,还是用setValue的方法来改变object的值。
    • 我已经阅读了源代码和文档,这就是我在这里提出这个问题的原因,因为我相信我所做的应该可以工作。
    【解决方案2】:

    这里的问题是,尽管绑定到 mapLabel 字段,但当 mapLabel 字段的值更改时,视图绑定并未更新。

    原因是我没有在绑定上设置生命周期所有者。

    binding.setLifecycleOwner(this)

    在第 10 次阅读 this blog post 后,我意识到了自己的错误。

    我的新 MapsActivity.kt

    class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
    
        private lateinit var mMap: GoogleMap
        private lateinit var viewModel: MapViewModel
        private lateinit var binding: ActivityMapsBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            binding = DataBindingUtil.setContentView(this, R.layout.activity_maps)
            binding.setLifecycleOwner(this) //<- NEW!
            viewModel = ViewModelProviders.of(this).get(MapViewModel::class.java)
            binding.vm = viewModel
    
            val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
            mapFragment.getMapAsync(this)
    
            // Here is where i'm setting the MapType on the ViewModel.
            viewModel.mapType.value = MapType.MAP
        }
    
        override fun onMapReady(googleMap: GoogleMap) {
            mMap = googleMap
        }
    }
    

    从好的方面来说,我学到了很多关于 LiveData 内部工作原理的知识!

    【讨论】:

    • 您可以将此答案标记为已接受的答案。你说得对,我一般不会这样使用这个绑定(我一般是用 ObservableField 来观察数据变化的)。谢谢。
    猜你喜欢
    • 2019-12-14
    • 1970-01-01
    • 2020-07-22
    • 2016-06-19
    • 2015-09-05
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多