우선 @BindingAdapter은 뷰바인딩 할때 알아보도록 하고 어떤 adapter을 썻는지 확인해보자
(1) PagingDataAdapter
(2) FragmentStateAdapter
(3) ListAdapter
여기서 2번은 뷰페이져에 사용하는 어댑터니 다음에 알아보고 ListAdapter은 자주 사용해 봤는데 PagingDataAdapter은 보긴 했는데 어떻게 쓰는건지 몰라서 안써봤다...
먼저 Adapter란 리사이클러 뷰에서 표시할 뷰들의 set을 바인딩 해준다.
1. ListAdapter
ListAdapter는 Adapter와 diff를 제공함
diff는 submit 된 리스트들의 변화를 감지하고 알려준다. AsyncListDiffer를 직접 사용하지 않아도 간편하게 이러한 기능을 제공하는게 ListAdapter임.
좀 더 자세하고 특정한 상황에서 사용하기 위해서는 AsyncListDiffer를 사용해서 구현해 줄 수 있다.
2. PagingDataAdapter
위의 ListAdapter가 List를 리사이클러뷰에 나타내기 위한 어댑터라면 PagingDataAdapter는 PagingData를 나타내기 위한 어댑터임.
paing데이터가 새로 들어올때 마다 submitData가 실행해준다. 페이지 네이션을 편하게 해주는 어댑터인거 같음.
여기에 1번과 같이 diff도 제공.
3.LiveData
다른 앱 컴퍼넌트 들의 라이프사이클을 인지하는 옵저버블 데이타 클래스임. 그래서 라이브데이터는 앱 컴퍼넌트가 존재할때만 데이터를 제공하므로 메모리 누수가 적다.
라이브 데이터사용시의 이점은 많음 -> 안드로이드 디벨로퍼 페이지확인 ㄱㄱ 굳!
4. Flow
비동기적으로 처리할 수 있는 데이터로 코루틴에서 사용된다.
suspend fun을 사용하므로 ui스레드를 막지않고 자연스럽게 네트워크와의 연결이 가능하다.
자세하게는 모르겠다...
5. ListAdapter 분석
class GardenPlantingAdapter :
ListAdapter<PlantAndGardenPlantings, GardenPlantingAdapter.ViewHolder>(
GardenPlantDiffCallback()
) { ...
}
private class GardenPlantDiffCallback : DiffUtil.ItemCallback<PlantAndGardenPlantings>() {
override fun areItemsTheSame(
oldItem: PlantAndGardenPlantings,
newItem: PlantAndGardenPlantings
): Boolean {
return oldItem.plant.plantId == newItem.plant.plantId
}
override fun areContentsTheSame(
oldItem: PlantAndGardenPlantings,
newItem: PlantAndGardenPlantings
): Boolean {
return oldItem.plant == newItem.plant
}
}
리스트 어댑터를 만들때 diffutil을 사용한 itemcallback을 구현해 줘야한다. 앞에서 설명했듯이 PlantAndGardenPlantings 리스트가 들어올때 이전과 새로운 리스트가 같은지 다른지를 확인해줌.
위에서 bind해주는 함수를 보면
fun bind(plantings: PlantAndGardenPlantings) {
with(binding) {
viewModel = PlantAndGardenPlantingsViewModel(plantings)
executePendingBindings()
}
}
이런 식으로 되어있는데 여기서 사용하는 viewModel은
class PlantAndGardenPlantingsViewModel(plantings: PlantAndGardenPlantings) {
private val plant = checkNotNull(plantings.plant)
private val gardenPlanting = plantings.gardenPlantings[0]
val waterDateString: String = dateFormat.format(gardenPlanting.lastWateringDate.time)
val wateringInterval
get() = plant.wateringInterval
val imageUrl
get() = plant.imageUrl
val plantName
get() = plant.name
val plantDateString: String = dateFormat.format(gardenPlanting.plantDate.time)
val plantId
get() = plant.plantId
companion object {
private val dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US)
}
}
위와 같은데 repository를 사용하는 뷰모델이 아니라 데이터 바인딩을 위한 viewModel인거같다.. 나중에 데이터 바인딩을 할 때 공부해 보자. 확실히 나는 bind 를 사용하면
chat.text = item.msg
이렇게 뷰와 string을 연결해줬었는데 그냥 xml에서 모든게 다 되구나.
adapter.submitList(result)
그리고 adapter을 사용하는 프래그먼트에서 submit을 해주면 된다.
6. ListAdapter에 Data를 제공하는 Viewmodel
@HiltViewModel
class PlantListViewModel @Inject internal constructor(
plantRepository: PlantRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
private val growZone: MutableStateFlow<Int> = MutableStateFlow(
savedStateHandle.get(GROW_ZONE_SAVED_STATE_KEY) ?: NO_GROW_ZONE
)
val plants: LiveData<List<Plant>> = growZone.flatMapLatest { zone ->
if (zone == NO_GROW_ZONE) {
plantRepository.getPlants()
} else {
plantRepository.getPlantsWithGrowZoneNumber(zone)
}
}.asLiveData()
init {
viewModelScope.launch {
growZone.collect { newGrowZone ->
savedStateHandle.set(GROW_ZONE_SAVED_STATE_KEY, newGrowZone)
}
}
}
fun setGrowZoneNumber(num: Int) {
growZone.value = num
}
fun clearGrowZoneNumber() {
growZone.value = NO_GROW_ZONE
}
fun isFiltered() = growZone.value != NO_GROW_ZONE
companion object {
private const val NO_GROW_ZONE = -1
private const val GROW_ZONE_SAVED_STATE_KEY = "GROW_ZONE_SAVED_STATE_KEY"
}
}
live 데이터를 observe해서 리스트에 submit 해주면 live데이터가 변할때마다 리스트가 업데이트 되서 좋음
Paging에 대해서는 다음글에서 알아보자
'안드로이드 jetpack > sunflower' 카테고리의 다른 글
6.liveData를 사용해 보자(Viewmodel 적용기) (0) | 2021.05.04 |
---|---|
5. work에 대해서 알아보자 (0) | 2021.05.02 |
4. Paging, PagingAdapter (0) | 2021.05.02 |
2.sunflower분석 ( hilt) (0) | 2021.04.29 |
1. DI 종속성 주입 (sunflower 분석 ,hilt, dagger) (0) | 2021.04.29 |