用Kotlin写一个Android闹钟

ai写的,还是调试了一下,有些问题要多次追问。记录下来,下次好用。

复制代码
package com.example.myclock

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.example.myclock.ui.theme.MyClockTheme
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import java.util.*
import android.content.BroadcastReceiver
import android.media.MediaPlayer
import android.media.RingtoneManager
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.TextField


// 添加全局状态管理类
object AlarmStateManager {
    // 定义一个公共变量,用于表示闹钟是否处于激活状态
    public var isAlarmActive = false
    // 定义一个私有变量,用于存储MediaPlayer对象
    private var mediaPlayer: MediaPlayer? = null
    // 定义一个私有变量,用于存储上下文对象
    private var context: Context? = null

    // 初始化方法,用于设置上下文对象
    fun initialize(context: Context) {
        this.context = context
    }

    // 激活闹钟的方法
    fun activateAlarm() {
        // 检查闹钟是否未激活且上下文对象不为空
        if (!isAlarmActive && context != null) {
            // 设置闹钟为激活状态
            isAlarmActive = true
            // 创建一个MediaPlayer对象,并设置默认的闹钟铃声
            mediaPlayer = MediaPlayer.create(context, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM))
            // 开始播放铃声
            mediaPlayer?.start()
        }
    }

    // 停用闹钟的方法
    fun deactivateAlarm() {
        // 设置闹钟为未激活状态
        isAlarmActive = false
        // 释放MediaPlayer对象
        mediaPlayer?.release()
        // 将MediaPlayer对象置为null
        mediaPlayer = null
    }
}
class MainActivity : ComponentActivity() {
    // 重写onCreate方法,这是Activity生命周期中的第一个方法
    override fun onCreate(savedInstanceState: Bundle?) {
        // 调用父类的onCreate方法,确保Activity正确初始化
        super.onCreate(savedInstanceState)
        // 启用Edge-to-Edge模式,使内容延伸到屏幕边缘
        enableEdgeToEdge()
        AlarmStateManager.initialize(this) // 初始化状态管理器
        // 设置应用程序的内容
        setContent {
            // 应用自定义的主题 MyClockTheme
            MyClockTheme {
                // 使用 MaterialTheme 的背景颜色创建一个 Surface
                Surface(color = MaterialTheme.colorScheme.background) {
                    // 创建并显示 AlarmScreen 组件
                    // 传递 applicationContext 作为上下文
                    // 传递一个 lambda 表达式作为 onDismissAlarm 参数,当闹钟被解除时调用 AlarmStateManager.deactivateAlarm()
                    AlarmScreen(
                        context = applicationContext,
                        onDismissAlarm = { AlarmStateManager.deactivateAlarm() }
                    )
                }
            }
        }
    }

}

@Composable
// 定义一个名为 AlarmScreen 的函数,接收一个 Context 对象和一个无参数的回调函数 onDismissAlarm
fun AlarmScreen(context: Context, onDismissAlarm: () -> Unit) {
    // 使用 remember 和 mutableStateOf 创建一个可变状态变量 timeInput,用于存储用户输入的时间
    var timeInput by remember { mutableStateOf("") }

    // 使用 Column 布局,使其子视图在垂直方向上居中对齐,并在水平方向上居中对齐
    Column(
        modifier = Modifier.fillMaxSize(), // 填满整个屏幕
        verticalArrangement = Arrangement.Center, // 垂直方向居中对齐
        horizontalAlignment = Alignment.CenterHorizontally // 水平方向居中对齐
    ) {
        // 显示提示文本,要求用户输入时间
        Text(text = "请输入时间 (HH:mm):")
        // 创建一个 TextField,用于用户输入时间
        TextField(
            value = timeInput, // 绑定到 timeInput 状态变量
            onValueChange = { timeInput = it }, // 当输入值改变时,更新 timeInput 状态变量
            label = { Text("输入时间") } // 设置输入框的标签
        )

        // 创建一个按钮,点击时设置闹钟
        Button(onClick = {
            // 将输入的时间字符串按 ":" 分割成小时和分钟
            val timeParts = timeInput.split(":")
            // 检查分割后的数组长度是否为 2
            if (timeParts.size == 2) {
                // 尝试将小时和分钟转换为整数
                val hour = timeParts[0].toIntOrNull()
                val minute = timeParts[1].toIntOrNull()
                // 检查转换结果是否为非空
                if (hour != null && minute != null) {
                    // 调用 setAlarm 函数设置闹钟
                    setAlarm(context, hour, minute)
                    // 显示一个 Toast 提示用户闹钟已设置
                    Toast.makeText(context, "闹钟已设置", Toast.LENGTH_SHORT).show()
                }
            }

        }) {
            // 按钮上显示的文本
            Text("设置闹钟")
        }
        // 根据全局状态显示提示和按钮
        Button(onClick = onDismissAlarm) {
            Text("停止")
        }
    }
}
// 定义一个函数setAlarm,用于设置闹钟
fun setAlarm(context: Context, hour: Int, minute: Int) {
    // 获取系统的闹钟管理服务
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    // 创建一个Intent,指向AlarmReceiver类,用于接收闹钟广播
    val intent = Intent(context, AlarmReceiver::class.java)
    // 创建一个PendingIntent,用于延迟执行Intent
    val pendingIntent = PendingIntent.getBroadcast(
        context,
        0,
        intent,
        // PendingIntent.FLAG_IMMUTABLE表示PendingIntent不可变,FLAG_UPDATE_CURRENT表示更新当前已有的PendingIntent
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )
    // 获取当前时间的Calendar实例,并设置小时和分钟
    val calendar = Calendar.getInstance().apply {
        set(Calendar.HOUR_OF_DAY, hour)
        set(Calendar.MINUTE, minute)
        set(Calendar.SECOND, 0)
    }
    // 设置闹钟,使用RTC_WAKEUP类型,在指定时间唤醒设备并执行pendingIntent
    alarmManager.setExact(
        AlarmManager.RTC_WAKEUP,
        calendar.timeInMillis,
        pendingIntent
    )
}

class AlarmReceiver : BroadcastReceiver() {
    // 重写BroadcastReceiver的onReceive方法,当接收到广播时调用
    override fun onReceive(context: Context, intent: Intent) {
        // 播放系统铃声
        AlarmStateManager.activateAlarm() // 激活状态并播放铃声

    }
}
复制代码
AndroidManifest.xml文件如下:
复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">



    <!-- 声明应用需要使用精确闹钟的权限 -->
    <uses-permission android:name="android.permission.USE_EXACT_ALARM" />


    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyClock"
        tools:targetApi="31">

        <!-- 定义一个接收器(Receiver)组件 -->
        <!--
            android:name=".AlarmReceiver"
            - 指定接收器的类名为 AlarmReceiver,这里的 "." 表示当前包名下的类。
            - AlarmReceiver 是一个继承自 BroadcastReceiver 的类,用于接收和处理广播消息。
         -->
        <receiver android:name=".AlarmReceiver" />

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyClock">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
相关推荐
二流小码农1 小时前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少2 小时前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker2 小时前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋2 小时前
Android 协程时代,Handler 应该退休了吗?
android
火柴就是我16 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
FunnySaltyFish19 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
砖厂小工1 天前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心1 天前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心1 天前
Android 17 来了!新特性介绍与适配建议
android·前端
Kapaseker1 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin