Я пытаюсь общаться с 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'
}
Я ввел модель представления в модуль с привязкой. вы можете проверить «MainActivityModule.kt», который я прикрепил выше для справки.
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);
Пробовал это, все еще возникает та же проблема. Мне интересно, есть ли у моей реализации проблемы с кинжалом или нет.
попробуйте после добавления строки binding.executePendingBindings() также
Теперь это должно работать, я думаю, вы тоже пытались, но просто попробуйте после чистого проекта.
Без DI все в порядке, модель представления и ее поведение работают хорошо. Я добавил свой ответ, вы можете посмотреть.
Как я и подозревал, проблема связана с инъекцией 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
Теперь работает отлично..
где вы определяете модель представления в MainActivity?