저번 글에서 문제는 토스트 메시지가 프래그먼트가 기기 회전 등의 이유로 재생성될 때마다 다시 보이는 것이었다. 바로 본론으로 들어가보자. Reset the game-finished event 보통, LiveData는 데이터 변경이 있을 때만 업데이트를 옵저버에게 전달한다. 이 행동의 예외 사항은 옵저버가 비활성 상태에서 활성 상태로 변경될 때도 업데이트를 수신한다는 것이다. 이것이 게임 종료에 대한 토스트가 반복적으로 발생하는 이유이다. 화면 회전 이후 GameFragment가 재생성되면 비활성에서 활성 상태로 변경되고, 옵저버는 기존 ViewModel에 다시 연결된다. 그리고, 데이터를 수신한다. 그럼 gameFinished 메소드가 다시 호출되고, 토스트가 표시된다. GameViewModel안의 ev..
현재 우리가 수정한 앱은 'End Game' 버튼을 누르면 점수 화면으로 넘어간다. 우리는 플레이어가 모든 단어를 거치고나면 점수 화면으로 이동하기를 원한다. 플레이어가 마지막 단어를 끝내면 게임이 자동으로 끝나서 버튼을 누를 필요가 없도록 만들고 싶다. 이 기능을 구현하기 위해 우리는 이벤트가 발생되어야 하고 ViewModel에서 모든 단어가 보여졌을 때 프래그먼트에 전달되어야 한다. 이를 위해서는 LiveData 옵저버 패턴을 적용해야 한다. Observer pattern은 observable(Subject)과 observer 사이의 통신을 정의한다. 이 앱의 LiveData의 경우, 위 그림의 Subject는 LiveData 객체이다. 그리고, Observer들은 프래그먼트와 같은 UI contro..
캡슐화는 객체의 필드에 직접 접근을 막는 방법이다. 우리가 객체를 캡슐화할 때 private인 내부 필드를 수정할 수 있도록 public 메소드들을 제공해야 한다. 캡슐화를 사용하면 다른 클래스가 이 내부 필드를 어떻게 조작할지 제어할 수 있다. 현재, 우리가 수정한 코드에서 viewModel.score.value를 사용하듯 모든 외부 클래스에서 score와 word 변수를 수정할 수 있다. 우리가 지금 만드는 이 앱에서는 문제가 없을 수 있지만, 출시용 앱에서는 ViewModel 객체의 데이터를 조작해야한다. 오직 ViewModel이 앱의 데이터를 수정해야한다. 하지만, UI controller가 데이터를 읽을 필요가 있고, 그래서 데이터 필드는 완벽한 private이 될 수 없다. 앱의 데이터를 캡슐..
저번 단계에서는 score와 word의 데이터를 LiveData 객체로 바꿔줬다. 이번 단계에서는 Observer 객체를 작업한 LiveData 객체에 붙일 것이다. 우리는 fragment의 뷰(viewLifecycleOwner)를 LifecycleOwner로써 사용할 것이다. 왜 viewLifecycleOwner를 사용할까? 프래그먼트의 뷰는 프래그먼트 자체가 부서지지 않더라도 사용자가 다른 곳으로 이동하면 부서진다. 이것은 필연적으로 프래그먼트의 lifecycle과 프래그먼트 뷰의 lifecycle 두 lifecycle을 만든다. 프래그먼트의 뷰 대신 프래그먼트의 lifecycle을 참조하면 뷰를 업데이트할 때 사소한 버그가 생길 수 있다. 그래서, 프래그먼트의 뷰에 영향을 미치는 observer를 ..
이번 코드랩에서는 ViewModel 클래스의 데이터와 LiveData를 어떻게 통합하는지 배우게 될 것이다. LiveData는 데이터베이스 변경이 있을 때 알려주는 데이터 객체를 빌드하도록 해주는 AAC 중 하나이다. LiveData 클래스를 사용하기 위해서는 앱의 데이터 변경을 관찰하는 observer를 설정해야 한다. LiveData는 lifecycle을 인식한다. 그래서 active lifecycle 상태에 있는 앱 구성 요소 observer만 업데이트한다. LiveData에 관한 코드랩을 시작하기 앞서 만약 ViewModel의 사용법을 모른다면 먼저 공부해야 한다. https://bonustrack02.tistory.com/entry/Codelab%EC%9C%BC%EB%A1%9C-ViewModel..
저번 3단계에서 'End Game' 버튼에 대한 리스너를 구현했는데, 이렇게 사용자가 게임을 종료하면 ScoreFragment에서는 점수를 보여주지 않는다. 우리는 ViewModel이 ScoreFragment에서 보여질 점수를 갖고 있기를 바란다. 그래서 우리는 ViewModel의 초기화동안 factory 패턴을 사용해 점수 값을 넘겨줄 것이다. factory 패턴이 익숙하지 않을 수 있다. 지금 나에게 떠오르는 예시는 BitmapFactory 정도인데, factory 패턴은 이름 그대로 직관적인 패턴이다. 공장에서 제품을 틀로 찍어내듯 생산하는데 factory 패턴도 같은 클래스의 인스턴스를 찍어내듯 생산하게끔 만든 패턴이다. 이 때, 인스턴스를 리턴해주기 위해 메소드를 사용한다. 1. score 패키..
이번 과제는 'End Game' 버튼에 클릭 리스너를 구현하는 것이다. 1. GameFragment에서 onEndGame() 메소드를 추가한다. private fun onEndGame() { } 이 메소드는 사용자가 'End Game' 버튼을 탭할 때 호출될 것이다. 2. GameFragment에서 onCreateView() 메소드 안에 'Got It'과 'Skip' 버튼의 리스너를 설정하는 코드를 위치시킨다. 위 두줄의 코드 바로 아래에 'End Game' 버튼을 위한 클릭 리스너를 binding 변수를 사용해 설정한다. binding.endGameButton.setOnClickListener { onEndGame() } 3. GameFragment에서 앱의 점수 화면으로 넘어가기 위해 gameFini..
이전 글에서 ViewModel이 기기의 설정(configuration) 변경에도 살아있는 것을 확인했다. 그리고, 이번에는 화면에 보여줄 데이터를 넣고, 그 데이터를 작업하는 코드를 ViewModel 안에 구현한다. ViewModel은 프래그먼트, 액티비티, 또는 뷰들의 참조를 절대 포함해서는 안된다. 이 친구들은 기기의 configuration이 변경되면 살아남지 못하기 때문이다. 우리가 ViewModel을 추가하기 전에는 화면 회전 등의 기기의 설정 변경이 있으면 프래그먼트는 부셔졌다 다시 만들어졌고, 데이터는 소실되었다. 하지만 ViewModel을 추가하고 프래그먼트의 데이터를 ViewModel로 이동한 뒤에는 프래그먼트에서 보여줄 모든 데이터는 이제 ViewModel 안에 있고, 앱이 기기의 설정..
이번 코드랩에서는 AAC의 ViewModel 클래스를 알아볼 것이다. 1페이지를 천천히 보다보니 ViewModel은 factory 디자인 패턴을 사용하고 있다는 것을 알 수 있다. ViewModel을 instantiate(인스턴스화)하려면 ViewModelFactory를 사용하라고 한다. 다음 페이지부터는 주어지는 샘플 앱이 있고, 여기서 문제를 정의한 후 문제 해결을 위해 어떤 단계를 밟아서 처리할 것인지 안내하고 있다. 1. GameViewModel 클래스(ViewModel을 상속하는) 추가하기 먼저 ViewModel을 사용하기 위해서는 모듈 수준의 build.gradle에서 Gradle dependency를 추가해주어야 한다. //ViewModel implementation 'androidx.lif..