지난 포스팅에서 만든 Todo App에 Live Data를 사용해보겠다!!
LiveData는 관찰가능한 데이터 Holder 클래스로, Android Jetpack의 구성요소이다.
지난 포스팅에 사용한 Viewmodel은 이러한 LiveData를 가지고 있다.
예를 들어 위 그림과 같이 음식점 메뉴를 나타내는 ViewModel 안에 LiveData가 있다고 가정하자!
LiveData의 '가격'이 변경되면 UI에서 알아채고, 변경된 데이터를 처리할 수 있다.
안드로이드 3.1 이상부터 LiveData 및 Viewmodel은 데이터 바인딩과 함께 작동한다.
Activity, Fragment 등의 LifeCycle을 인식하여 LifeCycle 내에서만 동작하는 요소로 LifeCycle이 종료되면(화면 destroy) 같이 삭제된다.
그러므로 메모리 누수가 없고, 수명주기 문제를 해결해주어 데이터 관리를 개발자가 하지 않아도 된다는 점 등 많은 이점을 가지고 있다.
LiveData는 옵저버 패턴 관련 즉, 데이터의 변경 사항을 알 수 있다.
🔸 build.gradle - dependencies 에 추가
def lifecycle_version = "2.2.0
// viewModel
implementation "androidx.lifecycle:lifrcycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
🔸 생성 및 사용하기
// ViewModel 생성
val myViewModel = ViewModelProviders.of(this).get(MyDataViewModel::class.java)
val binding = ActivityMainBindind.inflate(layoutInflater)
// 생성한 ViewModel과 Databinding을 연결한 후
binding.viewmodel = myViewModle
// 다음 코드를 추가해준다.
binding.setLifecycleOwner(this)
setContentView(binding.root)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel : TodoAdapter.MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.rvItem.apply {
layoutManager = LinearLayoutManager(this@MainActivity)
adapter = TodoAdapter(emptyList(), onClickDelete = {
viewModel.deleteTodo(it)
}, onClickItem = {
viewModel.toggleTodo(it)
})
}
binding.addBtn.setOnClickListener {
val todo = Todo(binding.addEt.text.toString())
viewModel.addTodo(todo)
}
//관찰하여 UI를 업데이트 하도록 observe() 구현
viewModel.todoLiveData.observe(this, Observer {
(binding.rvItem.adapter as TodoAdapter).setData(it)
})
}
}
data class Todo(
val text: String,
var isDone: Boolean = false
)
class TodoAdapter(
private var MyDataset: List<Todo>,
val onClickDelete: (todo: Todo) -> Unit,
val onClickItem: (todo: Todo) -> Unit
) :
RecyclerView.Adapter<TodoAdapter.TodoListHolder>() {
class TodoListHolder(val binding: ItemTodoBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoAdapter.TodoListHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_todo, parent, false)
return TodoListHolder(ItemTodoBinding.bind(view))
}
override fun getItemCount(): Int = MyDataset.size
// 이 부분 추가!
fun setData(newData : List<Todo>){
MyDataset = newData
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: TodoListHolder, position: Int) {
val todo = MyDataset[position]
holder.binding.tvList.text = todo.text
if (todo.isDone) {
holder.binding.tvList.apply {
paintFlags = paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
setTypeface(null, Typeface.ITALIC)
}
} else {
holder.binding.tvList.apply {
paintFlags = 0
setTypeface(null, Typeface.NORMAL)
}
}
holder.binding.ivDelete.setOnClickListener { onClickDelete.invoke(todo) }
holder.binding.root.setOnClickListener { onClickItem.invoke(todo) }
}
class MainViewModel : ViewModel() {
// LivaData로 하면 읽기만 가능하니 수정이 가능한 MutableLiveData를 쓴다.
val todoLiveData = MutableLiveData<List<Todo>>()
private val data = arrayListOf<Todo>()
fun toggleTodo(todo: Todo) {
todo.isDone = !todo.isDone
// 추가!
todoLiveData.value = data
}
fun addTodo(todo: Todo) {
data.add(todo)
// 추가!
// todoLivaData의 값을 변경된 data로 갱신해준다.
todoLiveData.value = data
}
fun deleteTodo(todo: Todo) {
data.remove(todo)
// 추가!
todoLiveData.value = data
}
}
}