Не удается взаимодействовать с XML из ViewModel с помощью привязки данных Android

Я пытаюсь общаться с viewmodel из xml и наоборот, используя шаблон MVVM. Я работал над привязкой данных раньше и успешно работал с Живые данные — Кинжал — MVVM . Недавно я попытался создать новый проект и с тех пор не могу отследить ответ с помощью XML и модели представления. Ни onClick из XML -> модель просмотра, ни присвоение значения текстовому просмотру из Вид -> XML не работают. Но нет никакого сбоя или чего-то еще, просто он не работает. Я добавил все связанные файлы [MainActivity, activity_main, viewModel, модуль Dagger, build.gradle]

Я буду очень признателен, если кто-нибудь скажет мне, что здесь происходит не так.

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

<data>
    <variable
        name = "viewmodel"
        type = "aveek.com.vm.ui.home.MainActivityViewModel"/>
</data>
<android.support.constraint.ConstraintLayout
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    tools:context = ".MainActivity">

 <TextView

        android:layout_width = "wrap_content"
        android:layout_height = "wrap_content"
        android:text = "@{viewmodel.balanceText}"
        app:layout_constraintBottom_toBottomOf = "parent"
        app:layout_constraintLeft_toLeftOf = "parent"
        app:layout_constraintRight_toRightOf = "parent"
        android:clickable = "true"
        android:onClick = "@{() -> viewmodel.clickData()}"
        app:layout_constraintTop_toTopOf = "parent" />
...
</layout>

MainActivity.kt

class MainActivity : AppCompatActivity(),LifecycleOwner {

@Inject
lateinit var binding : ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    AndroidInjection.inject(this)
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    binding.setLifecycleOwner(this) 
    with(binding){
        this.viewmodel?.let {
            it.balanceText.set( "Aveek testing")
            it.data.observe(this@MainActivity, Observer {
                Toast.makeText(this@MainActivity, "Data is now : $it", Toast.LENGTH_SHORT).show()
            })
           }
         }
       }

MainActivityViewModel.kt

class MainActivityViewModel : ViewModel() {
val data = MutableLiveData<Boolean>()
val balanceText = ObservableField<String>()
 fun clickData(){
    data.value = false
 }
}

MainActivityModule.kt

@Module
class  MainActivityModule{

/**
 * provides binding to  Main Activity from respective XML
 * @property viewModel
 * @property context
 * @return binding of the view
 */
@Provides
fun binding(context: MainActivity, viewModel : MainActivityViewModel) : 
ActivityMainBinding {
    val binding = DataBindingUtil.setContentView<ActivityMainBinding>(context, 
    R.layout.activity_main)
    binding.viewmodel = viewModel
    return binding
 }
}

build.gradle

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {
   compileSdkVersion 27
   defaultConfig {
    applicationId "aveek.test"
    minSdkVersion 15
    targetSdkVersion 27
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    vectorDrawables.useSupportLibrary = true
   }
   dataBinding {
    enabled = true
   }
   buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 
     'proguard-rules.pro'
    }
    }
    compileOptions {
     sourceCompatibility JavaVersion.VERSION_1_8
     targetCompatibility JavaVersion.VERSION_1_8
    }
   }

kapt {
  generateStubs = true
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:multidex:1.0.2'
implementation 'com.android.support:design:27.1.1'
implementation "android.arch.lifecycle:extensions:1.1.0"
//    annotationProcessor "android.arch.lifecycle:compiler:1.1.0"

kapt "com.android.databinding:compiler:$androidPluginVersion"

annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-android-processor:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"


implementation "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger-android:$daggerVersion"
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"

testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso- 
core:3.0.2'

}


ext{
    kotlin_version = '1.2.31'
    androidPluginVersion = '3.1.0'
    daggerVersion = '2.13'
}

где вы определяете модель представления в MainActivity?

Shweta Chauhan 28.03.2019 10:18

Я ввел модель представления в модуль с привязкой. вы можете проверить «MainActivityModule.kt», который я прикрепил выше для справки.

Aveek 28.03.2019 10:27
1
2
1 851
2

Ответы 2

you won't be able to use any generated methods to set your variable to the binding, as there's no common superclass besides the ViewDataBinding,so you will be forced to use reflection, or you can use the convenience method setVariable():

binding.setVariable(BR.viewModel, viewModel);

Пробовал это, все еще возникает та же проблема. Мне интересно, есть ли у моей реализации проблемы с кинжалом или нет.

Aveek 28.03.2019 10:41

попробуйте после добавления строки binding.executePendingBindings() также

Shweta Chauhan 28.03.2019 10:43

Теперь это должно работать, я думаю, вы тоже пытались, но просто попробуйте после чистого проекта.

Shweta Chauhan 28.03.2019 10:45

Без DI все в порядке, модель представления и ее поведение работают хорошо. Я добавил свой ответ, вы можете посмотреть.

Aveek 29.03.2019 03:50

Как я и подозревал, проблема связана с инъекцией Dagger. Возможно, я реализовал неправильный подход, но модель просмотра прекрасно работает без внедрения зависимостей.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    viewModel = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.viewmodel = viewModel

    binding.setLifecycleOwner(this)
    mLifecycleRegistry = LifecycleRegistry(this).apply {
        markState(Lifecycle.State.CREATED)
    }
    with(binding){
        this.viewmodel?.let {
            it.balanceText.set( "Aveek testing")
            it.data.observe(this@MainActivity, Observer {
                Toast.makeText(this@MainActivity, "Data is now : $it", 
          Toast.LENGTH_SHORT).show()
            })
        }
    }
}

Итак, я просмотрел код DI и обнаружил, что в MainActivity.onCreate() я делал это

AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

который в основном переопределяет поведение привязки данных. Итак, я обновил код следующим образом

AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
// and initiated binding here as injecting binding from module before setting content won't be effective
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

И удалено

//    @Inject
//    lateinit var binding : ActivityMainBinding

Теперь работает отлично..

Другие вопросы по теме