I am new to Kotlin and Android development. I am aware that there are similar if not even same questions here. Yes, i spent the whole day trying to figure it out.
I am also sorry not providing a proper toy example.
I have a simple goal. I want to have two EditText "linked" together. When I change one, i expect the other to be changed as well. I want to apply the Caesar's cipher (the cipher is implemented and works). But we will ignore the second EditText for now and focus only on the binding of the first.
I have read that the modern way to achieve this is with DataBinding. It sounds reasonable, since I don't have to write that much code.
However, since it is a "new" feature which also changed, many tutorials fail to provide me with the full code example, which i could simply reuse. I tried to implement it myself from the bits and pieces on the web.
THE BINDING DOES NOT WORK!
I have some layout of some fragment (I am leaving out unimportant things)
<layout>
    <data>
        <variable name="caesarViewModel" type="cz.fortescue.ui.caesar.CaesarViewModel"/>
    </data>
    <EditText
         android:id="@+id/caesar_input"
         android:inputType="text"
         android:text="@={caesarViewModel.input}"/>
</layout>
Where the "@={caesarViewModel.input}" is the important part. (don't miss the =).
Now the ViewModel.
class CaesarViewModel : ViewModel() {
    val input = MutableLiveData<String>().apply {
        value = "aaaaaa"
    }
}
I am using MutableLiveData, so the DataBiding will be able to change it.
I am also using apply to test the observer, which I will show you later on.
Last but not least the Fragment
class CaesarFragment : Fragment() {
    private lateinit var binding: FragmentCaesarBinding
    private val caesarViewModel by lazy { ViewModelProvider(this).get(CaesarViewModel::class.java) }
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentCaesarBinding.inflate(
            inflater,
            container,
            false
        )
        binding.caesarViewModel = caesarViewModel
        binding.lifecycleOwner = viewLifecycleOwner
        val root = inflater.inflate(R.layout.fragment_caesar, container, false)
        caesarViewModel.input.observe(viewLifecycleOwner, Observer { text ->
            caesarViewModel.output.value = encrypt(text) { caesarEncrypt(it, 1)}
            Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
        })
        return root
    }
}
Since I am new to Kotlin, I am half guessing what happens.
- caesarViewModel
The caesarViewModel is created as needed. Notice I am using the ViewModelProvider not ViewModelProviders.
- binding
There were 5 different ways i found online, how to properly get the binding. I hope this is now the correct one. I suspect here some errors, but I have not found any...
- Setting the members of binding
I am setting the lifecycleOwner and caesarViewModel. The latter should be a crucial part of the whole DataBinding thing.
- Observer
I create an Observer, which should notify me with a Toast, if anything changes.
It does it once with aaaaaa. Since this is the value, I applied to the MutableLiveData I suspect that the observer works just fine.
It seems to be also necessary to change the build.gradle
Plugins:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android:
dataBinding {
    enabled = true
}
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
    jvmTarget = JavaVersion.VERSION_1_8.toString()
}
Dependencies:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.navigation:navigation-fragment:2.2.1'
    implementation 'androidx.navigation:navigation-ui:2.2.1'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
What am I missing???
There has to be something I have overlooked, or don't know about.
In some tutorials, they use the @Bindable decorator, however this should not be necessary with MutableLiveData.
 
    