I am pretty new in Android Studio and need to make a Slider to view data received by an Arduino via Wi-Fi (Temperature and Light). I want to have a SemicircularSlider.kt composable that receives the values of the Arduino and displays them in a range which I will set on the Kotlin file. Then With the Kotlin file I want to add them to my layout in my XML file like it's another View, Or just a Widget and change the values (Like color and range of the slider) through XML.
As I'm new to Composable and Android Studio I tried making the Composable with ChatGPT and gave me This Code
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import kotlin.math.PI
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
@Composable
fun SemiCircularSlider(
modifier: Modifier = Modifier,
value: Float,
onValueChange: (Float) -> Unit,
colorStart: Color,
colorEnd: Color
) {
var angle by remember { mutableStateOf(0f) }
Canvas(modifier = modifier
.fillMaxSize()
.padding(16.dp)
.pointerInput(Unit) {
detectDragGestures { change, _ ->
val position = change.position
val center = Offset((size.width / 2).toFloat(), (size.height / 2).toFloat())
angle = ((atan2(position.y - center.y, position.x - center.x) * 180 / PI).toFloat() + 180) % 180
onValueChange(angle)
}
}
) {
val radius = size.minDimension / 2
val arcRect = Rect(Offset(size.width / 2 - radius, size.height / 2 - radius), Size(radius * 2, radius * 2))
drawArc(
color = colorStart,
startAngle = 180f,
sweepAngle = angle,
useCenter = false,
style = Stroke(width = 20f),
topLeft = arcRect.topLeft,
size = arcRect.size
)
val thumbX = center.x + cos((angle - 180) * PI / 180).toFloat() * radius
val thumbY = center.y + sin((angle - 180) * PI / 180).toFloat() * radius
drawCircle(
color = colorEnd,
radius = 20f,
center = Offset(thumbX, thumbY)
)
}
}
@Composable
fun MainControlScreen() {
var temperature by remember { mutableStateOf(0f) }
var light by remember { mutableStateOf(0f) }
MaterialTheme {
Column {
Text("Temperature: ${temperature.toInt()}")
SemiCircularSlider(
value = temperature,
onValueChange = { temperature = it },
colorStart = Color(0xFFFF5722),
colorEnd = Color(0xFFFFC107)
)
Spacer(modifier = Modifier.height(32.dp))
Text("Light: ${light.toInt()}")
SemiCircularSlider(
value = light,
onValueChange = { light = it },
colorStart = Color(0xFF03A9F4),
colorEnd = Color(0xFF4CAF50)
)
}
}
}
I didn't ask for all about the Arduino connection because I can't even show a semicircular slider on the screen
This solution dropped me the error "org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering"
This is the idea on How I want my slider to look Temperature Semicircular Slider Idea Only difference would be adding numbers on the leftmost bottom of the slider to show the user a range of values and a center number showing the current value
Feel free to ask any questions if needed or ask for more code. And sorry if my English is a bit off.
Your code (surprisingly ChatGPT gave a good answer) looks fine but it needs some tweaks to make it work.
First of all setting fillMaxSize()
for Canvas
is a mistake most of the time. I rather set an arbitrary size for it or set it to a portion of the device width/height but for the sake of just making this work let's just set an arbitrary size for it.
Canvas(modifier = modifier
.size(300.dp)
instead of:
Canvas(modifier = modifier
.fillMaxSize()
The next problem is that your SemiCircularSlider
has no use of the value
parameter which is the main data for your Slider/Gauge.
var angle by remember { mutableFloatStateOf(value) }
instead of:
var angle by remember { mutableFloatStateOf(0f) }
So fixing the above problems leaves us with this: