[Android Kotlin 기초] 7-3. Use LiveData to control Button states

Use LiveData to control Button states

Task: Add navigation(네비게이션 추가)

step1. Inspect the code

Alt text

Alt text

step2. Add navigation for sleep-quality tracking

private val _navigationToSleepQuality = MutableLiveData<SleepNight>()

val navigationToSleepQuality: LiveData<SleepNight>
    get() = _navigationToSleepQuality
fun doneNavigating(){
    _navigationToSleepQuality.value = null
}
fun onStopTracking() {
    viewModelScope.launch {
        ...
        _navigationToSleepQuality.value = oldNight
    }
}
sleepTrackerViewModel.navigationToSleepQuality.observe(viewLifecycleOwner, Observer { night->
    night?.let {
        this.findNavController().navigate(SleepTrackerFragmentDirections.actionSleepTrackerFragmentToSleepQualityFragment(night.nightId))
        sleepTrackerViewModel.doneNavigating()
    }
})

Task: Record the sleep quality

step1. Create a ViewModel and a ViewModelFactory(뷰모델과 뷰모델 팩토리 생성)

class SleepQualityViewModel(private val sleepNightKey: Long = 0L, val database: SleepDatabaseDao) : ViewModel()
private val _navigateToSleepTracker = MutableLiveData<Boolean?>()
    val navigateToSleepTracker: LiveData<Boolean?>
        get() = _navigateToSleepTracker

fun doneNavigating(){
    _navigateToSleepTracker.value = null
}
fun onSetSleepQuality(quality: Int){
    viewModelScope.launch {
        val tonight = database.get(sleepNightKey) ?: return@launch
        tonight.sleepQuality = quality
        database.update(tonight)

        _navigateToSleepTracker.value = true
    }
}
class SleepQualityViewModelFactory(private val sleepNightKey: Long, private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory{
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        when{
            modelClass.isAssignableFrom(SleepQualityViewModel::class.java)->{
                return SleepQualityViewModel(sleepNightKey, dataSource) as T
            }
        }
        throw IllegalStateException("Unknown ViewModel class")
    }
}

step2. Update the SleepQualityFragment

val arguments = SleepQualityFragmentArgs.fromBundle(requireArguments())
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepQualityViewModelFactory(arguments.sleepNightKey, dataSource)
val sleepQualityViewModel = ViewModelProvider(this, viewModelFactory).get(SleepQualityViewModel::class.java)
binding.sleepQualityViewModel = sleepQualityViewModel
<data>
       <variable
           name="sleepQualityViewModel"
           type="com.example.android.trackmysleepquality.sleepquality.SleepQualityViewModel" />
</data>
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(5)}"

Tast: Control button visibility and add a snackbar

step1. Update button states

android:enabled="@{sleepTrackerViewModel.startButtonVisible}"

android:enabled="@{sleepTrackerViewModel.stopButtonVisible}"

android:enabled="@{sleepTrackerViewModel.clearButtonVisible}"
val startButtonVisible = Transformations.map(tonight) {
   it == null
}
val stopButtonVisible = Transformations.map(tonight) {
   it != null
}
val clearButtonVisible = Transformations.map(nights) {
   it?.isNotEmpty()
}

step2. Use a snackbar to notify the user

private var _showSnackbarEvent = MutableLiveData<Boolean>()

val showSnackBarEvent: LiveData<Boolean>
   get() = _showSnackbarEvent

fun doneShowingSnackbar() {
   _showSnackbarEvent.value = false
}

fun onClear() {
    viewModelScope.launch {
        ...
        _showSnackbarEvent.value = true
    }
}
sleepTrackerViewModel.showSnackbarEvent.observe(viewLifecycleOwner, Observer {
    if (it == true){
        Snackbar.make(requireActivity().findViewById(android.R.id.content), getString(R.string.cleared_message), Snackbar.LENGTH_SHORT).show()
        sleepTrackerViewModel.doneShowingSnackbar()
    }
})

Question 1

One way to enable your app to trigger navigation from one fragment to the next is to use a LiveData value to indicate whether or not to trigger navigation.

