diff --git a/app/src/main/java/com/sopt/geonppang/domain/model/BestBakery.kt b/app/src/main/java/com/sopt/geonppang/domain/model/BestBakery.kt new file mode 100644 index 00000000..2e544227 --- /dev/null +++ b/app/src/main/java/com/sopt/geonppang/domain/model/BestBakery.kt @@ -0,0 +1,16 @@ +package com.sopt.geonppang.domain.model + +data class BestBakery( + val bakeryId: Int, + val bakeryName: String, + val firstNearStation: String, + val secondNearStation: String?, + val isBooked: Boolean, + val bookmarkCount: Int, + // TODO image type -> String 으로 수정 + val bakeryImage: Int, + val reviewCount: Int, + val isHACCP: Boolean, + val isVegan: Boolean, + val isNonGMO: Boolean, +) diff --git a/app/src/main/java/com/sopt/geonppang/domain/model/BestReview.kt b/app/src/main/java/com/sopt/geonppang/domain/model/BestReview.kt new file mode 100644 index 00000000..87d4ad89 --- /dev/null +++ b/app/src/main/java/com/sopt/geonppang/domain/model/BestReview.kt @@ -0,0 +1,14 @@ +package com.sopt.geonppang.domain.model + +data class BestReview( + val bakeryId: Int, + val bakeryName: String, + val reviewText: String, + val firstReviewChip: String, + val secondReviewChip: String, + val isBooked: Boolean, + val bookmarkCount: Int, + // TODO image type -> String 으로 수정 + val bakeryImage: Int, + val reviewCount: Int, +) diff --git a/app/src/main/java/com/sopt/geonppang/presentation/MainActivity.kt b/app/src/main/java/com/sopt/geonppang/presentation/MainActivity.kt index a78b3e5f..a7a70d02 100644 --- a/app/src/main/java/com/sopt/geonppang/presentation/MainActivity.kt +++ b/app/src/main/java/com/sopt/geonppang/presentation/MainActivity.kt @@ -6,6 +6,7 @@ import androidx.fragment.app.commit import androidx.fragment.app.replace import com.sopt.geonppang.R import com.sopt.geonppang.databinding.ActivityMainBinding +import com.sopt.geonppang.presentation.home.HomeFragment import com.sopt.geonppang.presentation.mypage.MyPageFragment import com.sopt.geonppang.util.binding.BindingActivity @@ -17,13 +18,13 @@ class MainActivity : BindingActivity(R.layout.activity_main } private fun initView() { -// supportFragmentManager.findFragmentById(R.id.fcv_home_container) -// ?: navigateTo() + supportFragmentManager.findFragmentById(R.id.fcv_home_container) + ?: navigateTo() binding.bnvHome.setOnItemSelectedListener { menu -> when (menu.itemId) { // TODO 해당하는 Fragment 연결 - R.id.menu_home -> {} + R.id.menu_home -> navigateTo() R.id.menu_sotrelist -> {} R.id.menu_mypage -> navigateTo() } diff --git a/app/src/main/java/com/sopt/geonppang/presentation/home/BestBakeryAdapter.kt b/app/src/main/java/com/sopt/geonppang/presentation/home/BestBakeryAdapter.kt new file mode 100644 index 00000000..efac445d --- /dev/null +++ b/app/src/main/java/com/sopt/geonppang/presentation/home/BestBakeryAdapter.kt @@ -0,0 +1,36 @@ +package com.sopt.geonppang.presentation.home + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.sopt.geonppang.databinding.ItemHomeBestBakeryBinding +import com.sopt.geonppang.domain.model.BestBakery +import com.sopt.geonppang.util.ItemDiffCallback + +class BestBakeryAdapter : ListAdapter( + ItemDiffCallback( + onItemsTheSame = { old, new -> old.bakeryId == new.bakeryId }, + onContentsTheSame = { old, new -> old == new } + ) +) { + + class BakeryViewHolder( + private val binding: ItemHomeBestBakeryBinding, + ) : RecyclerView.ViewHolder(binding.root) { + fun onBind(bakery: BestBakery) { + binding.bakery = bakery + binding.executePendingBindings() + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BakeryViewHolder { + val binding = + ItemHomeBestBakeryBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BakeryViewHolder(binding) + } + + override fun onBindViewHolder(holder: BakeryViewHolder, position: Int) { + holder.onBind(getItem(position)) + } +} diff --git a/app/src/main/java/com/sopt/geonppang/presentation/home/BestReviewAdapter.kt b/app/src/main/java/com/sopt/geonppang/presentation/home/BestReviewAdapter.kt new file mode 100644 index 00000000..c6ff5bef --- /dev/null +++ b/app/src/main/java/com/sopt/geonppang/presentation/home/BestReviewAdapter.kt @@ -0,0 +1,36 @@ +package com.sopt.geonppang.presentation.home + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.sopt.geonppang.databinding.ItemHomeBestReviewBinding +import com.sopt.geonppang.domain.model.BestReview +import com.sopt.geonppang.util.ItemDiffCallback + +class BestReviewAdapter : ListAdapter( + ItemDiffCallback( + onItemsTheSame = { old, new -> old.bakeryId == new.bakeryId }, + onContentsTheSame = { old, new -> old == new } + ) +) { + + class ReviewViewHolder( + private val binding: ItemHomeBestReviewBinding, + ) : RecyclerView.ViewHolder(binding.root) { + fun onBind(review: BestReview) { + binding.review = review + binding.executePendingBindings() + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReviewViewHolder { + val binding = + ItemHomeBestReviewBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ReviewViewHolder(binding) + } + + override fun onBindViewHolder(holder: ReviewViewHolder, position: Int) { + holder.onBind(getItem(position)) + } +} diff --git a/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt new file mode 100644 index 00000000..b482e077 --- /dev/null +++ b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeFragment.kt @@ -0,0 +1,31 @@ +package com.sopt.geonppang.presentation.home + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import com.sopt.geonppang.R +import com.sopt.geonppang.databinding.FragmentHomeBinding +import com.sopt.geonppang.util.binding.BindingFragment + +class HomeFragment : BindingFragment(R.layout.fragment_home) { + private val viewModel: HomeViewModel by viewModels() + + lateinit var bestBakeryAdapter: BestBakeryAdapter + lateinit var bestReviewAdapter: BestReviewAdapter + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + initLayout() + } + + private fun initLayout() { + bestBakeryAdapter = BestBakeryAdapter() + binding.rvHomeBestBakeryList.adapter = bestBakeryAdapter + bestBakeryAdapter.submitList(viewModel.mockBestBakeryList) + + bestReviewAdapter = BestReviewAdapter() + binding.rvHomeBestReviewList.adapter = bestReviewAdapter + bestReviewAdapter.submitList(viewModel.mockBestReviewList) + } +} diff --git a/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt new file mode 100644 index 00000000..474e1875 --- /dev/null +++ b/app/src/main/java/com/sopt/geonppang/presentation/home/HomeViewModel.kt @@ -0,0 +1,110 @@ +package com.sopt.geonppang.presentation.home + +import androidx.lifecycle.ViewModel +import com.sopt.geonppang.R +import com.sopt.geonppang.domain.model.BestBakery +import com.sopt.geonppang.domain.model.BestReview + +class HomeViewModel : ViewModel() { + val mockBestBakeryList = listOf( + BestBakery( + bakeryId = 1, + bakeryName = "건대 비건빵아아아아아아", + firstNearStation = "건대역", + secondNearStation = null, + isBooked = true, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 5, + isHACCP = true, + isVegan = true, + isNonGMO = true + ), + BestBakery( + bakeryId = 2, + bakeryName = "건대 비건빵아아아아아아", + firstNearStation = "덕소역", + secondNearStation = "구리역", + isBooked = false, + bookmarkCount = 0, + bakeryImage = R.drawable.bread1, + reviewCount = 5, + isHACCP = false, + isVegan = true, + isNonGMO = true + ), + BestBakery( + bakeryId = 3, + bakeryName = "건대 비건빵아아아아아아", + firstNearStation = "양정역", + secondNearStation = "하암역", + isBooked = false, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 5, + isHACCP = false, + isVegan = false, + isNonGMO = true + ), + BestBakery( + bakeryId = 4, + bakeryName = "건대 비건빵아아아아아아", + firstNearStation = "양원역", + secondNearStation = "후앙역", + isBooked = true, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 5, + isHACCP = true, + isVegan = false, + isNonGMO = true + ), + ) + + val mockBestReviewList = listOf( + BestReview( + bakeryId = 1, + bakeryName = "건대 비건빵아아아아아아", + isBooked = true, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 4, + reviewText = "정말 너무 맛있었어요! 갓 구운빵이 예술이었던 것 같아요오오오오오", + firstReviewChip = "친절해요", + secondReviewChip = "제로웨이스트" + ), + BestReview( + bakeryId = 2, + bakeryName = "건대 비건빵아아아아아아", + isBooked = true, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 4, + reviewText = "정말 너무 맛있었어요! 갓 구운빵이 예술이었던 것 같아요오오오오오", + firstReviewChip = "친절해요", + secondReviewChip = "제로웨이스트" + ), + BestReview( + bakeryId = 3, + bakeryName = "건대 비건빵아아아아아아", + isBooked = true, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 4, + reviewText = "정말 너무 맛있었어요! 갓 구운빵이 예술이었던 것 같아요오오오오오", + firstReviewChip = "친절해요", + secondReviewChip = "제로웨이스트" + ), + BestReview( + bakeryId = 4, + bakeryName = "건대 비건빵아아아아아아", + isBooked = true, + bookmarkCount = 5, + bakeryImage = R.drawable.bread1, + reviewCount = 4, + reviewText = "정말 너무 맛있었어요! 갓 구운빵이 예술이었던 것 같아요오오오오오", + firstReviewChip = "친절해요", + secondReviewChip = "제로웨이스트" + ), + ) +} diff --git a/app/src/main/java/com/sopt/geonppang/util/binding/BindingAdapter.kt b/app/src/main/java/com/sopt/geonppang/util/binding/BindingAdapter.kt index e4476a34..9b280660 100644 --- a/app/src/main/java/com/sopt/geonppang/util/binding/BindingAdapter.kt +++ b/app/src/main/java/com/sopt/geonppang/util/binding/BindingAdapter.kt @@ -1,9 +1,15 @@ package com.sopt.geonppang.util +import android.text.Spannable +import android.text.SpannableString +import android.text.style.ForegroundColorSpan import android.view.View import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.databinding.BindingAdapter import coil.load +import com.sopt.geonppang.R @BindingAdapter("image") fun ImageView.setImage(imageUrl: String) { @@ -15,3 +21,25 @@ fun View.setVisibility(isVisible: Boolean?) { if (isVisible == null) return this.visibility = if (isVisible) View.VISIBLE else View.GONE } + +@BindingAdapter("highlightNumbers") +fun TextView.highlightNumbers(text: CharSequence?) { + if (text.isNullOrEmpty()) { + this.text = text + return + } + + val spannableString = SpannableString.valueOf(text) + val numberPattern = "\\d+".toRegex() + + val color = ContextCompat.getColor(context, R.color.point_1) + val span = ForegroundColorSpan(color) + + numberPattern.findAll(text).forEach { matchResult -> + val startIndex = matchResult.range.start + val endIndex = matchResult.range.endInclusive + 1 + spannableString.setSpan(span, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + this.text = spannableString +} diff --git a/app/src/main/res/drawable/background_home_bakery_box.xml b/app/src/main/res/drawable/background_home_bakery_box.xml new file mode 100644 index 00000000..fdae5247 --- /dev/null +++ b/app/src/main/res/drawable/background_home_bakery_box.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_search_box.xml b/app/src/main/res/drawable/background_search_box.xml new file mode 100644 index 00000000..6ef081b5 --- /dev/null +++ b/app/src/main/res/drawable/background_search_box.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bread1.png b/app/src/main/res/drawable/bread1.png new file mode 100644 index 00000000..633025a0 Binary files /dev/null and b/app/src/main/res/drawable/bread1.png differ diff --git a/app/src/main/res/drawable/foreground_shadow.xml b/app/src/main/res/drawable/foreground_shadow.xml new file mode 100644 index 00000000..ed44f7f8 --- /dev/null +++ b/app/src/main/res/drawable/foreground_shadow.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_bookmark_selected.xml b/app/src/main/res/drawable/ic_bookmark_selected.xml new file mode 100644 index 00000000..e032c95f --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark_selected.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_bookmark_unselected.xml b/app/src/main/res/drawable/ic_bookmark_unselected.xml new file mode 100644 index 00000000..17b966a0 --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark_unselected.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_gmo_mark.xml b/app/src/main/res/drawable/ic_gmo_mark.xml new file mode 100644 index 00000000..da58264f --- /dev/null +++ b/app/src/main/res/drawable/ic_gmo_mark.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_haccp_mark.png b/app/src/main/res/drawable/ic_haccp_mark.png new file mode 100644 index 00000000..c8de07d8 Binary files /dev/null and b/app/src/main/res/drawable/ic_haccp_mark.png differ diff --git a/app/src/main/res/drawable/ic_home_filter.xml b/app/src/main/res/drawable/ic_home_filter.xml new file mode 100644 index 00000000..9e04221f --- /dev/null +++ b/app/src/main/res/drawable/ic_home_filter.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 00000000..54348bd9 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_vegan_mark.xml b/app/src/main/res/drawable/ic_vegan_mark.xml new file mode 100644 index 00000000..1e4435e1 --- /dev/null +++ b/app/src/main/res/drawable/ic_vegan_mark.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 00000000..cd1e8fb7 --- /dev/null +++ b/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_best_bakery.xml b/app/src/main/res/layout/item_home_best_bakery.xml new file mode 100644 index 00000000..fba772cf --- /dev/null +++ b/app/src/main/res/layout/item_home_best_bakery.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_home_best_review.xml b/app/src/main/res/layout/item_home_best_review.xml new file mode 100644 index 00000000..657423d1 --- /dev/null +++ b/app/src/main/res/layout/item_home_best_review.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7ab5d5f7..45b08528 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,15 @@ GeonPpang + + + 바이블님 맞춤\t + BEST 건빵집 + 궁금한 건빵집을 입력해주세요! + BEST 리뷰 + 건빵은 건강한 빵집의 위치와 성분 정보를 제공하여 소비자의 선택을 돕는 용도의 서비스입니다. 건빵의 모든 정보는 제조사에서 제공한 정보입니다. 이는 소비자의 구매를 돕기 위한 참고 사항이며, 제공 정보의 오류에 대한 책임을 지지 않습니다. + 리뷰(%d) + %d명 + 마이페이지 글루텐프리