LiveData
는 수명 주기를 인식하는 observable 데이터 홀더 클래스이다.
LiveData는 데이터를 가지고 있다. 모든 유형의 데이터에 사용할 수 있는 wrapper.
LiveData is observable. 즉, LiveData 객체에서 보유한 데이터가 변경되면 observer에게 알림이 간다.
LiveData는 수명 주기를 인식합니다. LiveData에 observer를 연결하면 observer는 LifecycleOwner(일반적으로 activity 또는 fragment)와 연결된다. LiveData는 STARTED 또는 RESUMED 같은 활성 수명 주기 상태인 observer만 업데이트한다.
Unsramble 앱에서 화면에 새로운 단어를 표시할 때 updateNextWordOnScreen()
메서드가 호출되고, 이것은 onViewCreated()
, restartGame()
, onSkipWord()
, onSubmitWord()
메서드에서 호출된다.
Livedata
를 사용하면 UI를 업데이트하기 위해 여러 위치에서 이 메서드를 호출하지 않아도 된다.
GameViewModel
의 현재 단어를 LiveData
로 변환하여 데이터를 LiveData로 래핑하는 방법을 알아본다. 이후 이 LiveData 객체에 관찰자를 추가하고 LiveData를 관찰하는 방법을 알아볼 것이다.
String 타입이었던 _currentScrambledWord를 MutableLiveData<String>
타입으로 변경한다.
private val _currentScrambledWord = MutableLiveData<String>()
_currentScrambledWord
를 사용하는 곳이 있다면 _currentScrambledWord.value
로 수정한다.
마찬가지로 _score
와 _currentWordCount
도 MutableLiveData<>
로 변경하고 사용되는 곳에 .value를 사용한다. (이제 정수 타입이 아니라 LiveData 타입이니까!)
null에 안전하게 덧셈처리 하려면 plus()
를 사용한다.
_score+=SCORE_INCREASE
→ _score.value = (_score.value)?.plus(SCORE_INCREASE)
증감연산자++도 null 안전하게 _currentWordCount.value = (_currentWordCount.value)?.inc()
GameFragment
의 onViewCreated()
에서 observe()
를 호출한다.
viewModel.currentScrambledWord.observe(
viewLifecycleOwner,
{newWord -> binding.textViewUnscrambledWord.text = newWord})
viewLifecycleOwner
는 frament의 뷰 수명 주기를 나타낸다.
newWord
에는 scamble된 새 단어가 포함된다.
✔ 람다 표현식은 항상 중괄호로 묶는다.
score
와 wordCount
도 마찬가지로 기존 binding 코드를 observe()로 변경한다.
데이터 결합: 뷰 결합 binding과 개념은 비슷하지만 반대로 뷰(레이아웃 파일)에서 데이터를 참조하는 방식
이전에 배웠던 뷰 결합은 binding.textViewUnscrambledWord.text = viewModel.currentScrambledWord
레이아웃 파일에서 데이터 결합하면? android:text="@{gameViewModel.currentScrambledWord}"
build.gradle(Mudule)
에서 builFeature
내부
viewBinding = true
→ dataBinding = true
로 변경
buildFeatures {
dataBinding = true
}
game_fragment.xml
은 ScrollView로 감싸져있다. ScrollView를 클릭 후 전구 모양을 누르면 Convert to data binding layout
이 뜬다.
누르면 <layout> 태그가 ScrollView를 감싸고, 내부에 <data>도 추가된다.
game_fragment.kt
의 onCreateView()
에서 binding 변수를
binding = GameFragmentBinding.inflate(inflater, container, false)
에서
binding = DataBindingUtil.inflate(inflater, R.layout.game_fragment, container, false)
로 변경
<data>
태그 내부에 데이터 바인딩할 속성을 선언한다.
<variable
name="gameViewModel"
type="com.example.android.unscramble.ui.game.GameViewModel" />
<variable
name="maxNoOfWords"
type="int" />
onViewCreated()
에서 변수 초기화
binding.gameViewModel = viewModel
binding.maxNoOfWords = MAX_NO_OF_WORDS
레이아웃에 수명 주기 소유자를 전달한다.
binding.lifecycleOwner = viewLifecycleOwner
종속 변수 중 하나라도 변경되면 'DB 라이브러리'가 결합 표현식을 실행하고 이에 따라 뷰가 업데이트된다. 이러한 변경 감지는 데이터 결합 라이브러리를 사용할 때 무료로 제공되는 훌륭한 최적화 기능
예를 들어 아래 코드를 보면 결합 표현식은 @
기호로 시작하고 중괄호 {}
로 둘러싸인다. TextView 텍스트는 user 변수의 firstName 속성으로 설정된다.
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}" />
game_fragment.xml
에서 textView_unscrambled_word
텍스트 뷰에 아래 코드를 추가한다.
android:text="@{gameViewModel.currentScrambledWord}"
이제 LiveData
변경사항 업데이트가 레이아웃에 직접 수신되므로 fragment에 관찰자가 필요없다. 즉 GameFragment
에서 viewModel.currentScrambledWord.observe()
에 대한 코드를 삭제한다.
점수와 단어 수도 결합 표현식으로 바꾼다.
이 코드 삭제
viewModel.score.observe(viewLifecycleOwner,
{ newScore ->
binding.score.text = getString(R.string.score, newScore)
})
viewModel.currentWordCount.observe(viewLifecycleOwner,
{ newWordCount ->
binding.wordCount.text =
getString(R.string.word_count, newWordCount, MAX_NO_OF_WORDS)
})