From 22b0de0fe8bb773b9003c3e6c3611f9b5b1ad702 Mon Sep 17 00:00:00 2001 From: watermin Date: Fri, 26 May 2023 23:36:03 +0900 Subject: [PATCH] =?UTF-8?q?#23=20[feat]=20=EC=84=9C=EB=B2=84=ED=86=B5?= =?UTF-8?q?=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../carrot_aos/data/entity/ApiFactory.kt | 3 + .../data/model/response/HomeResponse.kt | 38 +++++ .../carrot_aos/data/service/HomeService.kt | 10 ++ .../carrot_aos/presentation/UiState.kt | 7 + .../presentation/home/HomeAdapter.kt | 108 ++++++++++++++ .../presentation/home/HomeFragment.kt | 41 +++++- .../presentation/home/HomeViewModel.kt | 136 ++++++++++++++++++ .../main/res/drawable/sel_home_item_like.xml | 5 + app/src/main/res/layout/fragment_home.xml | 7 + app/src/main/res/layout/item_home.xml | 3 +- 10 files changed, 356 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/sumin/android/carrot_aos/data/model/response/HomeResponse.kt create mode 100644 app/src/main/java/com/sumin/android/carrot_aos/data/service/HomeService.kt create mode 100644 app/src/main/java/com/sumin/android/carrot_aos/presentation/UiState.kt create mode 100644 app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeAdapter.kt create mode 100644 app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeViewModel.kt create mode 100644 app/src/main/res/drawable/sel_home_item_like.xml diff --git a/app/src/main/java/com/sumin/android/carrot_aos/data/entity/ApiFactory.kt b/app/src/main/java/com/sumin/android/carrot_aos/data/entity/ApiFactory.kt index bc89ba3..0564f52 100644 --- a/app/src/main/java/com/sumin/android/carrot_aos/data/entity/ApiFactory.kt +++ b/app/src/main/java/com/sumin/android/carrot_aos/data/entity/ApiFactory.kt @@ -1,7 +1,9 @@ +import androidx.datastore.preferences.protobuf.Api import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.sumin.android.carrot_aos.BuildConfig import com.sumin.android.carrot_aos.data.service.ChatService +import com.sumin.android.carrot_aos.data.service.HomeService import com.sumin.android.carrot_aos.data.service.ReviewService import com.sumin.android.carrot_aos.data.service.HeartService import com.sumin.android.carrot_aos.data.service.RecommendationService @@ -42,6 +44,7 @@ object SaleServicePool { } object ServicePool { + val homeService = ApiFactory.create() val chatService = ApiFactory.create() val UserIdService = ApiFactory.create() val reviewService = ApiFactory.create() diff --git a/app/src/main/java/com/sumin/android/carrot_aos/data/model/response/HomeResponse.kt b/app/src/main/java/com/sumin/android/carrot_aos/data/model/response/HomeResponse.kt new file mode 100644 index 0000000..75be047 --- /dev/null +++ b/app/src/main/java/com/sumin/android/carrot_aos/data/model/response/HomeResponse.kt @@ -0,0 +1,38 @@ +package com.sumin.android.carrot_aos.data.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class HomeResponse( + @SerialName("status") + val status: Int, + @SerialName("message") + val message: String, + @SerialName("data") + val data: List, +) { + @Serializable + data class HomeData( + @SerialName("saleId") + val saleId: Int, + @SerialName("title") + val title: String, + @SerialName("saleImgUrl") + val saleImgUrl: String, + @SerialName("location") + val location: String, + @SerialName("time") + val time: String, + @SerialName("isUpdated") + val isUpdated: Boolean, + @SerialName("price") + val price: Int, + @SerialName("isDiscount") + val isDiscount: Boolean, + @SerialName("likeCount") + val likeCount: Int, + @SerialName("isCheckLike") + val isCheckLike: Boolean + ) +} diff --git a/app/src/main/java/com/sumin/android/carrot_aos/data/service/HomeService.kt b/app/src/main/java/com/sumin/android/carrot_aos/data/service/HomeService.kt new file mode 100644 index 0000000..c3f5889 --- /dev/null +++ b/app/src/main/java/com/sumin/android/carrot_aos/data/service/HomeService.kt @@ -0,0 +1,10 @@ +package com.sumin.android.carrot_aos.data.service + +import com.sumin.android.carrot_aos.data.model.response.HomeResponse +import retrofit2.Call +import retrofit2.http.GET + +interface HomeService { + @GET("/sale") + fun getHomeList(): Call +} \ No newline at end of file diff --git a/app/src/main/java/com/sumin/android/carrot_aos/presentation/UiState.kt b/app/src/main/java/com/sumin/android/carrot_aos/presentation/UiState.kt new file mode 100644 index 0000000..e368dfd --- /dev/null +++ b/app/src/main/java/com/sumin/android/carrot_aos/presentation/UiState.kt @@ -0,0 +1,7 @@ +package com.sumin.android.carrot_aos.presentation + +sealed interface UiState { + object Success: UiState + object Failure: UiState + object Error: UiState +} \ No newline at end of file diff --git a/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeAdapter.kt b/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeAdapter.kt new file mode 100644 index 0000000..9126c30 --- /dev/null +++ b/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeAdapter.kt @@ -0,0 +1,108 @@ +package com.sumin.android.carrot_aos.presentation.home + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import coil.load +import com.sumin.android.carrot_aos.data.model.response.HomeResponse +import com.sumin.android.carrot_aos.databinding.ItemHomeBinding +import java.text.DecimalFormat + +class HomeAdapter( + private val navigateToSale: (Int, String) -> Unit, + context: Context +) : RecyclerView.Adapter() { + private val inflater by lazy { LayoutInflater.from(context) } + private var homeList: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val binding = ItemHomeBinding.inflate(inflater, parent, false) + return HomeViewHolder(navigateToSale, binding) + } + + override fun getItemCount() = homeList.size + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (holder is HomeViewHolder) holder.onBind(homeList[position]) + } + + @SuppressLint("NotifyDataSetChanged") + fun setHomeList(list: List) { + this.homeList = list.toList() + notifyDataSetChanged() + } +} + +//class HomeAdapter( +// private val navigateToSale: (Int, String) -> Unit, +// context: Context +//) : ListAdapter(diffUtil) { +// private val inflater by lazy { LayoutInflater.from(context) } +// +// override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { +// val binding = ItemHomeBinding.inflate(inflater, parent, false) +// return HomeViewHolder(navigateToSale, binding) +// } +// +// override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { +// if (holder is HomeViewHolder) holder.onBind(currentList[position]) +// } +// +// companion object { +// val diffUtil = object : DiffUtil.ItemCallback() { +// override fun areItemsTheSame( +// oldItem: HomeResponse.HomeData, +// newItem: HomeResponse.HomeData, +// ): Boolean { +// return oldItem.saleId == newItem.saleId +// } +// +// override fun areContentsTheSame( +// oldItem: HomeResponse.HomeData, +// newItem: HomeResponse.HomeData, +// ): Boolean { +// return oldItem == newItem +// } +// } +// } +//} + +class HomeViewHolder( + private val navigateToSale: (Int, String) -> Unit, + private val binding: ItemHomeBinding, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind(data: HomeResponse.HomeData) { + binding.ivHomeItem.load(data.saleImgUrl) + binding.tvHomeItemContent.text = data.title + binding.tvHomeItemWhere.text = data.location + binding.tvHomeItemReupload.visibility = toggleVisibility(data.isUpdated) + binding.tvHomeItemTime.text = data.time + binding.tvHomeItemPrice.text = formatPrice(data.price) + binding.ivHomeItemLike.visibility = toggleVisibility(data.likeCount == 0) + binding.ivHomeItemLike.isActivated = data.isCheckLike + binding.tvHomeItemLike.visibility = toggleVisibility(data.likeCount == 0) + binding.tvHomeItemLike.text = data.likeCount.toString() + binding.ivHomeItemDown.visibility = toggleVisibility(data.isDiscount) + binding.tvHomeItemDown.visibility = toggleVisibility(data.isDiscount) + binding.layoutHomeItem.setOnClickListener { + navigateToSale(data.saleId, formatPrice(data.price)) + } + } + + private fun toggleVisibility(status: Boolean): Int { + return when (status) { + true -> View.VISIBLE + else -> View.GONE + } + } + + private fun formatPrice(price: Int): String { + val priceFormat = DecimalFormat("#,###") + return "${priceFormat.format(price)}원" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeFragment.kt b/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeFragment.kt index 83fb8b4..19913b7 100644 --- a/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeFragment.kt +++ b/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeFragment.kt @@ -1,9 +1,48 @@ package com.sumin.android.carrot_aos.presentation.home +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels import com.sumin.android.carrot_aos.R import com.sumin.android.carrot_aos.databinding.FragmentHomeBinding +import com.sumin.android.carrot_aos.presentation.UiState import com.sumin.android.carrot_aos.presentation.base.BindingFragment +import com.sumin.android.carrot_aos.presentation.sale.SaleActivity +import com.sumin.android.carrot_aos.util.extension.showSnackbar class HomeFragment : BindingFragment(R.layout.fragment_home) { + private val viewModel by viewModels() -} \ No newline at end of file + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initRecyclerView() + } + + private fun initRecyclerView() { + val adapter = HomeAdapter(::navigateToSaleWith, requireContext()) + binding.rvHome.adapter = adapter + adapter.setHomeList(viewModel.homeList) + } + +// private fun addObservers(adapter: HomeAdapter) { +// viewModel.homeResult.observe(viewLifecycleOwner) { +// adapter.submitList(it.data) +// } +// viewModel.result.observe(viewLifecycleOwner) { +// when(it) { +// UiState.Success -> binding.root.showSnackbar("성공") +// UiState.Failure -> binding.root.showSnackbar("실패") +// UiState.Error -> binding.root.showSnackbar("에러") +// } +// } +// } + + private fun navigateToSaleWith(id: Int, price: String) { + val intent = Intent(requireContext(), SaleActivity::class.java).apply { + putExtra("itemId", id) + putExtra("itemPrice", price) + } + startActivity(intent) + } +} diff --git a/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeViewModel.kt b/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeViewModel.kt new file mode 100644 index 0000000..cd4239f --- /dev/null +++ b/app/src/main/java/com/sumin/android/carrot_aos/presentation/home/HomeViewModel.kt @@ -0,0 +1,136 @@ +package com.sumin.android.carrot_aos.presentation.home + +import androidx.lifecycle.ViewModel +import com.sumin.android.carrot_aos.data.model.response.HomeResponse + +class HomeViewModel : ViewModel() { + val homeList = listOf( + HomeResponse.HomeData( + saleId = 1, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 2, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 3, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 4, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 5, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 6, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 7, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + HomeResponse.HomeData( + saleId = 8, + title = "동교동 이웃 dd님에게만 보이는 순간이동 포털이 열렸어요", + saleImgUrl = "https://carrotbucket32.s3.ap-northeast-2.amazonaws.com/e47bd937-6831-4561-b7fa-042399477606-sale1.webp", + location = "서강동", + time = "2023. 5. 19. 오후 2:52:58", + isUpdated = false, + price = 10000, + isDiscount = true, + likeCount = 0, + isCheckLike = false + ), + ) +// private val homeService = ServicePool.homeService +// +// private val _homeResult = MutableLiveData() +// val homeResult: LiveData +// get() = _homeResult +// private val _result = MutableLiveData() +// val result: LiveData +// get() = _result +// +// fun getHomeList() { +// homeService.getHomeList() +// .enqueue(object : Callback { +// override fun onResponse( +// call: Call, +// response: Response +// ) { +// if (response.isSuccessful) { +// _result.value = UiState.Success +// _homeResult.value = response.body() +// } else { +// _result.value = UiState.Failure +// } +// } +// +// override fun onFailure(call: Call, t: Throwable) { +// _result.value = UiState.Error +// } +// +// }) +// } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/sel_home_item_like.xml b/app/src/main/res/drawable/sel_home_item_like.xml new file mode 100644 index 0000000..17c415e --- /dev/null +++ b/app/src/main/res/drawable/sel_home_item_like.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index e020faf..5e746a1 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -3,6 +3,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> + + + + + + android:src="@drawable/sel_home_item_like" />