MVVM์ ์ค์ ํ๋ก์ ํธ์๋ ์ ์ฉํด๋ด ์๋น~~
์์ฑ์ : ์ด์ข ํ
Present Time : 2018โ10-03-WED Supplement Present Time: 2018-10-24 WED
MVVM์ด๋ Model-View-ViewModel์ผ๋ก, ๊ตฌ๊ธ Android Architecture Component(์ดํ AAC)์ ์ง์์ผ๋ก ์ธํด ์๋๋ก์ด๋์์ ์ ์ ๋ถ์ํ๊ณ ์๋ ๋์์ธ ํจํด์
๋๋ค.
๊ฐ ์์๋ ์๋์ ๊ฐ์ ๋ถ๋ถ์ ๋ด๋นํฉ๋๋ค.
- ๋ฐ์ดํฐ ๋ชจ๋ธ์ด๊ณ ๋คํธ์ํฌ, ๋ด๋ถ DB ๋ฑ์ผ๋ก๋ถํฐ ๋ฐ์ดํฐ ์์ค๋ฅผ ๋ด๋นํฉ๋๋ค.
- Activity, Fragment ๋ฑ ์ ๋ฐ์ ์ธ ๋ชจ๋ View๋ฅผ ๋ปํฉ๋๋ค.
- ViewModel์ ๊ด์ธกํ๋ฉฐ, ๋ณํ๋ ViewModel์ ๋ฐ์ดํฐ๋ฅผ View์ ๋ฐ์ํฉ๋๋ค.
- View์ ๋ํ Model์ ๋๋ค.
- Model์์ ์ ๊ณต๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ์ํ๊ฐ ๋ณ๊ฒฝ๋์์๋ Event๋ฅผ ํธ์ถํฉ๋๋ค.
- MVP ํจํด๊ณผ๋ ๋ค๋ฅด๊ฒ 1:N ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค.
- ViewModel๊ณผ View๋ DataBinding์ผ๋ก ๊ธด๋ฐํ๊ฒ ๊ฒฐํฉ๋์ด ์์ต๋๋ค.
Databinding์ ๊ดํ ๋ด์ฉ์ ์ด ๊ธ์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
์ง๋๋ฒ๊ณผ ๊ฐ์ด, ๋ ๊ฐ์ EditText์์ ํ ์คํธ๋ฅผ ๋ฐ์์ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํด์ฃผ๋ ๊ฐ๋จํ ๊ณ์ฐ๊ธฐ๋ฅผ ๋ง๋ค์ด๋ด ์๋ค!
๋ ์ด์์์๋ 2๊ฐ์ EditText, 4๊ฐ์ ๊ณ์ฐ ๋ฒํผ, ๊ฒฐ๊ณผ๋ฅผ ๋์์ค ํ ์คํธ๋ทฐ๊ฐ ์กด์ฌํฉ๋๋ค. ์ด์ ์์ํ๋ ๋ทฐ๋ชจ๋ธ์ ๋ง๋ค์ด๋ด ์๋ค.
class MainViewModel {
val firstNum = ObservableField<String>()
val secondNum = ObservableField<String>()
val result = ObservableField<String>()
fun calc(op: Char) {
result.set(when (op) {
'+' -> (firstNum.get()!!.toInt() + secondNum.get()!!.toInt()).toString()
'-' -> (firstNum.get()!!.toInt() - secondNum.get()!!.toInt()).toString()
'*' -> (firstNum.get()!!.toInt() * secondNum.get()!!.toInt()).toString()
'/' -> (firstNum.get()!!.toInt() / secondNum.get()!!.toInt()).toString()
else -> ""
})
}
}
-
ObservableField
๋ฅผ ์ด์ฉํ์ฌ xml์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ธ๋ฉ ํ ์ ์๋ ํด๋์ค๋ฅผ ๋ง๋ญ๋๋ค. -
calc
๋ฉ์๋๋กfirstNum
๊ณผsecondNum
์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ์ฐ์ฐ์ ํ ํ,result
์ ๋ฃ์ต๋๋ค.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.example.dwg76.databinding_example.MainViewModel" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/firstNum"
android:text="@={viewModel.firstNum}" />
<EditText
android:id="@+id/secondNum"
android:text="@={viewModel.secondNum}" />
<Button
android:id="@+id/plus"
android:onClick="@{() -> viewModel.calc('+')}"
android:text="๋ํ๊ธฐ" />
<Button
android:id="@+id/minus"
android:onClick="@{() -> viewModel.calc('-')}"
android:text="๋นผ๊ธฐ" />
<Button
android:id="@+id/divide"
android:onClick="@{() -> viewModel.calc('/')}"
android:text="๋๋๊ธฐ"/>
<Button
android:id="@+id/multiple"
android:onClick="@{() -> viewModel.calc('*')}"
android:text="๊ณฑํ๊ธฐ"/>
<TextView
android:id="@+id/result"
android:text="@{viewModel.result}"/>
</android.support.constraint.ConstraintLayout>
</layout>
- Two-way Databinding์ ์ด์ฉํ์ฌ ViewModel์
ObservableField
๋ค์ EditText์ ๋ฐ์ธ๋ฉํด์ค๋๋ค. - ๊ฐ Button์ onClick์ด ์คํ๋ ์ ViewModel์
calc
๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ํด ๋์์ต๋๋ค.- ๊ทธ๋ฌ๋ฉด ViewModel์ ์ด๋ฒคํธ๊ฐ ๋ค์ด์ค๋ฉด ViewModel์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ , ViewModel์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ ๋๋ค.
- View๋ ViewModel์ ๋ฐ์ดํฐ๋ฅผ ๊ด์ธกํ๊ณ ๋ฐ์ดํฐ๊ฐ ๋ฐ๋๋ค๋ฉด View์ ๋ฐ์ํฉ๋๋ค.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val viewModel = MainViewModel()
binding.viewModel = viewModel
}
}
- ๊ธฐ์กด
setContentView
๋ฅผ ์ง์ฐ๊ณ ,DatabindingUtil
์ ์ด์ฉํ์ฌ ๋ค์setContentView
๋ฅผ ํด์ค๋๋ค. ActivityMainBinding
์ ๋ ์ด์์์ ๋ฃจํธ ํ๊ทธ๋ฅผlayout
์ผ๋ก ๋ฐ๊พธ๋ฉด ์ด๋ฆ์ ๋ง์ถฐ ์๋์ผ๋ก ์์ฑ๋ฉ๋๋ค.binding
๊ฐ์ฒด์viewModel
์ ํ ๋นํด์ฃผ๋ฉด xml์์viewModel
์ ์ ๊ทผ ํ ์ ์๊ฒ ๋ฉ๋๋ค.
๊ทผ๋ฐ ์ด๊ฒ ํ๋ฉด์ ๋๋ฆฌ๋ฉด ๋ฐ์ดํฐ๋ค์ด ์ฌ๋ผ์ ธ์
onCreate
๊ฐ ํธ์ถ๋ ๋ ๋ง๋ค ์๋ก์ด ViewModel์ด ์์ฑ๋๊ธฐ ๋๋ฌธ์
๋๋ค.
๊ตฌ๊ธ์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๋ช ์ฃผ๊ธฐ์ ์ต์ ํ๋ ViewModel๊ณผ LiveData๋ฅผ ๋ด๋์ต๋๋ค.
๊ธฐ์กด ์ฝ๋๋ฅผ ์กฐ๊ธ ๋ฆฌํฉํ ๋ง๋ง ํ๋ฉด ํ๋ก๊ทธ๋๋จธ๋ ์๋ช ์ฃผ๊ธฐ๋ฅผ ํ๋ค๊ฒ ๊ด๋ฆฌํ ํ์๊ฐ ์ฌ๋ผ์ง๋๋ค. ๋ฌผ๋ก ๊ธฐ์กด ๊ตฌ์กฐ๊ฐ MVVM ํ์ ์ผ ๋ ์ด์ง๋ง์.
์ด ๊ธ์ด AAC๋ฅผ ๋ค๋ฃจ๋ ๊ธ์ ์๋๊ธฐ ๋๋ฌธ์, ๋น์ค์ ์ ์ง๋ง ViewModel๊ณผ LiveData๊ฐ ๋ฌด์์ธ์ง ๊ฐ๋ตํ๊ฒ ์ค๋ช ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
AAC์ ViewModel์ ์๋ช
์ฃผ๊ธฐ์ ์ต์ ํ๋์ด ์์ต๋๋ค.
์๊น ๋ง๋ค์๋ ViewModel์ View๊ฐ ๋ฉ์ถ๋ฉด ViewModel์ด ์ฌ๋ผ์ง๋ ๋ฐ๋ฉด, AAC์ ViewModel์ Activity๊ฐ ๋๋ ๋ ๊น์ง ์ฌ๋ผ์ง์ง ์๊ณ , View์ ์๋ช
์ฃผ๊ธฐ์ ๋ณ๊ฐ๋ก ํ๋ฌ๊ฐ๋๋ค.
class MainViewModel : ViewModel() {
}
- AAC์ ๋ทฐ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ค๋ฉด
ViewModel
ํด๋์ค๋ฅผ ์์๋ฐ์ต๋๋ค. - View์์ ViewModel์ ๋ถ๋ฌ์ฌ ๋์๋
ViewModelProviders
๋ฅผ ์ด์ฉํ์ฌ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์์ต๋๋ค.
val viewModel = ViewModelProviders.of(this)[MainViewModel::class.java]
AAC์ ViewModel์ ์ฌ์ฉํ๋ฉด ์๋ช ์ฃผ๊ธฐ์ ์ํฅ์ ๋ฐ์ง ์๊ณ ์์ ํ ViewModel์ ๋ง๋ค ์ ์์ต๋๋ค.
ViewModel
์ ViewModelProviders.of()
์ ์ธ์๋ก FragmentActivit
ํน์ Fragment
๋ฅผ ๋ฐ์ต๋๋ค.
์ด ๋ ํด๋์ค๋ LiceCycleOwner
๋ฅผ ๊ฐ์ง๊ณ ์๋๋ฐ, ViewModelProviders
๋ LifeCycleOwner
์ ๋ฐ๋ผ ViewModel
์ธ์คํด์ค๋ฅผ ๋ฐํํด์ค๋๋ค.
์ด๋ฅผ ์ด์ฉํ์ฌ ๊ฐ์ ์กํฐ๋นํฐ ์๋์ ์๋ ํ๋๊ทธ๋จผํธ๋ค๋ผ๋ฆฌ ๋ฐ์ดํฐ ๊ณต์ ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
Fragment
์์ getActivity()
๋ก ์กํฐ๋นํฐ์LifeCycleOwner
๋ฅผ ๊ฐ์ ธ์ค๋ฉด ๊ฐ์ ์กํฐ๋นํฐ ์๋์ ์๋ ๋๊ฐ์ ํ๋๊ทธ๋จผํธ๊ฐ ๊ฐ์ ViewModel
์ ๊ณต์ ํ ์ ์์ต๋๋ค.
LiveData๋ ์๋ช ์ฃผ๊ธฐ์ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ ์ธ์งํ ์ ์๋ ๊ด์ฐฐ์ด ๊ฐ๋ฅํ ํด๋์ค์ ๋๋ค.
observe
๋ฉ์๋๋ฅผ ์ด์ฉํด์ LiveData
๋ฅผ ๊ด์ธกํ ์ ์๊ณ , ๊ฐ์ด ๋ณํ๋๋ฉด observe
๊ฐ ํธ์ถ๋ฉ๋๋ค.
Start ์ Resume ์ํ์ผ ๋ observe
๊ฐ ํ์ฑํ๋๊ณ , Destroyed ์ํ๋ก ๋ค์ด๊ฐ ๋ ๊ด์ฐฐ์ ์ทจ์ํฉ๋๋ค.
์ด๋ก์จ, ๋ฉ๋ชจ๋ฆฌ ๋ฆญ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๋ณดํต์ Databinding์ ์ฌ์ฉํ ๋์๋ ๋ฐ์ดํฐ๋ฐ์ธ๋ฉ์์ ์ง์ํ๋ Observable
๊ณผ ๊ฐ์ ํด๋์ค๋ค์ ์ฌ์ฉํด์ผํ์ง๋ง, Databinding ๊ฐ์ฒด์์ setLifecycleOwner()
๋ฅผ ํธ์ถํ๋ฉด Observable
๋์ LiveData๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ํ ๋น์ํค๋ ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.setLifecycleOwner(this)
๋ฐฉ๊ธ ์งฐ๋ ์ฝ๋์ AAC๋ฅผ ์ ์ฉํด๋ด ์๋ค!
์๊ฐ๋ณด๋ค ๊ฐ๋จํ๊ฒ, ObservableField
๋ค์ ๋ชจ๋ LiveData
๋ก ๋ฐ๊พธ๊ณ , ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๊ฐ์ฒด์ setLifeCycleOwner
ํจ์ ๋ฅผ ํธ์ถํ๋ฉด ๋ฉ๋๋ค.
class MainViewModel : ViewModel() {
val firstNum = MutableLiveData<String>()
val secondNum = MutableLiveData<String>()
val result = MutableLiveData<String>()
fun calc(op: Char) {
Log.d("MainViewModel", "${firstNum.value}, ${secondNum.value}")
result.value = when (op) {
'+' -> (firstNum.value!!.toInt() + secondNum.value!!.toInt()).toString()
'-' -> (firstNum.value!!.toInt() - secondNum.value!!.toInt()).toString()
'*' -> (firstNum.value!!.toInt() * secondNum.value!!.toInt()).toString()
'/' -> (firstNum.value!!.toInt() / secondNum.value!!.toInt()).toString()
else -> ""
}
Log.d("MainViewModel", "${result.value}")
}
}
MutableLiveData
๋ ๋ณํ ์ ์๋LiveData
๋ก, ๊ฐ์ด ๋ณ๊ฒฝ๋๋ ๋ฐ์ดํฐ์ผ ๋ ์ฌ์ฉํฉ๋๋ค.- ๋ณดํต ViewModel ์์
private
๋กMutableLiveData
๋ฅผ ์์ฑํ๊ณ ,LiveData
๋ก View์ ๋ฐ์ดํฐ๋ฅผ ๋ ธ์ถํฉ๋๋ค.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val viewModel = ViewModelProviders.of(this)[MainViewModel::class.java]
binding.setLifecycleOwner(this)
binding.viewModel = viewModel
}
}
xml์ ๋ฐ๋ก ๊ฑด๋๋ฆด ํ์ ์์ด ๋๊ฐ์ด ๋์ด๋ ์๊ด์ด ์์ต๋๋ค. ๊ฐ๋จํ๊ฒ AAC๋ฅผ ์ ์ฉ์ํฌ ์ ์์ต๋๋ค. ์ด์ ์๋ช ์ฃผ๊ธฐ์๋ ์์ ํ๋ค์. ์!
JustGo๋ ํ์๊ฐ ์งํํ๋ ํ๋ก์ ํธ์ธ๋ฐ, AAC์ MVVM ํจํด์ ์ด์ฉํ์ฌ ๊ฐ๋ฐํ์์ต๋๋ค. JustGo์ ํจ๊ป MVVM ํจํด์ ์ด๋ป๊ฒ ์ ์ฉํด์ผํ ์ง ์์๋ด ์๋ค. ์ ์ฒด ํ๋ก์ ํธ๋ ์ด ๊ณณ์์ ํ์ธํ ์ ์์ต๋๋ค.
- JustGo๋ ๋ฌด์์ ์ฌํ์ง ์๋น์ค๋ก, ๊ด๊ด์ง๊น์ง ๊ฐ๋จํ ๋ค๋น๊ฒ์ด์ ์ ์ ๊ณตํด์ค๋๋ค.
- ํ๋ฉด์๋ ๊ฐ์ผ ํ ๋ชฉ์ ์ง(ex. ๋ฒ์์ฌ๊ฑฐ๋ฆฌ๊น์ง ๋๋ณด๋ก ์ด๋)๋ฅผ ์๋ ค์ค๋๋ค.
- ํ๋ฉด์๋ ์ง๋๋ฅผ ๋์์ฃผ๋ฉฐ, ์ง๋๋ ์๋ฒ์์ ๊ฐ์ ธ์จ ๋ง์ปค, ํด๋ฆฌ๋ผ์ธ ์ ๋ณด๋ฅผ ์๋ ค์ค๋๋ค.
- ์ฌ์ฉ์์ ์์น ์ ๋ณด๋ฅผ ๊ณ์ ๋ฐ์์ค๋ฉฐ, ๋ชฉ์ ์ง์ ๋์ฐฉํ๋ฉด ๋ค์ ๋ชฉ์ ์ง๋ฅผ ๋์์ค๋๋ค.
- ์ต์ข ๊ด๊ด์ง์ ๋์ฐฉํ๋ฉด, ๊ทธ ์ฌํ์ง ์ ๋ณด๋ฅผ ์๋ ค์ฃผ๋ ์๋ก์ด ์กํฐ๋นํฐ๋ฅผ ๋์์ค๋๋ค.
์์ ๊ฐ์ ๊ฐ๋จํ ๋ค๋น๊ฒ์ด์ ์ฝ๋๋ฅผ ๋ฆฌ๋ทฐํ๋ฉฐ MVVM ํจํด์ด ์ด๋ค ์์ผ๋ก ์งํ๋๋์ง ์์๋ด ์๋ค.
class NavigationViewModel : ViewModel() {
private val _direction = MutableLiveData<ArrayList<DirectionModel.Point>>()
private val _polyLine = MutableLiveData<String>()
private val _transit = MutableLiveData<String>()
private val _type = MutableLiveData<String>()
val direction get() = _direction as LiveData<ArrayList<DirectionModel.Point>>
val polyLine get() = _polyLine as LiveData<String>
val transit get() = _transit as LiveData<String>
val type get() = _type as LiveData<String>
val travelFinishEvent = SingleLiveEvent<Any>()
var index = 0
fun getNavigation(transport: String, lat: Double, lng: Double, desLat: Double, desLng: Double) {
getDirection(transport, lat, lng, desLat, desLng) {
onSuccess = {
_direction.value = body()!!.points
_polyLine.value = body()!!.polyline.replace("\\", """\""")
_transit.value = _direction.value!![index].instruction
_type.value = _direction.value!![index + 1].let { " With ${it.mode}" }
}
}
}
fun compareLocation(lat: Double, lng: Double) {
direction.value?.let { directionLiveData ->
if (index < directionLiveData.size - 1) {
val direction = directionLiveData[index + 1]
direction.lat = String.format("%.5f", direction.lat).toDouble()
direction.lng = String.format("%.5f", direction.lng).toDouble()
if (direction.lat - 0.001 < lat.toFive() && lat.toFive() < direction.lat + 0.001) {
if (direction.lng - 0.001 < lng.toFive() && lng.toFive() < direction.lng + 0.001) {
index++
_transit.value = directionLiveData[index].instruction
_type.value = directionLiveData[index + 1].let { " With ${it.mode}" }
}
}
} else if (directionLiveData.size != 0) {
travelFinishEvent.call()
}
}
}
fun Double.toFive() = String.format("%.5f", this).toDouble()
}
์๋ ์ฌ์ ์ํฉ ์ค๋ช ์ ์ ์กฐ๊ฑด๋ค์ ๋ฐ๋ผ์ ์ฝ๋๋ฅผ ์์ฑํ ์ฝ๋์ ๋๋ค.
๋ทฐ์์ ๋ทฐ๋ชจ๋ธ์ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ ๋์๋ MutableLiveData
๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๊ณ , View์ ๋
ธ์ถํ ๋์๋ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ LiveData
๋ก ์นํ์์ผ View์์ ViewModel์ ๋ฐ์ดํฐ ์ํ๋ฅผ ๋ณ๊ฒฝํ ์ ์๊ฒ ๋ง๋ญ๋๋ค. ์ฝํ๋ฆฐ ๊ณต์ ๋ฌธ์์ ๋ฐ๋ผ์ ์์ฒ๋ผ ์ฝ๋๋ฅผ ์งฐ๋๋ฐ, Mutableํ ๋ฐ์ดํฐ๋ _๋ฅผ ๋ถ์ด๊ณ , Immutable ํ ๋ฐ์ดํฐ๋ _๋ฅผ ๋ถ์ด์ง ์๊ฒ ๋ง๋ค์ด ๋ฐ์ดํฐ๋ฅผ ๋
ธ์ถ์ํต๋๋ค.
-
์ฒ์ ์์ํ ๋,
getNavigation()
์ ํตํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ฉฐ, ์ฑ๊ณต ์LiveData
์ ๋ฐ์ดํฐ๋ฅผ ์ง์ด๋ฃ์ต๋๋ค. -
compareLocation()
ํจ์๋ View๋ก๋ถํฐ ํธ์ถ ๋ ์์ ์ ๋๋ค. ์๋์ ๊ฒฝ๋๋ฅผ ์ธ์๋ก ๋ฐ์ผ๋ฉฐ, ๋ฐ์ ์์น๊ฐ ๋ชฉ์ ์ง์ ์ธ์ ํ๋ฉด ๋ค์ ๋ชฉ์ ์ง๋ฅผ ์ ๊ณตํฉ๋๋ค. -
transit
๊ณผtype
์DataBinding
์ ์ด์ฉํ์ฌ xml์ ๋ฐ์ธ๋ฉ ํด ๊ฐ์ผ ํ ๋ชฉ์ ์ง ์, ๊ฐ์ผ ํ ๋ฐฉ๋ฒ์ ๋์์ค๋๋ค.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.justgo.ui.navigation.NavigationViewModel" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
tools:context="com.justgo.ui.navigation.NavigationActivity">
<android.support.constraint.ConstraintLayout
android:id="@+id/navigation_trans_container"/>
<ImageView
android:src="@drawable/ic_bus_24px" />
<TextView
android:id="@+id/navigation_trans_tv"
android:text="To"
android:textColor="#AFFF" />
<TextView
android:id="@+id/navigation_trans_location_tv"
android:textColor="#FFF"
android:text="@{viewModel.transit}" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/navigation_tag_container">
<ImageView
android:src="@drawable/ic_bookmark_24px" />
<TextView
android:id="@+id/navigation_tag_location_tv"
android:textColor="#FFF"
android:text="@{viewModel.type}"/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</layout>
์ ์ฝ๋๋ ๋ค๋น๊ฒ์ด์ ์ ์ผ๋ถ์ ๋๋ค. ์๋ ๋ ์ด์์์์ ํ๋์ ์ ์ผ๋ก ๊ฐ์ธ์ง ๋ถ๋ถ์ ๋ด๋นํฉ๋๋ค.
android:text="@{viewModel.transit}"
, android:text="@{viewModel.type}"
๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ด์ฉํ์ฌ ์กํฐ๋นํฐ์ ๋ณ ๊ฐ์ญ ์์ด ๋ฐ๋ก xml์ ๋ฐ์ธ๋ฉ ํ ์ ์์ต๋๋ค.
class NavigationActivity : DataBindingActivity<ActivityNavigationBinding>(), OnMapReadyCallback {
override fun getLayoutId(): Int = R.layout.activity_navigation
val viewModel by lazy { ViewModelProviders.of(this)[NavigationViewModel::class.java] }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.viewModel = viewModel
viewModel.getNavigation(transType.toString(), lat, lng, desLat, desLng)
viewModel.travelFinishEvent.observe(this, Observer {
toast("Travel Is Finish!")
val arriveIntent = Intent(this, ArriveActivity::class.java)
arriveIntent.putExtra("placeid", placeId)
startActivity(arriveIntent)
finish()
})
}
override fun onMapReady(map: GoogleMap) {
viewModel.polyLine.observe(this, Observer {
val decoded = PolyUtil.decode(it)
map.addPolyline(PolylineOptions().addAll(decoded)
.width(5F))
})
viewModel.direction.observe(this, Observer { direction ->
direction!!.forEach { point ->
LatLng(point.lat, point.lng).let {
map.addMarker(MarkerOptions().position(it))
}
}
})
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1F, object : LocationListener {
override fun onLocationChanged(location: Location) {
CameraUpdateFactory.newLatLngZoom(LatLng(location.latitude, location.longitude), 17F).let {
map.animateCamera(it)
viewModel.compareLocation(location.latitude, location.longitude)
}
}
}
}
}
MVVM ํจํด์์๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ด์ฉํ์ฌ xml์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ก ๋ฐ์ธ๋ฉ ํ๋ ๋ฐฉ๋ฒ์ ๊ถ์ฅํฉ๋๋ค. ์ง๋์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ ์ด์ฉ ์ ์๋ ๊ฒฝ์ฐ๋ Activity๋ Fragment์์ ์ฒ๋ฆฌํ์ง๋ง, ๊ทธ๋ฐ ํน๋ณํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด xml์์ ์ฒ๋ฆฌํ ์ ์๋๋ก ํฉ๋๋ค.
- Activity, ์ฆ
View
์์๋ViewModelProviders
๋ฅผ ์ด์ฉํ์ฌ ์๋ช ์ฃผ๊ธฐ์ ์์ ํViewModel
์ ๋ถ๋ฌ์ต๋๋ค. DataBindingActivity<ActivityNavigationBinding>()
์ ๋ฐ์ดํฐ๋ฐ์ธ๋ฉ์ ํธํ๊ฒ ํ๊ธฐ ์ํด ๋ง๋AppCompatActivity
์ ์์๋ฐ์ ์ถ์ ํด๋์ค์ ๋๋ค.getLayoutId()
์์ ๋ ์ด์์ ์์ด๋๋ฅผ ๋ฐ๊ณ , ์ ๋ค๋ฆญ์์ ๋ฐ์ ํด๋์ค๋ฅผ ์ด์ฉํ์ฌbinding
์ด๋ผ๋ ์ด๋ฆ์ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.binding
๊ฐ์ฒด์ viewModel์ ๋ฐ์ธ๋ฉํ์ฌ xml์์ ViewModel์ ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์๊ฒ๋ ํฉ๋๋ค.- View์์๋
travelFinishEvent
,direction
,polyline
์ ๊ด์ธกํ๋ฉฐ ๋ฐ์ดํฐ์ ๋ณํ๊ฐ ๊ฐ์งํ๋ฉด ์๋์ ๊ฐ์ ๋ก์ง์ ์ฒ๋ฆฌํฉ๋๋ค.
- ์ฌํ์ด ๋๋ฌ์ ๋ ํธ์ถ๋๋ฉฐ,
ArriveActivity
๋ผ๋ ์๋ก์ด ์กํฐ๋นํฐ๋ฅผ ๋์์ค๋๋ค. - ์กํฐ๋นํฐ๋ฅผ ์ ํํ๊ธฐ ์ํด์๋ Navigator ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ์ฌ ViewModel์์ ์ฒ๋ฆฌํ๊ฑฐ๋
SingleLiveEvent
๋ฅผ ์ด์ฉํ์ฌ ์กํฐ๋นํฐ์์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ์๋๋ฐ, ํ์์ ๋ฐฉ๋ฒ์ด ๊ฐ๊ฒฐํ๊ธฐ ๋๋ฌธ์ ํ์์ ๋ฐฉ๋ฒ์ ์ ํํ์์ต๋๋ค.
- ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ฉด
polyLine
๊ณผdirection
์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ฃผ๋๋ฐ, VIew์์ ์ดLiveData
๋ฅผ Observe ํ๋ฉฐ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์์ ๋ ๋งต์ ํด๋ฆฌ๋ผ์ธ๊ณผ ๋ง์ปค๋ฅผ ๊ทธ๋ ค์ค๋๋ค. travelFinishEvent
์ ๋์ผํ๊ฒSingleLiveEvent
๋ฅผ ์ด์ฉํ์ฌ ๊ตฌํํ์ฌ๋ ๋ฌด๊ดํ์ง๋ง,LiveData
์ Observe๋ฅผ ์ดํดํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ก์ง์ ๊ตฌ์ฑํ์์ต๋๋ค.
- 3์ด๋ง๋ค ์์น ์ ๋ณด๋ฅผ ๊ฐฑ์ ํ๋ฉฐ
viewModel.compareLocation
์ ํธ์ถํ๋ฉฐ ํ์ฌ ์์น์ ๋ชฉ์ ์ง๋ฅผ ๋น๊ตํฉ๋๋ค. - ์์น ์ ๋ณด ๊ฐฑ์ ๊ฐ์ ๊ฒฝ์ฐ๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ์ ๋ฌด๋ฆฌ๊ฐ ์์ด์ ์ด๋ฐ ๊ฒฝ์ฐ์๋ View(Activity)์์ ์ฒ๋ฆฌ๋ฅผ ํ์์ต๋๋ค.
์ ๋ฐฉ๋ฒ์ด ์ณ์ ๋ฐฉ๋ฒ์ผ๋ก MVVM ํจํด์ ๊ตฌํํ ๊ฒ์ด๋ผ๊ณ ํ์ ํ ์๋ ์์ต๋๋ค. ํ์ง๋ง ์ ์ฝ๋๋ฅผ ๋ฆฌ๋ทฐํ๋ฉฐ MVVM ํจํด์ ๋ํ ๊ฐํผ๋ฅผ ์ก์ผ์ จ์ผ๋ฉด ํ๋ ๋ฐ๋จ์ ๋๋ค! ๊ฐ์ฌํฉ๋๋ค.