Skip to content
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

#29 Create AM, PM ScrollableSelctor #33

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@ package com.composepicker.picker.component

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import com.composepicker.picker.common.PickerCommonConfiguration
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
Expand All @@ -24,64 +29,94 @@ import kotlinx.coroutines.flow.map
@Composable
fun ScrollableSelector(
modifier: Modifier,
valueList: List<Int>,
value: Int,
valueList: List<String>,
value: String,
limit: Int = 3,
suffix: @Composable (() -> Unit) = {},
onValueChanged: (Int) -> Unit,
configuration: PickerCommonConfiguration = PickerCommonConfiguration.Builder().build(),
onValueChanged: (String) -> Unit,
isYear: Boolean = false,
) {/*
TODO : Use This Selector at TimePicker & DatePicker. This Item must be scrolled with 3 text values with suffix on center.
*/
if (isYear) {
FiniteLazyColumn(valueList = valueList, value = value, limit = 5, onValueChanged = {})
} else {
InfiniteLazyColumn(valueList = valueList, value = value, limit = 5, onValueChanged = {})
is24Hour: Boolean = true,
arrangement: Dp = 8.dp,
highlightFontColor: Color = Color.Black,
fontColor: Color = Color.DarkGray,
highlightTextStyle: TextStyle = MaterialTheme.typography.titleMedium,
textStyle: TextStyle = MaterialTheme.typography.titleSmall,
) {

require(limit % 2 != 0) { "limit Must be Odd." }
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically
) {
if (isYear || is24Hour.not()) {
FiniteLazyColumn(
modifier = modifier,
valueList = valueList,
value = value,
limit = limit,
onValueChanged = {})
} else {
InfiniteLazyColumn(
modifier = modifier,
valueList = valueList,
value = value,
limit = limit,
onValueChanged = {})
}
suffix()
}

}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FiniteLazyColumn(
modifier: Modifier = Modifier,
valueList: List<Int>,
value: Int,
valueList: List<String>,
value: String,
limit: Int,
suffix: @Composable (() -> Unit) = {},
onValueChanged: (Int) -> Unit,
configuration: PickerCommonConfiguration = PickerCommonConfiguration.Builder().build(),
arrangement: Dp = 8.dp,
highlightFontColor: Color = Color.Black,
fontColor: Color = Color.DarkGray,
highlightTextStyle: TextStyle = MaterialTheme.typography.titleMedium,
textStyle: TextStyle = MaterialTheme.typography.titleSmall,
onValueChanged: (String) -> Unit,
) {

val yearValueList = valueList.toMutableList()

yearValueList.add(yearValueList.lastIndex + 1, 0)
yearValueList.add(yearValueList.lastIndex + 1, 0)
yearValueList.add(0,0)
yearValueList.add(0,0)
val halfLimit = limit / 2

repeat(halfLimit) {
yearValueList.add(0, "0")
yearValueList.add(yearValueList.lastIndex + 1, "0")
}

val itemHeightPixels = remember { mutableStateOf(0) }
val unSelectLineHeight = (textStyle.lineHeight * (limit - 1))
val lineHeight =
rememberUpdatedState(newValue = unSelectLineHeight.value + highlightTextStyle.lineHeight.value + (arrangement.value * (halfLimit)))
val listState =
rememberLazyListState(initialFirstVisibleItemIndex = (yearValueList.indexOf(value) - 2))
val centerIndex = remember { mutableStateOf((listState.firstVisibleItemIndex + 2)) }
rememberLazyListState(initialFirstVisibleItemIndex = (yearValueList.indexOf(value) - halfLimit))
val centerIndex = remember { mutableStateOf((listState.firstVisibleItemIndex + halfLimit)) }
val centerItem = remember { mutableStateOf(yearValueList[centerIndex.value]) }
val flingBehavior = rememberSnapFlingBehavior(lazyListState = listState)

LazyColumn(
state = listState,
modifier = modifier.height(pixelsToDp(pixels = itemHeightPixels.value * limit)),
flingBehavior = flingBehavior
modifier = modifier.height(lineHeight.value.dp),
verticalArrangement = Arrangement.spacedBy(arrangement),
flingBehavior = flingBehavior,
horizontalAlignment = Alignment.CenterHorizontally
) {
items(yearValueList.size) { index ->
val item = yearValueList[index]

Row(modifier = Modifier.onSizeChanged { size ->
itemHeightPixels.value = size.height
}) {
if (item != 0) {
if(index == centerIndex.value) {
Text(text = "$item")
suffix()
Row {
if (item != "0") {
if (item == centerItem.value) {
Text(text = "$item", style = highlightTextStyle, color = highlightFontColor)
} else {
Text(text = "$item")
Text(text = "$item", style = textStyle, color = fontColor)
}
} else {
Text(text = "")
Expand All @@ -93,11 +128,11 @@ fun FiniteLazyColumn(
// call center value
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }.distinctUntilChanged().map {
(it % yearValueList.size) + limit / 2
(it % yearValueList.size) + halfLimit
}.collectLatest { index ->
centerIndex.value = index % yearValueList.size
centerItem.value = yearValueList[centerIndex.value]
onValueChanged(centerItem.value)
onValueChanged(centerItem.value)
}
}
}
Expand All @@ -106,16 +141,21 @@ fun FiniteLazyColumn(
@Composable
internal fun InfiniteLazyColumn(
modifier: Modifier = Modifier,
valueList: List<Int>,
value: Int,
valueList: List<String>,
value: String,
limit: Int,
suffix: @Composable (() -> Unit) = {},
onValueChanged: (Int) -> Unit,
configuration: PickerCommonConfiguration = PickerCommonConfiguration.Builder().build(),
arrangement: Dp = 8.dp,
onValueChanged: (String) -> Unit,
highlightFontColor: Color = Color.Black,
fontColor: Color = Color.DarkGray,
highlightTextStyle: TextStyle = MaterialTheme.typography.titleMedium,
textStyle: TextStyle = MaterialTheme.typography.titleSmall,
) {
val itemHeightPixels = remember { mutableStateOf(0) }
val unSelectLineHeight = (textStyle.lineHeight * (limit - 1))
val lineHeight =
rememberUpdatedState(newValue = unSelectLineHeight.value + highlightTextStyle.lineHeight.value + (arrangement.value * (limit - 2)))
val listState = rememberLazyListState(
initialFirstVisibleItemIndex = (value + valueList.size - (limit / 2) - 1) % valueList.size
initialFirstVisibleItemIndex = (value.toInt() + valueList.size - (limit / 2) - 1) % valueList.size
)
val centerIndex =
remember { mutableStateOf((listState.firstVisibleItemIndex + limit / 2) % valueList.size) }
Expand All @@ -124,28 +164,21 @@ internal fun InfiniteLazyColumn(

LazyColumn(
state = listState,
modifier = modifier.height(pixelsToDp(pixels = itemHeightPixels.value * limit)),
flingBehavior = flingBehavior
modifier = modifier.height(lineHeight.value.dp),
flingBehavior = flingBehavior,
verticalArrangement = Arrangement.spacedBy(arrangement),
horizontalAlignment = Alignment.CenterHorizontally
) {
items(count = Int.MAX_VALUE) { index ->
val idx = index % valueList.size
val item = valueList[idx]

if (idx == centerIndex.value) {
Row(modifier = Modifier.onSizeChanged { size ->
itemHeightPixels.value = size.height
}) {
Text(text = "$item")
suffix()
}

if (item == centerItem.value) {
Text(text = "$item", style = highlightTextStyle, color = highlightFontColor)
} else {
Row(modifier = Modifier.onSizeChanged { size ->
itemHeightPixels.value = size.height
}) {
Text(text = "$item")
}
Text(text = "$item", style = textStyle, color = fontColor)
}

}
}

Expand All @@ -161,6 +194,5 @@ internal fun InfiniteLazyColumn(
}
}

@Composable
private fun pixelsToDp(pixels: Int) = with(LocalDensity.current) { pixels.toDp() }

//@Composable
//private fun pixelsToDp(pixels: Int) = with(LocalDensity.current) { pixels.toDp() }
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@ package com.composepicker.picker.datepicker

import android.text.format.DateFormat
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.composepicker.picker.component.ScrollableSelector
import kotlinx.datetime.Instant

Expand All @@ -18,33 +27,41 @@ fun ProgDatePicker(
yearSuffix: @Composable () -> Unit = {},
monthSuffix: @Composable () -> Unit = {},
daySuffix: @Composable () -> Unit = {},
onDateChanged: (instant: Instant) -> Unit
onDateChanged: (instant: Instant) -> Unit,
arrangement: Dp = 8.dp,
highlightFontColor: Color = Color.Black,
fontColor: Color = Color.DarkGray,
highlightTextStyle: TextStyle = MaterialTheme.typography.titleMedium,
textStyle: TextStyle = MaterialTheme.typography.titleSmall
) {
// TODO : Change Composable When DateFormat Change.
Row(modifier = modifier) {
ScrollableSelector(
modifier = modifier,
modifier = Modifier,
valueList = progDatePickerState.yearList,
value = progDatePickerState.year,
onValueChanged = {
onDateChanged(Instant.parse("$it.${progDatePickerState.month}.${progDatePickerState.day}"))
},
suffix = yearSuffix,
isYear = true,
)
ScrollableSelector(
modifier = modifier,
modifier = Modifier,
valueList = progDatePickerState.monthList,
value = progDatePickerState.month,
suffix = monthSuffix,
onValueChanged = {
onDateChanged(Instant.parse("$it.${progDatePickerState.month}.${progDatePickerState.day}"))
onDateChanged(Instant.parse("${progDatePickerState.year}.$it.${progDatePickerState.day}"))
}
)
ScrollableSelector(
modifier = modifier,
modifier = Modifier,
valueList = progDatePickerState.dayList,
value = progDatePickerState.day,
suffix = daySuffix,
onValueChanged = {
onDateChanged(Instant.parse("$it.${progDatePickerState.month}.${progDatePickerState.day}"))
onDateChanged(Instant.parse("${progDatePickerState.year}.${progDatePickerState.month}.$it"))
}
)
}
Expand All @@ -53,10 +70,26 @@ fun ProgDatePicker(
@Preview
@Composable
fun PreviewProgDatePicker() {
ProgDatePicker(
modifier = Modifier.fillMaxSize(),
progDatePickerState = rememberProgDatePickerState(Instant.parse("2018-01-28T18:35:24.00Z")),
dateFormat = DateFormat(),
onDateChanged = {}
)
Surface(
modifier = Modifier.padding(12.dp),
color = Color.Gray,
shape = RoundedCornerShape(1)
) {
ProgDatePicker(
modifier = Modifier.fillMaxWidth().padding(24.dp),
progDatePickerState = rememberProgDatePickerState(Instant.parse("2018-01-28T18:35:24.00Z")),
dateFormat = DateFormat(),
onDateChanged = {},
yearSuffix = {
Text(text = "λ…„")
},
monthSuffix = {
Text(text = "μ›”")
},
daySuffix = {
Text(text = "일")
}
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
Expand All @@ -25,13 +26,13 @@ class ProgDatePickerState(
require(minDate >= Instant.parse("1000-01-01T00:00:00.00Z")) { "minDate cannot be smaller than 1000.01.01" }
}

var year by mutableIntStateOf(initialDate.toLocalDateTime(TimeZone.currentSystemDefault()).year)
var month by mutableIntStateOf(initialDate.toLocalDateTime(TimeZone.currentSystemDefault()).monthNumber)
var day by mutableIntStateOf(initialDate.toLocalDateTime(TimeZone.currentSystemDefault()).dayOfMonth)
var year by mutableStateOf(initialDate.toLocalDateTime(TimeZone.currentSystemDefault()).year.toString())
var month by mutableStateOf(initialDate.toLocalDateTime(TimeZone.currentSystemDefault()).monthNumber.toString())
var day by mutableStateOf(initialDate.toLocalDateTime(TimeZone.currentSystemDefault()).dayOfMonth.toString())

val yearList = (minDate.toLocalDateTime(TimeZone.currentSystemDefault()).year..maxDate.toLocalDateTime(TimeZone.currentSystemDefault()).year).toList()
val monthList = (1..12).toList()
val dayList = (1..31).toList() // TODO : Change DateList when month change.
val yearList = (minDate.toLocalDateTime(TimeZone.currentSystemDefault()).year..maxDate.toLocalDateTime(TimeZone.currentSystemDefault()).year).map { it.toString() }
val monthList = (1..12).map{ it.toString()}
val dayList = (1..31).map{ it.toString()} // TODO : Change DateList when month change.

companion object {
fun Saver(): Saver<ProgDatePickerState, *> = Saver(
Expand Down
Loading