【问题标题】:Android: Recyclerview, Data binding and View ModelAndroid:Recyclerview、数据绑定和视图模型
【发布时间】:2021-11-07 12:53:58
【问题描述】:

我的目标是在 recyclerview 中显示 HTTP 请求的结果。因为我有一段时间没有使用 Android,所以我想使用最喜欢的方法。

我有一个设备数据类:

data class Device(val name: String, val ipAddress: String, val type: String = "")

一个新设备适配器:

class NewDevicesAdapter(private val items: List<Device>) :
    RecyclerView.Adapter<NewDevicesAdapter.ViewHolder>() {
    lateinit var binding: AdapterNewDevicesBinding


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        ViewHolder(
            AdapterNewDevicesBinding.inflate(LayoutInflater.from(parent.context))
        )

    override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(items[position])

    override fun getItemCount(): Int = items.size

    inner class ViewHolder(val binding: AdapterNewDevicesBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: Device) {
            binding.device = item
        }
    }
}

使用 xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="device"
            type="[...].Device" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <LinearLayout
            [...]

            <TextView
                android:id="@+id/adapter_newDevices_textView_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{device.name}" />

            <TextView
                android:id="@+id/adapter_newDevices_textView_ip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{device.ipAddress}" />
        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

还有一个 NewDeviceFragment 类:

class NewDeviceFragment : Fragment() {
    private lateinit var binding: FragmentNewDeviceBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentNewDeviceBinding.inflate(layoutInflater).apply {
            devicesViewModel = ViewModelProvider(this@NewDeviceFragment).get(DevicesViewModel::class.java)
            lifecycleOwner = viewLifecycleOwner
        }

        [...]

        binding.devicesViewModel!!.items.observe(viewLifecycleOwner, Observer { response ->
            binding.newDeviceRecyclerViewDevices.adapter = NewDevicesAdapter(response as List<Device>)
        })

        return binding.root
    }

使用 xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="devicesViewModel"
            type="[...].DevicesViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        [...]

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/newDevice_recyclerView_devices"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="16dp"
            android:layout_marginBottom="16dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/adapter_new_devices"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/newDevice_toolbar" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我有一个 ViewModel 类,它应该通过 HTTP 请求收集数据,然后将其存储在 LiveData 对象中,以通过片段中的观察者显示在 recyclerview 中:

class DevicesViewModel: ViewModel() {
    val items: MutableLiveData<List<Device>> by lazy {
        MutableLiveData<List<Device>>()
    }

    init {
        scanForDevices()
    }


    private fun scanForDevices() = {
        val newDevices: MutableList<Device> = arrayListOf()
        val queue = Volley.newRequestQueue()

        for (i in 100..255) {
            val ipAddress = "192.168.2.$i"
            val url = "$ipAddress/cm?cmnd=STATUS"
            [...]
        }
    }

但是对于 Volley RequestQueue,我需要 ViewModel 中不存在的上下文。现在我听说过 AndroidViewModel,但我不确定这是否是正确的方法。也许有人可以给我一个建议,我可以/应该如何通过 HTTP 请求和 Recyclerview 中的结果最好地实现这个概念。

【问题讨论】:

  • 使用 AndroidViewModel 绝对没问题,或者您可以使用 Hillt 这是一个 DI 框架并在 ViewModel 中注入上下文,这也是一个可行的解决方案

标签: android kotlin data-binding android-volley viewmodel


【解决方案1】:

如果您需要在 Viewmodel 中使用上下文,您可以使用 AndroidViewModel (AVM),因为它包含应用程序上下文。要检索上下文调用 getApplication(),否则使用常规 ViewModel (VM)。

如果您在 scanForDevices() 中使用常规 VM、PASS 应用程序或上下文作为参数。

出于单元测试的目的,Android VM 不是最好的选择,因为它最好避免任何涉及 android 生命周期相关对象的事情,但如果你仍然想使用 Android VM,请通过 Dagger 2 或 Hilt 使用依赖注入。

【讨论】:

    【解决方案2】:

    您可以使用 AndroidViewModel 提供的 Application context,您应该扩展 AndroidViewModel,它只是一个包含 Application 引用的 ViewModel。

    DevicesViewModel(application: Application) : AndroidViewModel(application) {
    
    private val context = getApplication<Application>().applicationContext
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-25
      • 2014-03-06
      相关资源
      最近更新 更多