[Android] Sunflower 클론코딩 (22.07.08)

유재민·2022년 7월 8일
0

Sunflower

목록 보기
11/12
post-thumbnail

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를 생성한다.

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을 추가한다.

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)
    }
}

Adapter

마지막으로 리사이클러뷰를 보여주기 위한 어댑터를 생성해보자.
리스트 아이템 갱신에 효율적인 ListAdapter와 DiffUtil을 사용하여 구현을 해보자.

1. onCreateViewHolder()

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로 바인딩 설정

2. onBindViewHolder()

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bind(getItem(position))
}

각 아이템을 bind 한다.

3. ViewHolder()

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 초기화

4. DiffCallback

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에 식물을 추가할 수 있어서 추후 작업으로 미룸.

profile
유잼코딩

0개의 댓글