Skip to content

Commit

Permalink
Merge pull request #139 from depromeet/feature/#138-card-back-add-detail
Browse files Browse the repository at this point in the history
[feat/#138] 카드 뒷면 관심사 삭제/추가 디테일 수정
  • Loading branch information
unam98 authored Feb 12, 2024
2 parents 92e7578 + 0c5a36e commit bda5f80
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,6 @@ fun BackCardView(backCard: BackCard) {
factory = { context ->
BackCardView(context).apply {
getInstance(backCard)
submitInterestList( //todo - 더미
listOf(
Interest("모여서 각자 일하기"),
Interest("사이드 프로젝트"),
Interest("네트워킹")
)
)
setIsModifyDetail(isModifyDetail = true)
isModify = true
rotationY = 180f
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.teumteum.teumteum.util.callback

interface OnCurrentListChangedListener<T> {
fun onCurrentListChanged(previousList: List<T>, currentList: List<T>)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,30 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.FlexboxLayoutManager
import com.teumteum.teumteum.util.extension.dpToPx

class FlexboxItemDecoration(private val context: Context, private val horizontalSpaceDp: Int, private val verticalSpaceDp: Int) : RecyclerView.ItemDecoration() {
class FlexboxItemDecoration(private val context: Context, private val leftSpaceDp: Int, private val topSpaceDp: Int) : RecyclerView.ItemDecoration() {

private val horizontalSpacePx = horizontalSpaceDp.dpToPx(context)
private val verticalSpacePx = verticalSpaceDp.dpToPx(context)
private val leftSpacePx = leftSpaceDp.dpToPx(context)
private val topSpacePx = topSpaceDp.dpToPx(context)

override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val layoutManager = parent.layoutManager as? FlexboxLayoutManager
val position = parent.getChildAdapterPosition(view)

layoutManager?.let {
val itemCount = parent.adapter?.itemCount ?: 0
val isLastRowItem = position >= itemCount - it.flexWrap
val isFirstInRow = position % it.flexWrap == 0

// 왼쪽에 아무것도 없는 첫 번째 아이템은 왼쪽 마진을 0으로 설정
if (isFirstInRow) {
outRect.left = 0
} else {
outRect.left = horizontalSpacePx
}

// 마지막 줄의 아이템은 아래 마진을 0으로 설정
if (isLastRowItem) {
outRect.bottom = 0
} else {
outRect.bottom = verticalSpacePx
outRect.left = leftSpacePx
}

// 상단 마진은 항상 동일하게 적용
outRect.top = verticalSpacePx
outRect.top = topSpacePx

// 오른쪽 마진은 항상 동일하게 적용
outRect.right = horizontalSpacePx
outRect.right = leftSpacePx
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,32 @@ import android.util.TypedValue
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexWrap
import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import com.teumteum.teumteum.R
import com.teumteum.teumteum.util.callback.OnCurrentListChangedListener
import com.teumteum.teumteum.util.custom.itemdecoration.FlexboxItemDecoration
import com.teumteum.teumteum.util.custom.view.adapter.InterestAdapter
import com.teumteum.teumteum.util.custom.view.model.BackCard
import com.teumteum.teumteum.util.custom.view.model.Interest
import com.teumteum.teumteum.util.extension.dpToPx
import timber.log.Timber

/**
* 카드 후면 뷰
*
* xml, compose 모든 환경에서 뷰를 재활용 할 수 있게 커스텀뷰로 제작
*/
class BackCardView : CardView {
class BackCardView : CardView, OnCurrentListChangedListener<Interest> {
private val layoutParent = ConstraintLayout.LayoutParams.PARENT_ID
private var backCard = BackCard()

Expand All @@ -46,21 +50,44 @@ class BackCardView : CardView {
ivFloat.visibility = if (value) View.VISIBLE else View.INVISIBLE
}

var isModifyDetail: Boolean = false
set(value) {
field = value
ivEditGoalContent.visibility = if (value) View.VISIBLE else View.INVISIBLE
}

var currentList = MutableLiveData<MutableList<Interest>>()

// isModifyDetail 값을 설정하고 어댑터에 UI 갱신을 알리는 함수
@SuppressLint("NotifyDataSetChanged")
fun setIsModifyDetail(isModifyDetail: Boolean) {
this.isModifyDetail = isModifyDetail //todo - 하나의 변수를 어댑터 안팎으로 2개씩 나눠 다루고 있는데 추후 하나로 통일
interestAdapter.isModifyDetail = isModifyDetail
// UI를 새로고침하도록 어댑터에 알림
interestAdapter.notifyDataSetChanged()
}

// 공개 속성으로 RecyclerView와 Adapter 제공
val interestAdapter = InterestAdapter()
val interestAdapter = InterestAdapter(context, this)
lateinit var rvInterests: RecyclerView
private set

fun submitInterestList(interests: List<Interest>) {
interestAdapter.submitList(interests.reversed()) //flexboxLayout에서 item 쌓이는 순서 reverse 지원을 안 해서 직접 item 순서를 뒤집어서 submitList
val currentList = interestAdapter.currentList.toMutableList()

if (!currentList.any { it.interest == "추가하기" } && isModifyDetail) {
currentList.add(Interest("추가하기"))
}

val availableSlots = 4 - currentList.size

if (interests.size > availableSlots) {
Toast.makeText(context, "최대 3개까지 선택할 수 있어요", Toast.LENGTH_SHORT).show()
} else {
val interestsToAdd = interests.take(availableSlots)
currentList.addAll(0, interestsToAdd)

interestAdapter.submitList(currentList.reversed())
}
}

constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
Expand Down Expand Up @@ -206,8 +233,8 @@ class BackCardView : CardView {
marginBottom = 32,
marginStart = 32,
marginEnd = 32,
itemHorizontalSpaceDp = 8,
itemVerticalSpaceDp = 4,
itemLeftSpaceDp = 8,
itemTopSpaceDp = 8,
)
}

Expand Down Expand Up @@ -344,8 +371,8 @@ class BackCardView : CardView {
endToEndOf: Int? = null,
endToStartOf: Int? = null,
background: Int? = null,
itemHorizontalSpaceDp: Int,
itemVerticalSpaceDp: Int,
itemLeftSpaceDp: Int,
itemTopSpaceDp: Int,
) {
rvInterests = RecyclerView(context).apply {
this.id = id
Expand All @@ -362,8 +389,8 @@ class BackCardView : CardView {
addItemDecoration(
FlexboxItemDecoration(
context,
itemHorizontalSpaceDp,
itemVerticalSpaceDp
itemLeftSpaceDp,
itemTopSpaceDp
)
)
background?.let { setBackgroundResource(it) }
Expand Down Expand Up @@ -395,4 +422,11 @@ class BackCardView : CardView {
rvInterests.layoutParams = layoutParams
this.addView(rvInterests)
}

override fun onCurrentListChanged(previousList: List<Interest>, currentList: List<Interest>) {
Timber.tag("갱신 리스트 p").d("${previousList}")
Timber.tag("갱신 리스트 c").d("${currentList}")
this.currentList.value = currentList.filterNot { it.interest == "추가하기" }.toMutableList()
Timber.tag("currentList").d("${this.currentList.value}")
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,51 @@
package com.teumteum.teumteum.util.custom.view.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.teumteum.teumteum.R
import com.teumteum.teumteum.databinding.ItemInterestBinding
import com.teumteum.teumteum.util.callback.OnCurrentListChangedListener
import com.teumteum.teumteum.util.custom.view.model.Interest
import timber.log.Timber

class InterestAdapter() : ListAdapter<Interest, InterestAdapter.ItemViewHolder>(
class InterestAdapter(
val context: Context,
var onCurrentListChangedListener: OnCurrentListChangedListener<Interest>,
) : ListAdapter<Interest, InterestAdapter.ItemViewHolder>(
ItemListDiffCallback
) {
var isModifyDetail: Boolean = false // 외부에서 접근 가능하도록 isModifyDetail 속성 추가
var isModifyDetail: Boolean = false
var onAddItemClick: (() -> Unit)? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val binding = ItemInterestBinding.inflate(LayoutInflater.from(parent.context), parent, false)
// onCreateViewHolder 시점에 isModifyDetail 값을 ItemViewHolder에 전달
return ItemViewHolder(binding, isModifyDetail)
val binding =
ItemInterestBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ItemViewHolder(binding, isModifyDetail, onAddItemClick) { position ->
if (currentList.size > 2) {
removeItem(position)
} else {
Toast.makeText(context, "1개 이하는 삭제가 불가능 해요", Toast.LENGTH_SHORT).show()
}
}
}

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(getItem(position))
holder.binding.root.setOnClickListener {
removeItem(position)
}
val item = getItem(position)
holder.bind(item)
}

override fun onCurrentListChanged(
previousList: MutableList<Interest>,
currentList: MutableList<Interest>,
) {
super.onCurrentListChanged(previousList, currentList)
onCurrentListChangedListener?.onCurrentListChanged(previousList, currentList)
}

private fun removeItem(position: Int) {
Expand All @@ -36,24 +55,49 @@ class InterestAdapter() : ListAdapter<Interest, InterestAdapter.ItemViewHolder>(
submitList(newList)
}

class ItemViewHolder(val binding: ItemInterestBinding, private val isModifyDetail: Boolean) :
RecyclerView.ViewHolder(binding.root) {
class ItemViewHolder(
private val binding: ItemInterestBinding,
private val isModifyDetail: Boolean,
private val onAddItemClick: (() -> Unit)?,
private val onRemoveItem: (position: Int) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {

fun bind(item: Interest) {
binding.tvInterest.text = itemView.context.getString(R.string.item_interest, item.interest)
// 생성자를 통해 전달받은 isModifyDetail 값을 사용
binding.ivDelete.visibility = if (isModifyDetail) View.VISIBLE else View.GONE
with(binding) {
if (item.interest == "추가하기") {
tvInterest.text = item.interest
tvInterest.setTextColor(
ContextCompat.getColor(
itemView.context,
com.teumteum.base.R.color.text_button_primary_default
)
)
clInterest.setBackgroundResource(R.drawable.shape_rect4_color_outline_level01_active)
ivDelete.setImageDrawable(
ContextCompat.getDrawable(
itemView.context,
R.drawable.ic_plus_fill
)
)
root.setOnClickListener { onAddItemClick?.invoke() }
} else {
tvInterest.text =
itemView.context.getString(R.string.item_interest, item.interest)
clInterest.setBackgroundResource(R.drawable.shape_rect4_background)
root.setOnClickListener(null)
ivDelete.setOnClickListener { onRemoveItem(absoluteAdapterPosition) }

}
ivDelete.visibility = if (isModifyDetail) View.VISIBLE else View.GONE
}
}
}

object ItemListDiffCallback : DiffUtil.ItemCallback<Interest>() {
override fun areItemsTheSame(oldItem: Interest, newItem: Interest): Boolean {
return oldItem == newItem
}
override fun areItemsTheSame(oldItem: Interest, newItem: Interest): Boolean =
oldItem == newItem

override fun areContentsTheSame(
oldItem: Interest, newItem: Interest
): Boolean {
return oldItem.interest == newItem.interest
}
override fun areContentsTheSame(oldItem: Interest, newItem: Interest): Boolean =
oldItem.interest == newItem.interest
}
}
2 changes: 1 addition & 1 deletion app/src/main/res/drawable-night/ic_plus_fill.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
android:viewportHeight="24">
<path
android:pathData="M5.636,18.364C9.151,21.879 14.849,21.879 18.364,18.364C21.879,14.849 21.879,9.151 18.364,5.636C14.849,2.121 9.151,2.121 5.636,5.636C2.121,9.151 2.121,14.849 5.636,18.364ZM11,16.243V13H7.757V11H11L11,7.757H13L13,11H16.243V13H13V16.243H11Z"
android:fillColor="#FFFFFF"
android:fillColor="#C9C9C9"
android:fillType="evenOdd"/>
</vector>
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_plus_fill.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
android:viewportHeight="24">
<path
android:pathData="M5.636,18.364C9.151,21.879 14.849,21.879 18.364,18.364C21.879,14.849 21.879,9.151 18.364,5.636C14.849,2.121 9.151,2.121 5.636,5.636C2.121,9.151 2.121,14.849 5.636,18.364ZM11,16.243V13H7.757V11H11L11,7.757H13L13,11H16.243V13H13V16.243H11Z"
android:fillColor="#C9C9C9"
android:fillColor="#FFFFFF"
android:fillType="evenOdd"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<solid android:color="@color/outline_level01_active" />
<corners android:radius="4dp" />
</shape>
Loading

0 comments on commit bda5f80

Please sign in to comment.