Add a ViewModel
1. In the Android window of your Android Studio under the Gradle Scripts folder, open the file build.gradle(Module:Example.app).
2. To use the ViewModel in your app, verify that you have the ViewModel library dependency inside the dependencies block.
dependencies {
...
// ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
}
Always use the latest version of the library.
3. Create a new Kotlin class file called MyViewModel. In the Android window, right click on the ui.game folder. Select New > Kotlin File/Class.
4. Give it the name MyViewModel, and select Class from the list.
5. Change MyViewModel to be subclassed from ViewModel. ViewModel is an abstract class, so you need to extend it to use it in your app. See the MyViewModel class definition below.
class MyViewModel : ViewModel() {
}
Attach the ViewModel to the Fragment
To associate a ViewModel to a UI controller (activity / fragment), create a reference (object) to the ViewModel inside the UI controller.
In this step, you create an object instance of the GameViewModel inside the corresponding UI controller, which is MyFragment.
1. At the top of the MyFragment class, add a property of type MyViewModel.
2. Initialize the MyViewModel using the by viewModels() Kotlin property delegate. You will learn more about it in the next section.
private val viewModel: MyViewModel by viewModels()
3. If prompted by Android Studio, import androidx.fragment.app.viewModels.
Backing property
A backing property allows you to return something from a getter other than the exact object.
You have already learned that for every property, the Kotlin framework generates getters and setters.
For getter and setter methods, you could override one or both of these methods and provide your own custom behavior. To implement a backing property, you will override the getter method to return a read-only version of your data. Example of backing property:
// Declare private mutable variable that can only be modified
// within the class it is declared.
private var _count = 0
// Declare another public immutable field and override its getter method.
// Return the private property's value in the getter method.
// When count is accessed, the get() function is called and
// the value of _count is returned.
val count: Int
get() = _count
Warning: Never expose mutable data fields from your ViewModel—make sure this data can't be modified from another class. Mutable data inside the ViewModel should always be private.
The lifecycle of a ViewModel
Context as the name suggests means the context or the current state of the application, activity, or fragment. It contains the information regarding the activity, fragment or application. Usually it is used to get access to resources, databases, and other system services.
What is Livedata
LiveData is an observable data holder class that is lifecycle-aware.
Some characteristics of LiveData:
- LiveData holds data; LiveData is a wrapper that can be used with any type of data.
- LiveData is observable, which means that an observer is notified when the data held by the LiveData object changes.
- LiveData is lifecycle-aware. When you attach an observer to the LiveData, the observer is associated with a LifecycleOwner (usually an activity or fragment). The LiveData only updates observers that are in an active lifecycle state such as STARTED or RESUMED. You can read more about LiveData and observation here.
private val _currentWordCount = MutableLiveData<Int>(0)
val currentWordCount: LiveData<Int>
get() = _currentWordCount
private val _currentScrambledWord = MutableLiveData<String>()
val currentScrambledWord: LiveData<String>
get() = _currentScrambledWord
class GameFragment : Fragment() {
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
// Update the UI
viewModel.currentScrambledWord.observe(viewLifecycleOwner,
Observer { newWord ->
binding.textViewUnscrambledWord.text = newWord
})
viewModel.currentWordCount.observe(viewLifecycleOwner,
Observer { newCount ->
binding.wordCount.text = getString(R.string.word_count,newCount, MAX_NO_OF_WORDS)
})
}
Step 1: Change view binding to data binding
1. In the build.gradle(Module) file, enable the dataBinding property under the buildFeatures section.
android {
...
buildFeatures {
dataBinding = true
}
}
2. To use data binding in any Kotlin project, you should apply the kotlin-kapt plugin in the build.gradle(Module) file.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
Above steps auto generates a binding class for every layout XML file in the app. If the layout file name is activity_main.xml then your autogen class will be called ActivityMainBinding.
Step 2: Convert layout file to data binding layout
Data binding layout files are slightly different and start with a root tag of <layout> followed by an optional <data> element and a view root element. This view element is what your root would be in a non-binding layout file.
1. To convert the layout to a Data Binding layout, wrap the root element in a <layout> tag. You'll also have to move the namespace definitions (the attributes that start with xmlns:) to the new root element. Add <data></data> tags inside <layout> tag above the root element. Android Studio offers a handy way to do this automatically: Right-click the root element (ScrollView), select Show Context Actions > Convert to data binding layout
2. Your layout should look something like this:
<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>
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
...
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
3. In GameFragment, at the beginning of the onCreateView() method, change the instantiation of the binding variable to use data binding.
Replace
binding = GameFragmentBinding.inflate(inflater, container, false)
With
binding = DataBindingUtil.inflate(inflater, R.layout.game_fragment, container, false)
Add data binding variables
In this task you will add properties in the layout file to access the app data from the viewModel. You will initialize the layout variables in the code.
1. In game_frament.xml, inside the <data> tag and a child tag called <variable>, declare a property called gameViewModel and of the type GameViewModel. You will use this to bind the data in ViewModel to the layout.
<data>
<variable
name="gameViewModel"
type="com.example.android.unscramble.ui.game.GameViewModel" />
<variable
name="maxNoOfWords"
type="int" />
</data>
Notice the type of gameViewModel contains the package name. Make sure this package name matches with the package name in your app.
2. In GameFragment at the beginning of the onViewCreated()method, initialize the layout variables gameViewModel and maxNoOfWords. The LiveData is lifecycle-aware observable, so you have to pass the lifecycle owner to the layout. In the GameFragment, inside the onViewCreated()method
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.gameViewModel = viewModel
binding.maxNoOfWords = MAX_NO_OF_WORDS
// Specify the fragment view as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = viewLifecycleOwner
...
}
Use binding expressions
Step 1: Add binding expression to the current word
<TextView
android:id="@+id/textView_unscrambled_word"
...
android:text="@{gameViewModel.currentScrambledWord}"
.../>
<TextView
android:id="@+id/word_count"
...
android:text="@{@string/word_count(gameViewModel.currentWordCount, maxNoOfWords)}"
.../>
<TextView
android:id="@+id/score"
...
android:text="@{@string/score(gameViewModel.score)}"
... />
'Android' 카테고리의 다른 글
Navigation Graph (0) | 2021.02.11 |
---|---|
MainActivity.kt (0) | 2021.02.11 |
Notes... (0) | 2021.02.09 |
[Android Studio, Windows 10] Starting Emulator with Consol (0) | 2020.11.20 |
[Android Studio] 프로젝트 내의 원하는 문자열 찾기 Find in Path... (0) | 2020.06.18 |
댓글