GardenFragment 화면을 구성해보자.
// GardenFragment.kt
override fun onCreateView() {
...
binding.addPlant.setOnClickListener {
navigateToPlantListPage()
}
...
}
private fun navigateToPlantListPage() {
requireActivity().findViewById<ViewPager2>(R.id.view_pager).currentItem =
PLANT_LIST_PAGE_INDEX
}
Add Plant
버튼 클릭 시 PlantListFragment 화면으로 이동
이제 My Garden에 식물을 추가할 때 생기는 List를 만들어줘야한다. 그전에 필요한 Data class를 생성한다.
GardenPlanting
이란 데이터 클래스를 생성한다.
사용자의 유용한 메타데이터와 함께 자신의 정원에 식물을 추가할 때 표현되는 데이터 객체다.
@Entity(
tableName = "garden_plantings",
foreignKeys = [
ForeignKey(entity = Plant::class, parentColumns = ["id"], childColumns = ["plant_id"])
],
indices = [Index("plant_id")]
)
테이블명을 선언하고 외래키로 Plant
테이블을 지정해준다.
data class GardenPlanting(
@ColumnInfo(name = "plant_id") val plantId: String,
/**
* 식물이 정원에 심어질 때를 나타내는 컬럼으로, 수확할 시기를 알려줄 때 사용됨
*/
@ColumnInfo(name = "plant_date") val plantDate: Calendar = Calendar.getInstance(),
/**
* 마지막으로 물을 준 날짜, 물을 줄 시기를 알려줄 때 사용됨
*/
@ColumnInfo(name = "last_watering_date")
val lastWateringDate: Calendar = Calendar.getInstance()
) {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
var gardenPlantingId: Long = 0
}
정원에 식물을 심은 날짜, 마지막으로 물을 준 날짜와 해당 식물 id를 컬럼으로 선언하며 gardenPlantingId를 primary key 로 지정한다.
GardenPlanting
데이터 클래스를 생성했다면, PlantAndGardenPlanting
데이터 클래스를 생성해보겠다.
data class PlantAndGardenPlantings(
@Embedded
val plant: Plant,
@Relation(parentColumn = "id", entityColumn = "plant_id")
val gardenPlantings: List<GardenPlanting> = emptyList()
)
@Relation
을 통해 Plant와 GardenPlanting의 1:M 관계를 간결화 할 수 있다. 위와 같이 하나의 질의를 통해 Plant와 GardenPlanting 테이블을 자동으로 맵핑시켜준다.
Data Class 셋팅이 끝났으면 Adapter을 추가해주자.
그 전에 layout에 필요한 data를 추가한다.
<!-- list_item_garden_planting.xml -->
<data>
<variable
name="clickListener"
type="android.view.View.OnClickListener" />
<variable
name="viewModel"
type="com.jaemin.sunflower_clone.viewmodels.PlantAndGardenPlantingsViewModel" />
</data>
아이템 클릭리스너와 viewModel을 추가한다.
databinding에 필요한 데이터를 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)
}
}
마지막으로 리사이클러뷰를 보여주기 위한 어댑터를 생성해보자.
리스트 아이템 갱신에 효율적인 ListAdapter와 DiffUtil을 사용하여 구현을 해보자.
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): GardenPlantingAdapter.ViewHolder {
return ViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.list_item_garden_planting,
parent,
false
)
)
}
DataBindingUtil로 list_item_garden_planting
xml로 바인딩 설정
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
각 아이템을 bind 한다.
ViewHolder 클래스를 만든다.
init {
binding.setClickListener { view ->
binding.viewModel?.plantId?.let { plantId ->
navigateToPlant(plantId, view)
}
}
}
private fun navigateToPlant(plantId: String, view: View) {
val direction = HomeViewPagerFragmentDirections
.actionViewPagerFragmentToPlantDetailFragment(plantId)
view.findNavController().navigate(direction)
}
먼저 init에 setClickListener
을 초기화 해준다.
클릭 시 Detail 페이지로 이동한다.
fun bind(plantings: PlantAndGardenPlantings) {
with(binding) {
viewModel = PlantAndGardenPlantingsViewModel(plantings)
executePendingBindings()
}
}
bind 함수를 정의한다. viewModel 초기화
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
}
}
추후에 GardenFragment에 Adapter을 연동하고 DB 작업으로 데이터를 가져오도록 하겠다.
DetailFragment를 구현해야 Garden에 식물을 추가할 수 있어서 추후 작업으로 미룸.