Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.widget.ImageView
import coil.load
import coil.transform.CircleCropTransformation

fun ImageView.loadByCircle(imageUrl: String) {
fun ImageView.loadCircularImage(imageUrl: String) {
this.load(imageUrl) {
crossfade(true)
transformations(CircleCropTransformation())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.on.turip.R
import com.on.turip.databinding.ItemSearchResultBinding
import com.on.turip.domain.contents.VideoInformation
import com.on.turip.ui.common.loadByCircle
import com.on.turip.ui.common.loadCircularImage

class SearchResultViewHolder(
private val binding: ItemSearchResultBinding,
Expand Down Expand Up @@ -37,7 +37,7 @@ class SearchResultViewHolder(
videoInformation.content.creator.channelName,
videoInformation.content.uploadedDate,
)
binding.ivSearchResultThumbnail.loadByCircle(videoInformation.content.creator.profileImage)
binding.ivSearchResultThumbnail.loadCircularImage(videoInformation.content.creator.profileImage)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.on.turip.ui.travel.detail

data class DayModel(
val day: Int,
val isSelected: Boolean,
)
val isSelected: Boolean = false,
) {
fun isSame(dayModel: DayModel): Boolean = this == dayModel
}

fun Int.initDayModels(): List<DayModel> = (1..this).map { it.initDayModel() }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.on.turip.ui.travel.detail

import android.net.Uri
import androidx.core.net.toUri

data class PlaceModel(
val name: String,
val category: String,
val mapLink: String,
) {
val placeUri: Uri
get() = mapLink.toUri()
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@ class TravelDetailActivity : BaseActivity<TravelDetailViewModel, ActivitySearchD
)
}

private val travelDayAdapter: TravelDayAdapter =
private val travelDayAdapter by lazy {
TravelDayAdapter { dayModel ->
viewModel.selectDay(dayModel)
viewModel.updateDay(dayModel)
}
}

private val travelPlaceAdapter by lazy {
TravelPlaceAdapter { placeModel ->
val intent = Intent(Intent.ACTION_VIEW, placeModel.placeUri)
startActivity(intent)
}
}

private fun enableFullscreen() {
WindowCompat.setDecorFitsSystemWindows(this.window, false)
Expand Down Expand Up @@ -67,6 +75,7 @@ class TravelDetailActivity : BaseActivity<TravelDetailViewModel, ActivitySearchD
super.onCreate(savedInstanceState)

setupToolbar()
setupOnBackPressedDispatcher()
setupWebView()
setupAdapters()
setupListeners()
Expand All @@ -75,9 +84,13 @@ class TravelDetailActivity : BaseActivity<TravelDetailViewModel, ActivitySearchD

private fun setupToolbar() {
setSupportActionBar(binding.tbSearchDetail)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = null
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
title = null
}
}

private fun setupOnBackPressedDispatcher() {
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
Expand Down Expand Up @@ -117,6 +130,10 @@ class TravelDetailActivity : BaseActivity<TravelDetailViewModel, ActivitySearchD

private fun setupAdapters() {
binding.rvSearchDetailTravelDay.adapter = travelDayAdapter
binding.rvSearchDetailTravelPlace.apply {
adapter = travelPlaceAdapter
itemAnimator = null
}
}

private fun setupListeners() {
Expand All @@ -134,6 +151,9 @@ class TravelDetailActivity : BaseActivity<TravelDetailViewModel, ActivitySearchD
viewModel.days.observe(this) { dayModels ->
travelDayAdapter.submitList(dayModels)
}
viewModel.places.observe(this) { placeModels ->
travelPlaceAdapter.submitList(placeModels)
}
}

override fun onDestroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,32 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class TravelDetailViewModel : ViewModel() {
private val _days: MutableLiveData<List<DayModel>> = MutableLiveData()
val days: LiveData<List<DayModel>> = _days
private val _days: MutableLiveData<List<DayModel>> = MutableLiveData(emptyList())
val days: LiveData<List<DayModel>> get() = _days

private val _places: MutableLiveData<List<PlaceModel>> = MutableLiveData(emptyList())
val places: LiveData<List<PlaceModel>> get() = _places

private var placeCacheByDay: Map<DayModel, List<PlaceModel>>

init {
val loadDay: Int = 10 // TODO: 서버에서 받아온 여행일정 X일을 추출해서 추가
val loadDay: Int = 3 // TODO: 서버에서 받아온 여행일정 X일을 추출해서 추가
_days.value = loadDay.initDayModels()
placeCacheByDay = emptyMap() // TODO: 서버에서 받아온 데이터로 key:일차, value: 장소들로 map 자료구조로 캐싱
_places.value = placeCacheByDay[DayModel(1)]
}

fun selectDay(selectedDay: DayModel) {
val currentList = days.value ?: return
val newList =
currentList.map { item ->
if (item.day == selectedDay.day) {
item.copy(isSelected = true)
fun updateDay(day: DayModel) {
val daysStatus: List<DayModel> = days.value ?: return // TODO: days.value null 일 때 로직 처리 필요
val updateDaysStatus =
daysStatus.map { dayModel ->
if (dayModel.isSame(day)) {
dayModel.copy(isSelected = true)
} else {
item.copy(isSelected = false)
dayModel.copy(isSelected = false)
}
}
_days.value = newList
_days.value = updateDaysStatus
_places.value = placeCacheByDay[day]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.on.turip.ui.travel.detail

import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter

class TravelPlaceAdapter(
private val onClickListener: TravelPlaceViewHolder.OnPlaceListener,
) : ListAdapter<PlaceModel, TravelPlaceViewHolder>(TravelPlaceDiffUtil) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): TravelPlaceViewHolder = TravelPlaceViewHolder.of(parent, onClickListener)

override fun onBindViewHolder(
holder: TravelPlaceViewHolder,
position: Int,
) {
val placeModel: PlaceModel = getItem(position)
holder.bind(placeModel)
}

private object TravelPlaceDiffUtil : DiffUtil.ItemCallback<PlaceModel>() {
override fun areItemsTheSame(
oldItem: PlaceModel,
newItem: PlaceModel,
): Boolean = oldItem.name == newItem.name

override fun areContentsTheSame(
oldItem: PlaceModel,
newItem: PlaceModel,
): Boolean = oldItem == newItem
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.on.turip.ui.travel.detail

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.on.turip.databinding.ItemTravelPlaceBinding

class TravelPlaceViewHolder(
private val binding: ItemTravelPlaceBinding,
onClickListener: OnPlaceListener,
) : RecyclerView.ViewHolder(binding.root) {
private var placeModel: PlaceModel? = null

init {
binding.ivTravelPlaceLink.setOnClickListener {
placeModel?.let {
onClickListener.onPlaceClick(it)
}
}
}

fun bind(placeModel: PlaceModel) {
this.placeModel = placeModel
binding.tvTravelPlaceName.text = placeModel.name
binding.tvTravelPlaceCategory.text = placeModel.category
}

companion object {
fun of(
parent: ViewGroup,
onClickListener: OnPlaceListener,
): TravelPlaceViewHolder {
val inflater: LayoutInflater = LayoutInflater.from(parent.context)
val binding: ItemTravelPlaceBinding =
ItemTravelPlaceBinding.inflate(inflater, parent, false)
return TravelPlaceViewHolder(binding, onClickListener)
}
}

fun interface OnPlaceListener {
fun onPlaceClick(placeModel: PlaceModel)
}
}
12 changes: 12 additions & 0 deletions android/app/src/main/res/layout/activity_search_detail.xml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,18 @@
app:layout_constraintTop_toBottomOf="@id/rv_search_detail_travel_day"
tools:text="일정 4개" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_search_detail_travel_place"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="12dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_search_detail_day_place_count"
tools:listitem="@layout/item_travel_place" />

</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
57 changes: 57 additions & 0 deletions android/app/src/main/res/layout/item_travel_place.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl_travel_place"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginBottom="12dp"
android:background="@drawable/bg_stroke_gray200_radius_24dp"
android:backgroundTint="@color/gray_300_5b5b5b">

<TextView
android:id="@+id/tv_travel_place_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="28dp"
android:layout_marginTop="20dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/title1"
android:textColor="@color/pure_white_ffffff"
app:layout_constraintEnd_toStartOf="@id/iv_travel_place_link"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="경포대 해수욕장경포대 해수욕장경포대 해수욕장경포대 해수욕장" />

<TextView
android:id="@+id/tv_travel_place_category"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:textAppearance="@style/title3"
android:textColor="@color/pure_white_ffffff"
app:layout_constraintStart_toStartOf="@id/tv_travel_place_name"
app:layout_constraintTop_toBottomOf="@id/tv_travel_place_name"
tools:text="관광지" />

<ImageView
android:id="@+id/iv_travel_place_link"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:contentDescription="@null"
android:padding="15dp"
android:src="@drawable/btn_map_link"
app:layout_constraintBottom_toBottomOf="@id/tv_travel_place_category"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_travel_place_name" />

<View
android:layout_width="match_parent"
android:layout_height="23dp"
app:layout_constraintStart_toStartOf="@id/tv_travel_place_category"
app:layout_constraintTop_toBottomOf="@id/tv_travel_place_category" />

</androidx.constraintlayout.widget.ConstraintLayout>