Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

yourginieus

ViewModel 및 LiveData를 통한 데이터 바인딩 본문

Android/Android Kotlin 기초 실습 정리

ViewModel 및 LiveData를 통한 데이터 바인딩

EOJIN 2022. 10. 27. 13:47

1. Add ViewModel data binding

  • 이전까지는 앱의 view에 접근하기 위한 type-safe 방법으롤 data binding을 사용함
  • 하지만 data binding의 진정한 가치는, 이름에서 알 수 있듯이, 앱의 view 개체에 데이터를 직접 바인딩할 수 있다는 것!
  • 현재의 app architecture
    • view는 XML 레이아웃에서 정의되며, 뷰들을 위한 data는 ViewModel objects에 저장됨
    • 각 view와 관련된 ViewModel 사이에는 UI controller가 있어서 이들 사이의 중계자 역할을 함

  • Git it 버튼은 game_fragment.xml 레이아웃 파일의 Button에 정의됨
  • 사용자가 Got it 버튼을 탭하면, GameFragment 프래그먼트의 클릭리스너가 그에 대응하는, GameViewModel에 있는 클릭리스너를 호출함
  • GameViewModel에서 스코어가 업데이트됨
  • -> Button view와 GameViewModel은 서로 직접 통신하지 않음 : GameFragment에 있는 클릭리스너를 필요로 함

ViewModel을 data binding으로 전달하기

  • layout의 view가 ViewModel의 데이터와 직접(UI controller에 의존하지 않고) 통신하는 게 더 간단할 것!

  • ViewModel object는 앱의 모든 UI data를 보유함
  • ViewModel object를 data binding으로 전달함으로써, view와 viewModel object의 통신 중 일부를 자동화할 수 있음
  • 지금은 GameViewModel과 ScoreViewModel 클래스를 그와 관련된 xml layout과 관련지을 것
    • 또한 클릭 이벤트를 처리하기 위해 listener binding을 설정할 것

STEP 1 : Add data binding for the GameViewModel

  • GameViewModel을 그와 관련된 레이아웃 파일인 game_fragment.xml 과 연결할 것
    • game_fragment.xml 파일에 GameViewModel 타입의 data-binding 변수 추가하기
<layout ...>

   <data>

       <variable
           name="gameViewModel"
           type="com.example.android.guesstheword.screens.game.GameViewModel" />
   </data>
  
   <androidx.constraintlayout...
  • GameFragment 파일에서, GameViewModel을 data binding에 전달하기
    • viewModel을 binding.gameViewModel 변수로 지정해줘야 함
    • 그 코드를 onCreateView() 안에 viewModel을 초기화한 코드 이후에 넣기
// Set the viewmodel for databinding - this allows the bound layout access 
// to all the data in the ViewModel
binding.gameViewModel = viewModel

STEP 2 : Use listener bindings for event handling

  • Listener binding : onClick(), onZoomIn(), onZoomOut() 등의 이벤트가 발생됐을 때 실행되는 binding expressions
  • Listener binding은 람다식으로 작성됨
  • Data binding은 listener를 생성하고 view에 해당 리스너를 설정함
  • event를 위한 리스너가 작동하면, 리스너는 람다식을 검토함
  • Listener binding은 Android Gradle Plugin 2.0 이상에서 작동함
  • 이번 스텝에서는 GameFragment 안에 있는 클릭리스너를 game_fragment.xml 파일의 리스너 바인딩으로 치환할 것
    • game_fragment.xml에 skip_button을 위한 onClick 속성 추가
      • binding 표현을 정의하고 GameViewModel의 onSkip() 호출하기
      • 이러한 바인딩 표현을 listener binding이라고 함
<Button
   android:id="@+id/skip_button"
   ...
   android:onClick="@{() -> gameViewModel.onSkip()}"
   ... />
  • 마찬가지로 correct_button의 클릭이벤트로 GameViewModel의 onCorrect() 호출
<Button
   android:id="@+id/correct_button"
   ...
   android:onClick="@{() -> gameViewModel.onCorrect()}"
   ... />
  • end_game_button 의 클릭이벤트도 GameViewModel의 onGameFinish()로 연결
<Button
   android:id="@+id/end_game_button"
   ...
   android:onClick="@{() -> gameViewModel.onGameFinish()}"
   ... />
  • GameFragment에서, 클릭리스너를 set 하는 코드와 그 클릭리스너가 호출하는 함수 모두 삭제
//제거할 코드
binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }

/** Methods for buttons presses **/
private fun onSkip() {
   viewModel.onSkip()
}
private fun onCorrect() {
   viewModel.onCorrect()
}
private fun onEndGame() {
   gameFinished()
}

STEP 3 : Add data binding for the ScoreViewModel

  • 이번에는 ScoreViewModel을 관련된 레이아웃 파일인 score_fragment.xml과 연결할 것
    • score_fragment.xml에 ScoreViewModel 타입의 바인딩 변수 추가(위와 같은 방법)
<layout ...>
   <data>
       <variable
           name="scoreViewModel"
           type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
   </data>
   <androidx.constraintlayout.widget.ConstraintLayout
  • score_fragment.xml의 play_again_button에 onClick 속성 추가
    • listener binding을 정의하고 ScoreViewModel의 onPlayAgain()호출
