使用 Kotlin Multiplatform Mobile (KMM) 的多平台应用程序中的 DataBinding 错误

Posted

技术标签:

【中文标题】使用 Kotlin Multiplatform Mobile (KMM) 的多平台应用程序中的 DataBinding 错误【英文标题】:DataBinding error in multiplatform app using Kotlin Multiplatform Mobile (KMM) 【发布时间】:2021-03-29 05:29:32 【问题描述】:

我是 Kotlin 的新手,我遇到了 DataBinding 的问题 - C不能访问类 ViewModel。检查您的模块类路径是否存在缺失或冲突的依赖项。 但 DataBinding 在块 buildFeatures

中连接

我的 build.gradle.kts(androidApp):

    plugins 
    id("com.android.application")
    kotlin("android")
    id("kotlin-android")
    id("com.squareup.sqldelight")
    id("kotlinx-serialization")
    id("kotlin-kapt")

android 
    compileSdkVersion(30)
    defaultConfig 
        applicationId = "com.rompos.activator.kmm.androidApp"
        minSdkVersion(26)
        targetSdkVersion(30)
        versionCode = 1
        versionName = "1.0"
    
    buildTypes 
        getByName("release") 
            isMinifyEnabled = false
        
    
    buildFeatures 
        viewBinding = true
        dataBinding = true
    
    kotlinOptions 
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    
    packagingOptions 
        pickFirst("META-INF/*.kotlin_module")
    
    kapt 
        generateStubs = true
        correctErrorTypes = true
    


dependencies 
    implementation(project(":shared"))
    implementation("com.google.android.material:material:1.2.1")

    implementation("androidx.core:core-ktx:1.3.2")
    implementation("androidx.appcompat:appcompat:1.2.0")
    implementation("androidx.constraintlayout:constraintlayout:2.0.4")
    implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
    implementation("androidx.recyclerview:recyclerview:1.1.0")
    implementation("androidx.legacy:legacy-support-v4:1.0.0")
    implementation("androidx.activity:activity-ktx:1.1.0")
    implementation("androidx.fragment:fragment-ktx:1.2.5")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")

    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.2.0")
    implementation("androidx.lifecycle:lifecycle-common-java8:2.2.0")
    implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0")

    implementation("com.squareup.sqldelight:android-driver:1.4.4")
    implementation("org.kodein.di:kodein-di:7.1.0")

出现错误的片段:

const val EDIT_MODEL = "editModel"
const val EDIT_MODEL_ID = "editModelId"

open class EditServerFragment : Fragment() 
    private var _viewBinding: FragmentEditServerBinding? = null
    private val viewBinding get() = _viewBinding!!

    private val repository: ServersRepository by myApp.kodein.instance()
    private var serverFormViewModel = ServerFormViewModel()
    private var serverId: Long = 0

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        _viewBinding = FragmentEditServerBinding.inflate(inflater, container, false)
        val view = viewBinding.root

        val binding : FragmentEditServerBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_edit_server, container, false)
        (Error here ->)**binding.item** = serverFormViewModel

        // Dispatcher Back Step to Main
        activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) 
            override fun handleOnBackPressed() 
                toMain()
            
        )

        if (arguments?.getLong("ID") != null) 
            serverId = arguments?.getLong("ID")!!
        

        // Set title
        if (activity is AppCompatActivity) 
            (activity as AppCompatActivity).supportActionBar?.title = getString(R.string.edit_server)
        

        if (serverId > 0) 
            lifecycleScope.launch 
                viewBinding.progressBar.visibility = View.VISIBLE
                repository.get(serverId).let  server ->
                    serverFormViewModel.setForm(server)
                
            .also 
                viewBinding.progressBar.visibility = View.GONE
            
        

        (Error here ->)**binding.item** = serverFormViewModel

        viewBinding.cancelBtn.setOnClickListener 
            toMain()
        

        viewBinding.saveBtn.setOnClickListener 
            if (serverFormViewModel.isFormValid()) 
                lifecycleScope.launch 
                    saveRecord(serverFormViewModel)
                    requireActivity().run 
                        startActivity(Intent(this, MainActivity::class.java))
                        finish()
                    
                .also 
                    toMain()
                
             else 
                Utils.snackMsg(view, getString(R.string.error_empty_field))
                println("EMPTY")
            
        

        return viewBinding.root
    

    override fun onSaveInstanceState(outState: Bundle) 
        super.onSaveInstanceState(outState)
        outState.putLong(EDIT_MODEL_ID, serverId)
        outState.putParcelable(EDIT_MODEL, serverFormViewModel.getModel())
    

    override fun onViewStateRestored(savedInstanceState: Bundle?) 
        super.onViewStateRestored(savedInstanceState)
        if (savedInstanceState != null) 
            serverId = savedInstanceState.getLong(EDIT_MODEL_ID)
            serverFormViewModel.setForm(savedInstanceState.getParcelable(EDIT_MODEL)!!)
        
    

    private fun saveRecord(viewModel: ServerFormViewModel) 
        viewBinding.progressBar.visibility = View.VISIBLE
        try 
            repository.save(serverId, viewModel.getModel(serverId))
            toMain()
         catch (e: Exception) 
            view?.let  Utils.snackMsg(it, e.message.toString()) 
         finally 
            viewBinding.progressBar.visibility = View.GONE
        
    

    private fun toMain() 
        requireActivity().run 
            startActivity(Intent(this, MainActivity::class.java))
            finish()
        
    

