当设备息屏的时候,如何有定义的DreamService的程序,则会启动屏保程序,现在通过compose写一个简单的屏幕保护程序:
定义屏保服务
<service
android:name=".ComposeDreamService"
android:exported="true"
android:icon="@drawable/ic_launcher_foreground"
android:label="ComposeDreamService"
android:permission="android.permission.BIND_DREAM_SERVICE">
<intent-filter>
<action android:name="android.service.dreams.DreamService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
定义 DreamService
class ComposeDreamService : DreamService(),LifecycleOwner, ViewModelStoreOwner ,
SavedStateRegistryOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
private val mViewModelStore = ViewModelStore()
private val mSavedStateRegistryController = SavedStateRegistryController.create(this)
override val lifecycle: Lifecycle
get() = lifecycleRegistry
override val viewModelStore: ViewModelStore
get() = mViewModelStore
override val savedStateRegistry: SavedStateRegistry
get() = mSavedStateRegistryController.savedStateRegistry
override fun onCreate() {
super.onCreate()
mSavedStateRegistryController.performAttach()
mSavedStateRegistryController.performRestore(null)
lifecycleRegistry.currentState = Lifecycle.State.CREATED
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
lifecycleRegistry.currentState = Lifecycle.State.STARTED
// 允许交互(如果需要)
isInteractive = true
// 防止屏幕变暗
isFullscreen = true
// 创建 ComposeView
val composeView = ComposeView(this).apply {
setViewTreeLifecycleOwner(this@ComposeDreamService)
setViewTreeViewModelStoreOwner(this@ComposeDreamService)
setViewTreeSavedStateRegistryOwner(this@ComposeDreamService) // 👈 必须加这个!
setContent {
ScreenSaverContent()
}
}
setContentView(composeView)
}
override fun onDreamingStopped() {
super.onDreamingStopped()
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
viewModelStore.clear()
}
}
漂浮时钟
@Composable
fun ScreenSaverContent() {
var currentTime by remember { mutableStateOf(System.currentTimeMillis()) }
// 定时更新时间
LaunchedEffect(Unit) {
while (true) {
delay(1000)
currentTime = System.currentTimeMillis()
}
}
BoxWithConstraints(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
val maxWidthPx = with(LocalDensity.current) { maxWidth.toPx() }
val maxHeightPx = with(LocalDensity.current) { maxHeight.toPx() }
// 当前目标偏移量
val offsetX = remember { Animatable(0f) }
val offsetY = remember { Animatable(0f) }
// 定期随机移动位置
LaunchedEffect(Unit) {
val random = Random(System.currentTimeMillis())
while (true) {
// 随机新位置(边缘预留100px避免被裁剪)
val newX = random.nextFloat() * (maxWidthPx - 200f) + 100f
val newY = random.nextFloat() * (maxHeightPx - 200f) + 100f
// 动画移动过去
coroutineScope {
launch {
offsetX.animateTo(newX, animationSpec = tween(4000, easing = LinearEasing))
}
launch {
offsetY.animateTo(newY, animationSpec = tween(4000, easing = LinearEasing))
}
}
// 每10秒换一次位置
delay(10_000)
}
}
// 绘制时钟
Text(
text = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(currentTime),
color = Color.White,
fontSize = 48.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.offset { IntOffset(offsetX.value.toInt(), offsetY.value.toInt()) }
)
}
}
1717

被折叠的 条评论
为什么被折叠?