<Button
   android:id="@+id/play_again_button"
   ...
   android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
   ... />
  • ScoreFragment의 onCreateView() 안에서, viewModel 초기화하기
  • 그리고 binding variable인 binding.scoreViewModel을 초기화하기
viewModel = ...
binding.scoreViewModel = viewModel
  • ScoreFragment에서, playAgainButton을 위한 clicke listener를 정의하는 코드 제거하기
//제거할 코드
binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  • 앱을 실행하면, 이전과 같지만 button view가 ViewModel과 직접 통신하는 상태가 된 것!
  • view는 더 이상 ScoreFragment의 button click listener를 이용해 통신하지 않음!

2. Add LiveData to data binding

  • Data binding은 ViewModel objects와 함께 사용되는 LiveData로 동작함
  • 우리는 지금 data binding을 ViewModel object에 추가했으므로, LiveData를 통합할 준비가 됨
  • 앱이 UI에게 data가 변경되었음을 알리기 위한 data-binding source로 LiveData를 사용하도록 변경할 것! LiveData observer method를 사용하지 않고!

STEP 1 : Add word LiveData to the game_fragment.xml file

  • 지금의 word text view를 ViewModel의 LiveData object에 직접적으로 바인딩할 것
    • game_fragment.xml의 word_text 텍스트뷰에 android:text 속성 추가
    • 그걸 LiveData object로 설정 : GameViewModel의 word
      • binding 변수인 gameViewModel 사용
<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{gameViewModel.word}"
   ... />
  • word.value를 사용하지 않음! LiveData object를 그대로 사용하면 됨
  • LiveData object는 word의 현재 값을 표시함
  • word가 null 이라면, LiveData object는 빈 문자열을 표시함

 

  • GameFragment의 onCreateView()에서, gameViewModel을 초기화한 후, fragment를 binding변수의 lifecycle owner로서 set하기
    • 위의 LiveData object의 scope를 설정함 : object가 game_fragment.xml 레이아웃의 view를 자동으로 업데이트할 수 있도록 설정함
binding.gameViewModel = ...
// 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
  • GameFragment에서 LiveData word의 옵저버 삭제하기
//제거할 코드
/** Setting up LiveData observation relationship **/
viewModel.word.observe(viewLifecycleOwner, Observer { newWord ->
   binding.wordText.text = newWord
})
  • 그럼 현재 word는 UI 컨트롤러의 observer method 없이 업데이트됨!

STEP 2 : Add score LiveData to the score_fragment.xml file

  • LiveData인 score를 score fragment의 score 텍스트뷰에 연결
    • score_fragment.xml의 score text view에 android:text 속성 추가
    • scoreViewModel.score를 그 text 속성에 할당
    • score는 정수이므로, String.valueOf() 를 이용해 문자열로 변환
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{String.valueOf(scoreViewModel.score)}"
   ... />
  • ScoreFragment에서, scoreViewModel을 초기화한 후, current activity를 binding 변수의 lifecycle owner로 설정
binding.scoreViewModel = ...
// 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
  • ScoreFragment에서 score object를 위한 옵저버 삭제
//제거할 코드
// Add observer for score
viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  • 이제 score fragment 내의 score는 scoreFragment 내의 옵저버 없이 자동으로 표시됨

STEP 3 : Add string formatting with data binding

  • layout에 data binding으로 string formatting을 추가할 수 있음
  • 현재 word의 format을 큰따옴표가 둘러싸는 방식으로 설정할 것
  • 또한 Current Score를 앞에 붙여서 나타나도록 설정할 것

  • string.xml 파일에 다음의 문자열 추가
    • word와 score 텍스트뷰를 format 하는 데 사용될 것
    • %s 와 %d 는 현재 단어와 점수를 위한 placeholders
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
  • game_fragment.xml에서, word_text 텍스트뷰의 text 속성을 quote_format string resource를 사용하도록 업데이트
  • 이 formatting string의 argument로 gameViewModel.word 전달하기
<TextView
   android:id="@+id/word_text"
   ...
   android:text="@{@string/quote_format(gameViewModel.word)}"
   ... />
  • word_text처럼 score text view도 포맷하기
  • game_fragment.xml에서 score_text 텍스트뷰의 text 속성에 추가
  • score_format 문자열 리소스를 사용하여 한 개의 숫자 argument를 전달 : LiveData object인 score를 formatting string의 argument로 전달
<TextView
   android:id="@+id/score_text"
   ...
   android:text="@{@string/score_format(gameViewModel.score)}"
   ... />
  • GameFragment 클래스의 onCreateView() 안에서 score observer 코드 삭제
//제거할 코드
viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})

 

 

https://developer.android.com/codelabs/kotlin-android-training-live-data-data-binding?index=..%2F..android-kotlin-fundamentals&hl=ko#0 

 

 

'Android > Android Kotlin 기초 실습 정리' 카테고리의 다른 글

Create a RoomDB  (0) 2022.10.27
LiveData transformations  (0) 2022.10.27
LiveData 및 LiveData Observer  (0) 2022.10.27
ViewModel : ViewModelFactory  (0) 2022.10.26
LifecycleObserver, onSaveInstanceState()  (0) 2022.10.26
Comments