diff --git a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetail.kt b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetail.kt index 19b35c74..3a672ae5 100644 --- a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetail.kt +++ b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetail.kt @@ -2,15 +2,16 @@ package com.depromeet.team6.presentation.ui.itinerary.component import android.util.SparseArray import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -21,6 +22,7 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import com.depromeet.team6.R import com.depromeet.team6.domain.model.RealTimeBusArrival import com.depromeet.team6.domain.model.course.LegInfo @@ -93,30 +95,43 @@ private fun ItineraryInfoSuffix( modifier: Modifier = Modifier ) { val markerIconId = if (isDestination) R.drawable.map_marker_arrival else R.drawable.map_marker_departure + val timelineIconSize = Dimens.LegTimelineIconSize + val markerYOffset = if (isDestination) 0.dp else 2.dp + val timelineTimeIconGap = Dimens.LegTimelineTimeIconGap + val timelineTimeSlotWidth = Dimens.LegTimelineTimeSlotWidth + val timelineColumnWidth = timelineTimeSlotWidth + timelineTimeIconGap + timelineIconSize + Row( - modifier = Modifier + modifier = modifier.zIndex(1f), + verticalAlignment = Alignment.CenterVertically ) { - Column( - modifier = Modifier.width(Dimens.LegDetailVerticalLineWidth), - horizontalAlignment = Alignment.CenterHorizontally + Row( + modifier = Modifier + .width(timelineColumnWidth) + .height(Dimens.LegDetailVerticalLineWidth), + verticalAlignment = Alignment.CenterVertically ) { + Box(modifier = Modifier.width(timelineTimeSlotWidth)) { + BoardingTime( + boardingDateTime = arrivalTime, + modifier = Modifier.align(Alignment.CenterStart) + ) + } + Spacer(modifier = Modifier.width(timelineTimeIconGap)) + Image( - modifier = Modifier.size(36.dp), + modifier = Modifier + .offset(y = markerYOffset) + .size(timelineIconSize), imageVector = ImageVector.vectorResource(markerIconId), contentDescription = "" ) - BoardingTime( - boardingDateTime = arrivalTime, - modifier = Modifier - ) } Spacer( modifier = Modifier.width(6.dp) ) Text( - modifier = Modifier - .height(36.dp) - .wrapContentSize(Alignment.Center), + modifier = Modifier, text = name, style = defaultTeam6Typography.body5_B5SB14, color = defaultTeam6Colors.white diff --git a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetailLegs.kt b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetailLegs.kt index 74db2a20..8f415762 100644 --- a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetailLegs.kt +++ b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItineraryInfoDetailLegs.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -21,6 +22,7 @@ import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.layout.wrapContentWidth @@ -46,11 +48,18 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import com.depromeet.team6.R import com.depromeet.team6.domain.model.BusCongestion import com.depromeet.team6.domain.model.BusStatus @@ -85,6 +94,12 @@ fun ItineraryInfoDetailLegs( modifier: Modifier = Modifier, onClickBusInfo: (BusArrivalParameter) -> Unit = {} ) { + val timelineIconSize = Dimens.LegTimelineIconSize + val timelineTimeIconGap = Dimens.LegTimelineTimeIconGap + val timelineTimeSlotWidth = Dimens.LegTimelineTimeSlotWidth + val timelineColumnMinWidth = timelineTimeSlotWidth + timelineTimeIconGap + timelineIconSize + val timelineAxisOffset = timelineTimeSlotWidth + timelineTimeIconGap + (timelineIconSize / 2) + // Column의 실제 높이(px) var columnHeightPx by remember { mutableIntStateOf(0) } val density = LocalDensity.current @@ -104,12 +119,20 @@ fun ItineraryInfoDetailLegs( for ((idx, leg) in legs.withIndex()) { when (leg.transportType) { TransportType.WALK -> { - val verticalHeight = if (idx == 0 || idx == legs.size - 1) 48.dp else 60.dp + val verticalHeight = walkTimelineHeight( + distanceMeter = leg.distance, + isFirstWalk = idx == 0, + isLastWalk = idx == legs.size - 1 + ) + val walkLineOffsetY = if (idx == 0) (-3).dp else 0.dp DetailLegsWalk( boardingDateTime = leg.departureDateTime!!, timeMinute = leg.sectionTime / 60, distanceMeter = leg.distance, - verticalHeight = verticalHeight + verticalHeight = verticalHeight, + lineOffsetY = walkLineOffsetY, + timelineAxisOffset = timelineAxisOffset, + timelineColumnWidth = timelineColumnMinWidth ) } TransportType.BUS -> { @@ -123,6 +146,10 @@ fun ItineraryInfoDetailLegs( distanceMeter = leg.distance, busArrivalStatus = busArrivalStatus.get(idx), passStopList = leg.passStopList, + timelineIconSize = timelineIconSize, + timelineTimeSlotWidth = timelineTimeSlotWidth, + timelineTimeIconGap = timelineTimeIconGap, + timelineColumnMinWidth = timelineColumnMinWidth, onClickBusInfo = { routeName, stationName, subtypeIdx -> onClickBusInfo( BusArrivalParameter( @@ -146,7 +173,11 @@ fun ItineraryInfoDetailLegs( boardingDateTime = leg.departureDateTime!!, timeMinute = leg.sectionTime / 60, passStopList = leg.passStopList, - distanceMeter = leg.distance + distanceMeter = leg.distance, + timelineIconSize = timelineIconSize, + timelineTimeSlotWidth = timelineTimeSlotWidth, + timelineTimeIconGap = timelineTimeIconGap, + timelineColumnMinWidth = timelineColumnMinWidth ) } } @@ -228,6 +259,10 @@ private fun DetailLegsBus( distanceMeter: Int, busArrivalStatus: RealTimeBusArrival?, passStopList: List, + timelineIconSize: Dp, + timelineTimeSlotWidth: Dp, + timelineTimeIconGap: Dp, + timelineColumnMinWidth: Dp, modifier: Modifier = Modifier, onClickBusInfo: (String, String, Int) -> Unit = { routeName: String, stationName: String, subtypeIdx: Int -> } ) { @@ -237,9 +272,13 @@ private fun DetailLegsBus( .parse(boardingDateTime) .plusMinutes(timeMinute.toLong()) .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + val timelineAxisOffset = timelineTimeSlotWidth + timelineTimeIconGap + (timelineIconSize / 2) + val disembarkingMarkerSize = timelineIconSize * (14f / 26f) + val disembarkingMarkerOffset = (timelineIconSize - disembarkingMarkerSize) / 2 Row( modifier = modifier + .zIndex(1f) .wrapContentHeight() ) { val busColor = TransportTypeUiMapper.getColor(TransportType.BUS, subtypeIdx) @@ -247,50 +286,66 @@ private fun DetailLegsBus( // 좌측 버스 수직라인 Box( modifier = Modifier - .width(Dimens.LegDetailVerticalLineWidth) + .widthIn(min = timelineColumnMinWidth) + .wrapContentWidth() .height(rowHeight.dp), - contentAlignment = Alignment.TopCenter + contentAlignment = Alignment.TopStart ) { // 세로 직선 Box( modifier = Modifier .fillMaxHeight() - .width(4.dp) - .padding(top = 15.dp, bottom = 20.dp) + .width(3.dp) + .align(Alignment.TopStart) + .offset(x = timelineAxisOffset - 1.5.dp) + .padding(top = 12.dp, bottom = 8.dp) .background(busColor) - .align(Alignment.Center) ) Column( modifier = Modifier + .widthIn(min = timelineColumnMinWidth) .wrapContentWidth(), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.Start ) { - // 버스 아이콘 - Image( - imageVector = ImageVector.vectorResource(busIconId), - contentDescription = null, - modifier = Modifier.size(36.dp) - ) - // 탑승 시각 - BoardingTime( - boardingDateTime = boardingDateTime, - modifier = Modifier - ) + Row( + modifier = Modifier.wrapContentWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Box(modifier = Modifier.width(timelineTimeSlotWidth)) { + BoardingTime( + boardingDateTime = boardingDateTime, + modifier = Modifier.align(Alignment.CenterStart) + ) + } + Spacer(modifier = Modifier.width(timelineTimeIconGap)) + Image( + imageVector = ImageVector.vectorResource(busIconId), + contentDescription = null, + modifier = Modifier.size(timelineIconSize) + ) + } Spacer(modifier = Modifier.weight(weight = 1f)) // 남은 공간 차지해서 아래로 밀어줌 - // 하차지점 - Box( - modifier = Modifier - .size(18.dp) // 지름 크기 - .clip(CircleShape) - .background(busColor) - ) - Spacer(modifier = Modifier.height(2.dp)) - BoardingTime( - boardingDateTime = disembarkingDateTime, - modifier = Modifier - ) + Row( + modifier = Modifier.wrapContentWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Box(modifier = Modifier.width(timelineTimeSlotWidth)) { + BoardingTime( + boardingDateTime = disembarkingDateTime, + modifier = Modifier.align(Alignment.CenterStart) + ) + } + Spacer(modifier = Modifier.width(timelineTimeIconGap)) + Box( + modifier = Modifier + .size(disembarkingMarkerSize) + .offset(x = disembarkingMarkerOffset) + .clip(CircleShape) + .background(busColor) + ) + } } } @@ -393,6 +448,8 @@ private fun DetailLegsBus( val stop = passStopList[i] Text( text = stop.stationName, + maxLines = 1, + overflow = TextOverflow.Ellipsis, style = defaultTeam6Typography.body7_B7M13, color = defaultTeam6Colors.gray200 ) @@ -402,22 +459,10 @@ private fun DetailLegsBus( Spacer(Modifier.height(36.dp)) // 하차 - Row( - modifier = Modifier - .padding(bottom = 20.dp) - ) { - Text( - text = disembarkingStation, - style = defaultTeam6Typography.body5_B5SB14, - color = defaultTeam6Colors.white - ) - Spacer(Modifier.width(4.dp)) - Text( - text = stringResource(R.string.itinerary_info_legs_disembarking), - style = defaultTeam6Typography.body5_B5SB14, - color = defaultTeam6Colors.gray200 - ) - } + DisembarkingStationText( + stationName = disembarkingStation, + modifier = Modifier.padding(bottom = 20.dp) + ) } } } @@ -432,6 +477,10 @@ private fun DetailLegsSubway( timeMinute: Int, distanceMeter: Int, passStopList: List, + timelineIconSize: Dp, + timelineTimeSlotWidth: Dp, + timelineTimeIconGap: Dp, + timelineColumnMinWidth: Dp, modifier: Modifier = Modifier ) { var rowHeight by remember { mutableStateOf(0) } @@ -440,9 +489,13 @@ private fun DetailLegsSubway( .parse(boardingDateTime) .plusMinutes(timeMinute.toLong()) .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + val timelineAxisOffset = timelineTimeSlotWidth + timelineTimeIconGap + (timelineIconSize / 2) + val disembarkingMarkerSize = timelineIconSize * (14f / 26f) + val disembarkingMarkerOffset = (timelineIconSize - disembarkingMarkerSize) / 2 Row( modifier = modifier + .zIndex(1f) .wrapContentHeight() ) { val subwayColor = TransportTypeUiMapper.getColor(TransportType.SUBWAY, subtypeIdx) @@ -450,50 +503,66 @@ private fun DetailLegsSubway( // 좌측 버스 수직라인 Box( modifier = Modifier - .width(Dimens.LegDetailVerticalLineWidth) + .widthIn(min = timelineColumnMinWidth) + .wrapContentWidth() .height(rowHeight.dp), - contentAlignment = Alignment.TopCenter + contentAlignment = Alignment.TopStart ) { // 세로 직선 Box( modifier = Modifier .fillMaxHeight() - .width(4.dp) - .padding(top = 15.dp, bottom = 20.dp) + .width(3.dp) + .align(Alignment.TopStart) + .offset(x = timelineAxisOffset - 1.5.dp) + .padding(top = 12.dp, bottom = 8.dp) .background(subwayColor) - .align(Alignment.Center) ) Column( modifier = Modifier + .widthIn(min = timelineColumnMinWidth) .wrapContentWidth(), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.Start ) { - // 지하철 아이콘 - Image( - imageVector = ImageVector.vectorResource(subwayIconId), - contentDescription = null, - modifier = Modifier.size(36.dp) - ) - // 탑승 시각 - BoardingTime( - boardingDateTime = boardingDateTime, - modifier = Modifier - ) + Row( + modifier = Modifier.wrapContentWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Box(modifier = Modifier.width(timelineTimeSlotWidth)) { + BoardingTime( + boardingDateTime = boardingDateTime, + modifier = Modifier.align(Alignment.CenterStart) + ) + } + Spacer(modifier = Modifier.width(timelineTimeIconGap)) + Image( + imageVector = ImageVector.vectorResource(subwayIconId), + contentDescription = null, + modifier = Modifier.size(timelineIconSize) + ) + } Spacer(modifier = Modifier.weight(weight = 1f)) // 남은 공간 차지해서 아래로 밀어줌 - // 하차지점 - Box( - modifier = Modifier - .size(18.dp) // 지름 크기 - .clip(CircleShape) - .background(subwayColor) - ) - Spacer(modifier = Modifier.height(2.dp)) - BoardingTime( - boardingDateTime = disembarkingDateTime, - modifier = Modifier - ) + Row( + modifier = Modifier.wrapContentWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Box(modifier = Modifier.width(timelineTimeSlotWidth)) { + BoardingTime( + boardingDateTime = disembarkingDateTime, + modifier = Modifier.align(Alignment.CenterStart) + ) + } + Spacer(modifier = Modifier.width(timelineTimeIconGap)) + Box( + modifier = Modifier + .size(disembarkingMarkerSize) + .offset(x = disembarkingMarkerOffset) + .clip(CircleShape) + .background(subwayColor) + ) + } } } @@ -570,6 +639,8 @@ private fun DetailLegsSubway( val stop = passStopList[i] Text( text = stop.stationName, + maxLines = 1, + overflow = TextOverflow.Ellipsis, style = defaultTeam6Typography.body7_B7M13, color = defaultTeam6Colors.gray200 ) @@ -579,22 +650,10 @@ private fun DetailLegsSubway( Spacer(Modifier.height(36.dp)) // 하차 - Row( - modifier = Modifier - .padding(bottom = 20.dp) - ) { - Text( - text = disembarkingStation, - style = defaultTeam6Typography.body5_B5SB14, - color = defaultTeam6Colors.white - ) - Spacer(Modifier.width(4.dp)) - Text( - text = stringResource(R.string.itinerary_info_legs_disembarking), - style = defaultTeam6Typography.body5_B5SB14, - color = defaultTeam6Colors.gray200 - ) - } + DisembarkingStationText( + stationName = disembarkingStation, + modifier = Modifier.padding(bottom = 20.dp) + ) } } } @@ -605,6 +664,9 @@ private fun DetailLegsWalk( timeMinute: Int, distanceMeter: Int, verticalHeight: Dp, + lineOffsetY: Dp, + timelineAxisOffset: Dp, + timelineColumnWidth: Dp, modifier: Modifier = Modifier ) { Row( @@ -614,13 +676,11 @@ private fun DetailLegsWalk( // 좌측 점선 Column( modifier = Modifier - .width(Dimens.LegDetailVerticalLineWidth), - horizontalAlignment = Alignment.CenterHorizontally + .width(timelineColumnWidth), + horizontalAlignment = Alignment.Start ) { - Spacer( - modifier = Modifier.height(5.dp) - ) DottedLineWithCircles( + modifier = Modifier.offset(x = timelineAxisOffset - 2.5.dp, y = lineOffsetY), height = verticalHeight ) } @@ -656,7 +716,7 @@ fun BoardingTime( ) { val boardingTime = LocalDateTime.parse(boardingDateTime) Box( - modifier = Modifier + modifier = modifier .wrapContentSize() .border( width = 1.dp, @@ -720,6 +780,85 @@ private fun DottedLineWithCircles( } } +@Composable +private fun DisembarkingStationText( + stationName: String, + modifier: Modifier = Modifier +) { + val stationStyle = defaultTeam6Typography.body5_B5SB14 + val suffixStyle = defaultTeam6Typography.body5_B5SB14 + val suffixText = stringResource(R.string.itinerary_info_legs_disembarking) + val textMeasurer = rememberTextMeasurer() + val density = LocalDensity.current + + BoxWithConstraints(modifier = modifier.fillMaxWidth()) { + val maxWidthPx = with(density) { maxWidth.toPx() }.toInt() + val spacingPx = with(density) { 4.dp.roundToPx() } + val suffixWidthPx = textMeasurer.measure( + text = suffixText, + style = suffixStyle, + maxLines = 1 + ).size.width + + val combinedLayout = textMeasurer.measure( + text = buildAnnotatedString { + append(stationName) + append(" ") + append(suffixText) + }, + style = stationStyle, + constraints = Constraints(maxWidth = maxWidthPx) + ) + val useCompactSingleLine = combinedLayout.lineCount > 2 + + if (useCompactSingleLine) { + val stationMaxWidthDp = with(density) { + (maxWidthPx - suffixWidthPx - spacingPx).coerceAtLeast(0).toDp() + } + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + modifier = Modifier.width(stationMaxWidthDp), + text = stationName, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = stationStyle, + color = defaultTeam6Colors.white + ) + Spacer(Modifier.width(4.dp)) + Text( + text = suffixText, + style = suffixStyle, + color = defaultTeam6Colors.gray200 + ) + } + } else { + Text( + text = buildAnnotatedString { + withStyle(SpanStyle(color = defaultTeam6Colors.white)) { append(stationName) } + append(" ") + withStyle(SpanStyle(color = defaultTeam6Colors.gray200)) { append(suffixText) } + }, + maxLines = 2, + overflow = TextOverflow.Clip, + style = stationStyle + ) + } + } +} + +private fun walkTimelineHeight( + distanceMeter: Int, + isFirstWalk: Boolean, + isLastWalk: Boolean +): Dp { + val baseHeight = (distanceMeter / 10f).dp.coerceIn(44.dp, 120.dp) + return when { + isFirstWalk -> baseHeight + 10.dp + isLastWalk -> baseHeight - 4.dp + else -> baseHeight + }.coerceIn(40.dp, 130.dp) +} + @Composable private fun BusNumberButton( busName: String, diff --git a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItinerarySummary.kt b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItinerarySummary.kt index 5ea201a8..b0728aa8 100644 --- a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItinerarySummary.kt +++ b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/ItinerarySummary.kt @@ -1,6 +1,5 @@ package com.depromeet.team6.presentation.ui.itinerary.component -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -39,14 +38,13 @@ fun ItinerarySummary( // ) Column( modifier = modifier - .background(defaultTeam6Colors.gray950) ) { // 남은 시간 if (durationHour > 0) { Text( modifier = Modifier .fillMaxWidth() - .padding(vertical = 8.dp), + .padding(top = 2.dp, bottom = 8.dp), text = stringResource(R.string.itinerary_summary_duration_time, durationHour, durationMinute), style = defaultTeam6Typography.display4_D4SB28, fontSize = 28.sp, @@ -56,7 +54,7 @@ fun ItinerarySummary( Text( modifier = Modifier .fillMaxWidth() - .padding(vertical = 8.dp), + .padding(top = 2.dp, bottom = 8.dp), text = stringResource(R.string.itinerary_summary_duration_minute, durationMinute), style = defaultTeam6Typography.display4_D4SB28, fontSize = 28.sp, @@ -83,7 +81,7 @@ fun ItinerarySummary( // 대중교통 정보 요약 SummaryBarChart( modifier = Modifier - .padding(vertical = 16.dp), + .padding(top = 14.dp, bottom = 10.dp), legs = legs ) diff --git a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/SummaryBarChart.kt b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/SummaryBarChart.kt index 435b8a7a..c2c09a91 100644 --- a/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/SummaryBarChart.kt +++ b/app/src/main/java/com/depromeet/team6/presentation/ui/itinerary/component/SummaryBarChart.kt @@ -45,9 +45,8 @@ fun SummaryBarChart( modifier: Modifier = Modifier, legs: List ) { - val total = legs.sumOf { it.sectionTime }.toFloat() var rowWidthPx by remember { mutableStateOf(0f) } // Row의 너비를 저장할 변수 - val minBarWidth = 25.dp + val minBarWidth = 18.dp val density = LocalDensity.current var finalWidths by remember { mutableStateOf(emptyList()) } @@ -156,19 +155,25 @@ private fun calculateFinalWidths( minBarWidth: Dp ): List { // 임시 데이터 구조: (인덱스, 해당 leg의 sectionTime) - val remainingLegs = legs.mapIndexed { index, leg -> - index to leg.sectionTime - }.toMutableList() + if (legs.isEmpty()) return emptyList() // 최소너비 미리 세팅해두고, 그만큼을 전체너비에서 제외 - val finalWidths = MutableList(legs.size) { minBarWidth } - var remainingWidth = totalWidth.value - (minBarWidth.value * finalWidths.size) // dp 단위의 Float 값으로 사용 + val totalWidthValue = totalWidth.value + val minWidthValue = minBarWidth.value + val totalMinWidth = minWidthValue * legs.size + + if (totalMinWidth >= totalWidthValue) { + val average = totalWidthValue / legs.size + return List(legs.size) { average.dp } + } // 남은 아이템에 대해 남은 너비를 비율로 분배 - val remainingTimeSum = remainingLegs.sumOf { it.second } - remainingLegs.forEach { (index, time) -> - val allocated = if (remainingTimeSum > 0) remainingWidth * (time / remainingTimeSum.toFloat()) else 0f - finalWidths[index] += allocated.dp + val remainingWidth = totalWidthValue - totalMinWidth + val remainingTimeSum = legs.sumOf { it.sectionTime.coerceAtLeast(0) }.coerceAtLeast(1) + val finalWidths = MutableList(legs.size) { minBarWidth } + legs.forEachIndexed { index, leg -> + val allocated = remainingWidth * (leg.sectionTime.coerceAtLeast(0) / remainingTimeSum.toFloat()) + finalWidths[index] = (minWidthValue + allocated).dp } return finalWidths diff --git a/app/src/main/java/com/depromeet/team6/presentation/util/Dimens.kt b/app/src/main/java/com/depromeet/team6/presentation/util/Dimens.kt index dd7b5433..b9705123 100644 --- a/app/src/main/java/com/depromeet/team6/presentation/util/Dimens.kt +++ b/app/src/main/java/com/depromeet/team6/presentation/util/Dimens.kt @@ -12,5 +12,8 @@ object Dimens { val LegDepartureTimeWidth = 38.dp val LegDetailVerticalLineWidth = 40.dp val LegDetailLineTextMargin = 17.dp + val LegTimelineIconSize = 26.dp + val LegTimelineTimeIconGap = 5.dp + val LegTimelineTimeSlotWidth = 44.dp val WalkIconWithRippleSize = 48.dp }