Fragment 들이 ViewModel 을 공유하하는 방법
중개자 역할 Host Activity 역할 부담이 줄어들음
LiveData
: 데이터가 변경되었는지 관찰하는 역할,
외부 접근 차단을 위해 private 로 선언
Observer
: Livedata 로부터 데이터 변경 알림을 받아 필요한 조치를 함
MutableLiveData
는 데이터를 변경할 수 있는 LiveData의 구현체LiveData Getter: _myCount
를 읽기 전용으로 외부에 노출하기 위한 LiveData의 getter를 정의 -> 외부에서 _myCount를 직접 변경X, 오직 읽기만 가능setCount()
: 속성 업데이트 함수package com.example.fragmentexample2
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class CommonDataViewModel : ViewModel() {
// 전달받는 값
private var _myCount = MutableLiveData<Int>(0)
// backUp muCount
val myCount: LiveData<Int> get() = _myCount
fun setCount(cnt:Int){
_myCount.value = cnt
}
}
getCount()
: MutableLiveData 타입 객체의 값을 가져 옴.ViewModel 인스턴스 생성
: by lazy
를 이용해 처음 접근할 때만 생성하도록, CommonDataViewModel 의 객체를 생성하고, 액티비티에 속한 ViewModel로 지정viewModel.setCount(currentCount)
를 호출하여 ViewModel의 setCount 메서드를 사용하여 ViewModel의 데이터를 업데이트class ClickFragment : Fragment() {
private val viewModel: CommonDataViewModel by lazy {
ViewModelProvider(requireActivity()).get(CommonDataViewModel::class.java)
}
private var currentCount = 50
private lateinit var binding: FragmentClickBinding
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.countUpButton.setOnClickListener {
currentCount += 5
binding.countTextView.text = "Count = $currentCount"
viewModel.setCount(currentCount)
}
binding.countDownButton.setOnClickListener {
currentCount -= 5
binding.countTextView.text = "Count = $currentCount"
viewModel.setCount(currentCount)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentClickBinding.inflate(inflater, container, false)
return binding.root
}
}
class ProgressFragment : Fragment() {
private lateinit var binding: FragmentProgressBinding
private val viewModel: CommonDataViewModel by lazy {
ViewModelProvider(requireActivity()).get(CommonDataViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentProgressBinding.inflate(inflater, container, false)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val myObserver = object : Observer<Int> {
override fun onChanged(cnt: Int) {
if (cnt in 1..100) {
binding.progressBar.progress = cnt
binding.ratingBar.rating = cnt / 20f
} else {
binding.progressBar.progress = 50
binding.ratingBar.rating = 2.5f
}
}
}
viewModel.myCount.observe(
viewLifecycleOwner, myObserver
)
}
}
package com.example.fragmentexample2
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.fragmentexample2.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
< 결론 >
- 가장 편해진 건 MainActivity 클래스 -> 중개 역할에서 벗어남
- ClickFragment에서 선언한 Interface를 상속받을 필요가 없음.
- ProgressFragment의 changeProgressBar()를 호출할 필요가 없음.