-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ui: 여행 화면 구현 #51 #75
ui: 여행 화면 구현 #51 #75
Changes from all commits
4211001
449d896
9eaf1eb
780838f
7fffcd6
61d887b
c64e9ec
34ef21b
ca01dd4
88c5bf0
c743c6d
8c107b7
a78bc55
e2d0cff
a11c5fe
c280d22
015a56d
7d7ce22
4e36d02
a2c4410
3203aea
236b1f6
06d7e3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.woowacourse.staccato.presentation.common | ||
|
||
/** | ||
* Used as a wrapper for data that is exposed via a LiveData that represents an event. | ||
*/ | ||
open class Event<out T>(private val content: T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Event와 SingleLiveData 관련 클래스 추가해주셔서 감사합니다ㅎㅎ 쇽 샥 ~ |
||
var hasBeenHandled = false | ||
private set // Allow external read but not write | ||
|
||
/** | ||
* Returns the content and prevents its use again. | ||
*/ | ||
fun getContentIfNotHandled(): T? { | ||
return if (hasBeenHandled) { | ||
null | ||
} else { | ||
hasBeenHandled = true | ||
content | ||
} | ||
} | ||
|
||
/** | ||
* Returns the content, even if it's already been handled. | ||
*/ | ||
fun peekContent(): T = content | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.woowacourse.staccato.presentation.common | ||
|
||
class MutableSingleLiveData<T> : SingleLiveData<T> { | ||
constructor() : super() | ||
|
||
constructor(value: T) : super(value) | ||
|
||
public override fun postValue(value: T) { | ||
super.postValue(value) | ||
} | ||
|
||
public override fun setValue(value: T) { | ||
super.setValue(value) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.woowacourse.staccato.presentation.common | ||
|
||
import androidx.lifecycle.LifecycleOwner | ||
import androidx.lifecycle.MutableLiveData | ||
|
||
abstract class SingleLiveData<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 나중에 해당 |
||
private val liveData = MutableLiveData<Event<T>>() | ||
|
||
protected constructor() | ||
|
||
protected constructor(value: T) { | ||
liveData.value = Event(value) | ||
} | ||
|
||
protected open fun setValue(value: T) { | ||
liveData.value = Event(value) | ||
} | ||
|
||
protected open fun postValue(value: T) { | ||
liveData.postValue(Event(value)) | ||
} | ||
|
||
fun getValue() = liveData.value?.peekContent() | ||
|
||
fun observe( | ||
owner: LifecycleOwner, | ||
onResult: (T) -> Unit, | ||
) { | ||
liveData.observe(owner) { it.getContentIfNotHandled()?.let(onResult) } | ||
} | ||
|
||
fun observePeek( | ||
owner: LifecycleOwner, | ||
onResult: (T) -> Unit, | ||
) { | ||
liveData.observe(owner) { onResult(it.peekContent()) } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,99 @@ | ||
package com.woowacourse.staccato.presentation.travel | ||
|
||
import android.app.ProgressDialog.show | ||
import android.os.Bundle | ||
import android.view.View | ||
import androidx.core.os.bundleOf | ||
import androidx.fragment.app.viewModels | ||
import androidx.navigation.fragment.findNavController | ||
import com.woowacourse.staccato.DeleteDialogFragment | ||
import com.woowacourse.staccato.R | ||
import com.woowacourse.staccato.databinding.FragmentTravelBinding | ||
import com.woowacourse.staccato.presentation.ToolbarHandler | ||
import com.woowacourse.staccato.presentation.base.BindingFragment | ||
import com.woowacourse.staccato.presentation.main.MainActivity | ||
import com.woowacourse.staccato.presentation.travel.adapter.MatesAdapter | ||
import com.woowacourse.staccato.presentation.travel.adapter.VisitsAdapter | ||
import com.woowacourse.staccato.presentation.travel.viewmodel.TravelViewModel | ||
import com.woowacourse.staccato.presentation.travel.viewmodel.TravelViewModelFactory | ||
import com.woowacourse.staccato.presentation.travelupdate.TravelUpdateActivity | ||
|
||
class TravelFragment : BindingFragment<FragmentTravelBinding>(R.layout.fragment_travel) { | ||
class TravelFragment : | ||
BindingFragment<FragmentTravelBinding>(R.layout.fragment_travel), | ||
ToolbarHandler { | ||
private val viewModel: TravelViewModel by viewModels { | ||
TravelViewModelFactory() | ||
} | ||
private val deleteDialog = DeleteDialogFragment { findNavController().popBackStack() } | ||
|
||
private lateinit var matesAdapter: MatesAdapter | ||
private lateinit var visitsAdapter: VisitsAdapter | ||
|
||
override fun onViewCreated( | ||
view: View, | ||
savedInstanceState: Bundle?, | ||
) { | ||
binding.btnTravelUpdate.setOnClickListener { | ||
val travelUpdateLauncher = (activity as MainActivity).travelUpdateLauncher | ||
TravelUpdateActivity.startWithResultLauncher( | ||
this.requireActivity(), | ||
travelUpdateLauncher, | ||
) | ||
// findNavController().navigate(R.id.action_travelFragment_to_travelUpdateFragment) | ||
initBinding() | ||
initToolbar() | ||
initMatesAdapter() | ||
initVisitsAdapter() | ||
observeTravel() | ||
navigateToVisit() | ||
} | ||
|
||
private fun initBinding() { | ||
binding.lifecycleOwner = this | ||
binding.viewModel = viewModel | ||
binding.toolbarHandler = this | ||
} | ||
|
||
private fun initToolbar() { | ||
binding.includeTravelToolbar.toolbarDetail.setNavigationOnClickListener { | ||
findNavController().popBackStack() | ||
} | ||
binding.btnVisit.setOnClickListener { | ||
findNavController().navigate(R.id.action_travelFragment_to_visitFragment) | ||
} | ||
|
||
private fun observeTravel() { | ||
viewModel.loadTravel() | ||
viewModel.travel.observe(viewLifecycleOwner) { travel -> | ||
matesAdapter.updateMates(travel.mates) | ||
visitsAdapter.updateVisits(travel.visits) | ||
} | ||
} | ||
|
||
private fun initMatesAdapter() { | ||
matesAdapter = MatesAdapter() | ||
binding.rvTravelMates.adapter = matesAdapter | ||
} | ||
|
||
private fun initVisitsAdapter() { | ||
visitsAdapter = VisitsAdapter(handler = viewModel) | ||
binding.rvTravelVisits.adapter = visitsAdapter | ||
} | ||
|
||
private fun navigateToVisit() { | ||
viewModel.visitId.observe(viewLifecycleOwner) { visitId -> | ||
val bundle = bundleOf(VISIT_ID_KEY to visitId) | ||
findNavController().navigate(R.id.action_travelFragment_to_visitFragment, bundle) | ||
} | ||
} | ||
|
||
override fun onUpdateClicked() { | ||
val travelUpdateLauncher = (activity as MainActivity).travelUpdateLauncher | ||
TravelUpdateActivity.startWithResultLauncher( | ||
requireActivity(), | ||
travelUpdateLauncher, | ||
) | ||
} | ||
|
||
override fun onDeleteClicked() { | ||
val fragmentManager = parentFragmentManager | ||
deleteDialog.apply { | ||
show(fragmentManager, DeleteDialogFragment.TAG) | ||
} | ||
} | ||
|
||
companion object { | ||
const val VISIT_ID_KEY = "visitId" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.woowacourse.staccato.presentation.travel | ||
|
||
interface TravelHandler { | ||
fun onVisitClicked(visitId: Long) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.woowacourse.staccato.presentation.travel.adapter | ||
|
||
import android.view.LayoutInflater | ||
import android.view.ViewGroup | ||
import androidx.recyclerview.widget.DiffUtil | ||
import androidx.recyclerview.widget.ListAdapter | ||
import com.woowacourse.staccato.databinding.ItemMatesBinding | ||
import com.woowacourse.staccato.presentation.travel.model.MateUiModel | ||
|
||
class MatesAdapter : ListAdapter<MateUiModel, MatesViewHolder>(diffUtil) { | ||
override fun onCreateViewHolder( | ||
parent: ViewGroup, | ||
viewType: Int, | ||
): MatesViewHolder { | ||
val inflater = LayoutInflater.from(parent.context) | ||
val binding = ItemMatesBinding.inflate(inflater, parent, false) | ||
return MatesViewHolder(binding) | ||
} | ||
|
||
override fun onBindViewHolder( | ||
holder: MatesViewHolder, | ||
position: Int, | ||
) { | ||
holder.bind(getItem(position)) | ||
} | ||
|
||
fun updateMates(mates: List<MateUiModel>) { | ||
submitList(mates) | ||
} | ||
|
||
companion object { | ||
val diffUtil = | ||
object : DiffUtil.ItemCallback<MateUiModel>() { | ||
override fun areItemsTheSame( | ||
oldItem: MateUiModel, | ||
newItem: MateUiModel, | ||
): Boolean = oldItem.id == newItem.id | ||
|
||
override fun areContentsTheSame( | ||
oldItem: MateUiModel, | ||
newItem: MateUiModel, | ||
): Boolean = oldItem == newItem | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.woowacourse.staccato.presentation.travel.adapter | ||
|
||
import androidx.recyclerview.widget.RecyclerView.ViewHolder | ||
import com.woowacourse.staccato.databinding.ItemMatesBinding | ||
import com.woowacourse.staccato.presentation.travel.model.MateUiModel | ||
|
||
class MatesViewHolder( | ||
private val binding: ItemMatesBinding, | ||
) : ViewHolder(binding.root) { | ||
fun bind(mate: MateUiModel) { | ||
binding.mate = mate | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
바인딩 어댑터의 어노테이션 속성과 확장 함수를 잘 활용하여 작성해주신 것 같아요!