布局:

<?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="item"
            type="com.rompos.activator.kmm.shared.model.ServerFormViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/editView"
        android:layout_
        android:layout_
        android:orientation="vertical">

        <androidx.appcompat.widget.LinearLayoutCompat
            android:layout_
            android:layout_
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <com.google.android.material.textfield.TextInputLayout
                android:layout_
                android:layout_
                android:hint="@string/server_title"
                app:endIconMode="clear_text"
                tools:layout_editor_absoluteY="8dp">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/serverTitle"
                    android:layout_
                    android:layout_
                    android:singleLine="true"
                    android:text="@=item.title" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_
                android:layout_
                android:hint="@string/server_url"
                app:endIconMode="clear_text">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/serverUrl"
                    android:layout_
                    android:layout_
                    android:inputType="textUri"
                    android:singleLine="true"
                    android:text="@=item.url" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_
                android:layout_
                android:hint="@string/server_token"
                app:endIconMode="clear_text">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/serverToken"
                    android:layout_
                    android:layout_
                    android:singleLine="true"
                    android:text="@=item.token" />
            </com.google.android.material.textfield.TextInputLayout>

        </androidx.appcompat.widget.LinearLayoutCompat>

        <androidx.appcompat.widget.LinearLayoutCompat
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:gravity="center|bottom"
            android:orientation="horizontal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toStartOf="parent">

            <Button
                android:id="@+id/cancelBtn"
                style="@style/Widget.AppCompat.Button.Borderless.Colored"
                android:layout_
                android:layout_
                android:text="@string/cancel_btn"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/saveBtn"
                app:layout_constraintStart_toStartOf="parent" />

            <Button
                android:id="@+id/saveBtn"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_
                android:layout_
                android:text="@string/save_btn"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent" />


        </androidx.appcompat.widget.LinearLayoutCompat>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/progressBar"
            android:layout_
            android:layout_
            android:background="#68898989"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <ProgressBar
                style="?android:attr/progressBarStyle"
                android:layout_
                android:layout_
                android:backgroundTintMode="add"
                android:progressBackgroundTintMode="multiply"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

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

视图模型:

class ServerFormViewModel 
    private var serverModel = ServerViewModel()

    fun setForm(server: Server) 
        serverModel.setModel(server)
    

    fun setForm(viewModel: ServerViewModel) 
        serverModel = viewModel
    

    fun getTitle() : String 
        return serverModel.title ?: ""
    

    fun setTitle(value: String) 
        serverModel.title = value
    

    fun getUrl() : String 
        return serverModel.url ?: ""
    

    fun setUrl(value: String) 
        serverModel.url = value
    

    fun getToken() : String 
        return serverModel.token ?: ""
    

    fun setToken(value: String) 
        serverModel.token = value
    

    fun isFormValid() : Boolean 
        return !serverModel.title.isNullOrEmpty() and !serverModel.url.isNullOrEmpty() and !serverModel.token.isNullOrEmpty()
    

    fun getModel(id: Long?) : Server 
        return Server(id!!, serverModel.title, serverModel.url, serverModel.token)
    

    fun getModel() : ServerViewModel 
        return serverModel
    

Git 上的这个项目: https://github.com/Diy2210/com.rompos.activator.kmm

【问题讨论】:

【参考方案1】:

我认为您需要在 onViewCreated 方法中绑定您的 ViewModel。

【讨论】:

以上是关于使用 Kotlin Multiplatform Mobile (KMM) 的多平台应用程序中的 DataBinding 错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Kotlin/Multiplatform 项目中使用 .klib 库

如何使用 Kotlin-Multiplatform 在 iOS 应用程序的后台线程中运行任务?

如何在 Kotlin Multiplatform(纯 kotlin)中进行延迟

在 Kotlin Multiplatform 中使用 Swift 协议默认实现

Kotlin Multiplatform Cocoapods 集成问题

如何使用 Cocoapods 调整 Kotlin Multiplatform 项目的 Swift 类名?