On this article, I’ll present you construct a 24h time picker dialog utilizing Jetpack Compose.
Let’s begin by defining a part for the timer selector itself, not the dialog. That method we are able to reuse the time picker in different elements of the appliance even outdoors the dialog.
@Composable
enjoyable TimerPicker(
onCancel: () -> Unit,
onOk: (Time) -> Unit,
modifier: Modifier = Modifier
)
onCancel
is named when the cancel button is pressed. onOk
is named when the okay button is pressed.
Time
only a information layer containing hours and minutes.
information class Time(val hour: Int, val minute: Int)
The place’s Luc? TimerPicker
composable we have to outline a couple of variables.
var selectedPart by bear in mind { mutableStateOf(TimePart.Hour) }
var selectedHour by bear in mind { mutableStateOf(0) }
var selectedMinute by bear in mind { mutableStateOf(0) }
val selectedIndex by bear in mind {
derivedStateOf { if (selectedPart == TimePart.Hour) selectedHour else selectedMinute / 5 }
}
val onTime: (Int) -> Unit = bear in mind {
{ if (selectedPart == TimePart.Hour) selectedHour = it else selectedMinute = it * 5 }
}
selectedPart
signifies whether or not we should always show hours or minutes. selectedHour
shops the chosen person’s time. selectedMinute
retailer chosen person minutes.
selectedIndex
represents the place within the clock, the hour is the hour (0-23) however the minute is the hour in the identical place. For instance, in quarter-hour it’s 3, in 40 minutes it’s 8. We have to divide selectedMinute
equals 5 as a result of that is the corresponding hour.
enum class TimePart { Hour, Minute }
TimePart
simply an enum representing what is chosen (hours or minutes).
On the watch, there are 2 playing cards, 1 hand reveals the hour and 1 minute I name TimeCard
.
@Composable
enjoyable TimeCard(
time: Int,
isSelected: Boolean,
onClick: () -> Unit
) {
Card(
form = RoundedCornerShape(6.dp),
backgroundColor = if (isSelected) selectedColor else secondaryColor,
modifier = Modifier.clickable { onClick() }
) {
Textual content(
textual content = if (time == 0) "00" else time.toString(),
fontSize = 32.sp,
coloration = if (isSelected) secondaryColor else Coloration.White,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp)
)
}
}
It is only a card that reveals the chosen hours and minutes. That is how they’re positioned in TimerPicker
ingredient.
Row(
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
TimeCard(
time = selectedHour,
isSelected = selectedPart == TimePart.Hour,
onClick = { selectedPart = TimePart.Hour }
)
Textual content(
textual content = ":",
fontSize = 32.sp,
coloration = Coloration.White,
modifier = Modifier.padding(horizontal = 2.dp)
)
TimeCard(
time = selectedMinute,
isSelected = selectedPart == TimePart.Minute,
onClick = { selectedPart = TimePart.Minute }
)
}
Every time they’re pressed, we replace selectedPart
based mostly on which one is pressed.
Now we come to the precise clock.
@Composable
enjoyable Clock(
index: Int,
modifier: Modifier = Modifier,
content material: @Composable () -> Unit
) {
val localDensity = LocalDensity.present
var radiusPx by bear in mind { mutableStateOf(0) }
val radiusInsidePx by bear in mind { derivedStateOf { (radiusPx * 0.67).toInt() } }
var indexCirclePx by bear in mind { mutableStateOf(36f) }
val padding by bear in mind {
derivedStateOf { with(localDensity) { (indexCirclePx * 0.5).toInt().toDp() } }
}
...
}
index
represents a quantity between 0 and 23, which is the at present chosen index.
radiusPx
is the radius of the outer circle (0-11) and radiusInsidePx
is the radius of the internal circle (12-23).
Under I declare 2 capabilities that calculate the place of the indicator on the clock.
enjoyable posX(index: Int) =
((if (index enjoyable posY(index: Int) =
((if (index
If the index is lower than 12, it means the time goes within the outer circle. If better than or equal to 12 then enter the internal ring.
non-public const val step = PI * 2 / 12
non-public enjoyable angleForIndex(index: Int) = -PI / 2 + step * index
We do not use levels when drawing, we use radians as a substitute. 1π radians is equal to 180° so π * 2 / 12
is the step for every index (360°/12).
angleForIndex
solely calculate the angle based mostly on the index. 0° will not be on the high, it is at 3 o’clock. We have to drop by 90° to verify the index 0 is at what we contemplate 0°. angleForIndex
begins with -π / 2
as a result of π / 2
it is 90° -π / 2
is -90°, that is the place the index 0 stars.
Now we lastly come to the clock drawing code, which remains to be in Clock
might be reassembled.
Field(modifier = modifier) {
Floor(
coloration = primaryColor,
form = CircleShape,
modifier = Modifier.fillMaxSize()
) {}
Structure(
content material = content material,
modifier = Modifier
.padding(padding)
.drawBehind {
val finish = Offset(
x = dimension.width / 2 + posX(time),
y = dimension.peak / 2 + posY(time)
)
drawCircle( // #1
radius = 9f,
coloration = selectedColor,
)
drawLine( // #2
begin = middle,
finish = finish,
coloration = selectedColor,
strokeWidth = 4f
)
drawCircle( // #3
radius = indexCirclePx,
middle = finish,
coloration = selectedColor,
)
}
) { measurables, constraints ->
val placeables = measurables.map { it.measure(constraints) }
assert(placeables.depend() == 12 || placeables.depend() == 24) { "Invalid gadgets: must be 12 or 24, is ${placeables.depend()}" }
indexCirclePx = (constraints.maxWidth * 0.07).toFloat() // #4
structure(constraints.maxWidth, constraints.maxHeight) {
val dimension = constraints.maxWidth
val maxElementSize = maxOf(placeables.maxOf { it.width }, placeables.maxOf { it.peak })
radiusPx = (constraints.maxWidth - maxElementSize) / 2 // #5
placeables.forEachIndexed { index, placeable ->
placeable.place( // #6
dimension / 2 - placeable.width / 2 + posX(index),
dimension / 2 - placeable.peak / 2 + posY(index),
)
}
}
}
}
I’m utilizing structure so we are able to select the place to put components.
#1 draw a small circle within the middle of the clock.
#2 attracts a line between the middle circle and the chosen index.
#3 attracts a circle on the chosen index.
#4 calculates chosen index circle radius based mostly on clock dimension.
#5 the radius of the outer circle is simply the obtainable house divided by 2.
#6 locations components in the course of their positions.
Will return TimerPicker
we insert Clock
ingredient. The aspectedRatio
set to 1 so it is at all times sq..
Clock(
time = selectedTime,
modifier = Modifier
.aspectRatio(1f)
.align(Alignment.CenterHorizontally)
) {
ClockMarks24h(selectedPart, selectedTime, onTime)
}
ClockMarks24h
There are components for the 24-hour clock.
@Composable
enjoyable ClockMarks24h(selectedPart: TimePart, selectedTime: Int, onTime: (Int) -> Unit) {
if (selectedPart == TimePart.Hour) {
Mark(textual content = "00", index = 0, isSelected = selectedTime == 0, onIndex = onTime)
(1..23).map {
Mark(textual content = it.toString(), index = it, isSelected = selectedTime == it, onIndex = onTime)
}
} else {
Mark(textual content = "00", index = 0, isSelected = selectedTime == 0, onIndex = onTime)
Mark(textual content = "05", index = 1, isSelected = selectedTime == 1, onIndex = onTime)
(2..11).map {
Mark(textual content = (it * 5).toString(), index = it, isSelected = selectedTime == it, onIndex = onTime)
}
}
}
The Mark
composable is what represents hour
And minute
. It is a regular part so it may be styled nonetheless you need.
@Composable
enjoyable Mark(
textual content: String,
index: Int, // 0..23
onIndex: (Int) -> Unit,
isSelected: Boolean
) {
Textual content(
textual content = textual content,
coloration = if (isSelected) secondaryColor else Coloration.White,
modifier = Modifier.clickable(
interactionSource = bear in mind { MutableInteractionSource() },
indication = null,
onClick = { onIndex(index) }
)
)
}
Lastly, we are able to create a part for the dialog and put TimerPicker
inside it.
@Composable
enjoyable TimerPickerDialog() {
val localContext = LocalContext.present
Dialog(onDismissRequest = { /*TODO*/ }) {
Field(modifier = Modifier.fillMaxWidth()) {
TimerPicker(
onOk = { Toast.makeText(localContext, it.toString(), Toast.LENGTH_SHORT).present() },
onCancel = {},
modifier = Modifier
.fillMaxWidth(0.8f)
.align(Alignment.Heart)
)
}
}
}
Closing end result:
If you wish to see the total supply code, you may test it out This.
You probably have any feedback or strategies, be at liberty to contact me on Twitter.
photograph taken by Ales Krivec ABOVE depart
John Wick: Chapter 4 (FREE) FULLMOVIE The Super Mario Bros Movie avatar 2 Where To Watch Creed 3 Free At Home Knock at the Cabin (2023) FullMovie Where To Watch Ant-Man 3 and the Wasp: Quantumania Cocaine Bear 2023 (FullMovie) Scream 6 Full Movie
Supply hyperlink