想不到什么题目,提下需求吧,我想在定时器里面执行一个 composable,但很显然这种方式是不允许的,会出现如下错误:
@Composable invocations can only happen from the context of a @Composable function
按道理,Timer与compose之间应该是可以实现共存的,或者有其他替代的方式,只是我找不到资料,不知道怎么写
val handler = Handler(Looper.getMainLooper())
val task: TimerTask = object : TimerTask() {
override fun run() {
handler.post {
// composable
Text("这是个composable")
}
}
}
val timer = Timer()
timer.schedule(task, 10000)
import androidx.compose.ui.unit.dp
import androidx.activity.compose.setContent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PointMode
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Surface(
color = Color(0xFF101010),
modifier = Modifier.fillMaxSize()
) {
Box(
contentAlignment = Alignment.Center
) {
// call the function Timer
// and pass the values
// it is defined below.
Timer(
totalTime = 100L * 1000L,
handleColor = Color.Green,
inactiveBarColor = Color.DarkGray,
activeBarColor = Color(0xFF37B900),
modifier = Modifier.size(200.dp)
)
}
}
}
}
}
// create a composable to
// Draw arc and handle
@Composable
fun Timer(
// total time of the timer
totalTime: Long,
// circular handle color
handleColor: Color,
// color of inactive bar / progress bar
inactiveBarColor: Color,
// color of active bar
activeBarColor: Color,
modifier: Modifier = Modifier,
// set initial value to 1
initialValue: Float = 1f,
strokeWidth: Dp = 5.dp
) {
// create variable for
// size of the composable
var size by remember {
mutableStateOf(IntSize.Zero)
}
// create variable for value
var value by remember {
mutableStateOf(initialValue)
}
// create variable for current time
var currentTime by remember {
mutableStateOf(totalTime)
}
// create variable for isTimerRunning
var isTimerRunning by remember {
mutableStateOf(false)
}
LaunchedEffect(key1 = currentTime, key2 = isTimerRunning) {
if(currentTime > 0 && isTimerRunning) {
delay(100L)
currentTime -= 100L
value = currentTime / totalTime.toFloat()
}
}
Box(
contentAlignment = Alignment.Center,
modifier = modifier
.onSizeChanged {
size = it
}
) {
// draw the timer
Canvas(modifier = modifier) {
// draw the inactive arc with following parameters
drawArc(
color = inactiveBarColor, // assign the color
startAngle = -215f, // assign the start angle
sweepAngle = 250f, // arc angles
useCenter = false, // prevents our arc to connect at te ends
size = Size(size.width.toFloat(), size.height.toFloat()),
// to make ends of arc round
style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)
)
// draw the active arc with following parameters
drawArc(
color = activeBarColor, // assign the color
startAngle = -215f, // assign the start angle
sweepAngle = 250f * value, // reduce the sweep angle
// with the current value
useCenter = false, // prevents our arc to connect at te ends
size = Size(size.width.toFloat(), size.height.toFloat()),
// to make ends of arc round
style = Stroke(strokeWidth.toPx(), cap = StrokeCap.Round)
)
// calculate the value from arc pointer position
val center = Offset(size.width / 2f, size.height / 2f)
val beta = (250f * value + 145f) * (PI / 180f).toFloat()
val r = size.width / 2f
val a = cos(beta) * r
val b = sin(beta) * r
// draw the circular pointer/ cap
drawPoints(
listOf(Offset(center.x + a, center.y + b)),
pointMode = PointMode.Points,
color = handleColor,
strokeWidth = (strokeWidth * 3f).toPx(),
cap = StrokeCap.Round // make the pointer round
)
}
// add value of the timer
Text(
text = (currentTime / 1000L).toString(),
fontSize = 44.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
// create button to start or stop the timer
Button(
onClick = {
if(currentTime <= 0L) {
currentTime = totalTime
isTimerRunning = true
} else {
isTimerRunning = !isTimerRunning
}
},
modifier = Modifier.align(Alignment.BottomCenter),
// change button color
colors = ButtonDefaults.buttonColors(
backgroundColor = if (!isTimerRunning || currentTime <= 0L) {
Color.Green
} else {
Color.Red
}
)
) {
Text(
// change the text of button based on values
text = if (isTimerRunning && currentTime >= 0L) "Stop"
else if (!isTimerRunning && currentTime >= 0L) "Start"
else "Restart"
)
}
}
}
最近怎么热恋compose了,这个目前情况,做简单的还好,复杂起来了可能卡壳,产品的需求可不会跟着你掌握的来。
compose 要运行在一个有@Composable 注解的函数中,你这个直接Text 肯定不行,
你把这个Text 这部分代码封装成一个用Composabe注释的函数中,然后再使用试试