(앱이 Framgnet에서 다음 Fragment로의 탐색을 트리거 할 수 있도록하는 한 가지 방법은 LiveData 값을 사용하여 navigation을 트리거할지 여부를 나타내는 것입니다.)

What are the steps for using a LiveData value, called gotoBlueFragment, to trigger navigation from the red fragment to the blue fragment? Select all that apply:

(gotoBlueFragment라는 LiveData 값을 사용하여 redFramgnet에서 blueFragment로의 탐색을 트리거하는 단계는 무엇입니까? 해당되는 모든 것들을 고르세요)

(ViewModel안에서 gotoBlueFragment의 값을가진 LiveData를 정의합니다.)

(RedFragment안에서 gotoBlueFragment값을 observe합니다. 적절한 경우 Observer {} 코드를 구현하여 BlueFragment로 이동 한 다음 gotoBlueFragment 값을 재설정하여 탐색이 완료되었음을 나타냅니다.)

(코드에서 gotoBlueFragment 변수를 앱이 RedFragment에서 BlueFragment로 이동해야 할 때마다 탐색을 트리거하는 값으로 설정하는지 확인합니다.)

(코드에서 사용자가 BlueFragment로 이동하기 위해 클릭하는 View에 대한 onClick 핸들러를 정의해야합니다. 여기서 onClick 핸들러는 goToBlueFragment 값을 관찰합니다.)

정답 : 1,2,3

Question 2

You can change whether a Button is enabled (clickable) or not by using LiveData. How would you ensure that your app changes the UpdateNumber button so that:

(LiveData를 사용하여 Button 활성화 (클릭 가능) 여부를 변경할 수 있습니다. 앱이 다음과 같이 UpdateNumber 버튼을 변경하도록하려면 어떻게해야합니까?)

The button is enabled if myNumber has a value greater than 5.

(myNumber의 값이 5보다 큰 경우 버튼이 활성화됩니다.)

The button is not enabled if myNumber is equal to or less than 5.

(myNumber가 5보다 작거나 같으면 버튼이 활성화되지 않습니다.)

Assume that the layout that contains the UpdateNumber button includes the variable for the NumbersViewModel as shown here:

(UpdateNumber 버튼이 포함 된 레이아웃에 다음과 같이 NumbersViewModel에 대한 변수가 포함되어 있다고 가정합니다.)

Assume that the ID of the button in the layout file is the following:

(레이아웃 파일의 버튼 ID가 다음과 같다고 가정합니다.)

android:id=”@+id/update_number_button”

What else do you need to do? Select all that apply.

(그 밖에 무엇을해야합니까? 해당되는 모든 것들을 고르세요.)

(NumbersViewModel 클래스에서 숫자를 나타내는 LiveData 변수 myNumber를 정의합니다. 또한 myNumber 변수에 대해 Transform.map ()을 호출하여 값이 설정된 변수를 정의하면 숫자가 5보다 큰지 여부를 나타내는 부울을 반환합니다.)

Specifically, in the ViewModel, add the following code:

(특히 ViewModel에서 다음 코드를 추가합니다.)

val myNumber: LiveData<Int>

val enableUpdateNumberButton = Transformations.map(myNumber) {
   myNumber > 5
}

(XML 레이아웃에서 update_number_button 버튼의 android : enabled 속성을 NumberViewModel.enableUpdateNumbersButton으로 설정합니다.)

android:enabled="@{NumbersViewModel.enableUpdateNumberButton}"

(NumbersViewModel 클래스를 사용하는 Fragment에서 버튼의 활성화 된 속성에 관찰자를 추가합니다.)

Specifically, in the Fragment, add the following code:

(특히 Fragment에 다음 코드를 추가합니다.)

// Observer for the enabled attribute
viewModel.enabled.observe(this, Observer<Boolean> { isEnabled ->
   myNumber > 5
})

In the layout file, set the android:enabled attribute of the update_number_button button to “Observable”. (레이아웃 파일에서 update_number_button 버튼의 android : enabled 속성을 “Observable”로 설정합니다.)

정답 : 2