Android Kotlin 桌面端功能示例
Android Kotlin 桌面端功能示例,包括文件对话框、消息框和系统托盘
💻 消息框 kotlin
🟢 simple
⭐⭐⭐
显示警告对话框、确认提示、Toast消息和Snackbar
⏱️ 20 min
🏷️ kotlin, android, ui, dialogs
Prerequisites:
Basic Kotlin, Android SDK
// Android Kotlin Message Box Examples
// Using AlertDialog, Toast, and Snackbar
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.widget.EditText
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity
import android.view.LayoutInflater
import android.view.View
// 1. Alert Dialogs
class AlertDialogHelper(private val activity: AppCompatActivity) {
// Simple alert dialog
fun showAlert(title: String, message: String) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("OK") { dialog, which ->
println("OK clicked")
}
.show()
}
// Alert with callback
fun showAlertWithCallback(
title: String,
message: String,
onOkClicked: () -> Unit
) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("OK") { dialog, which ->
onOkClicked()
}
.show()
}
// Confirmation dialog
fun showConfirmDialog(
title: String,
message: String,
onConfirm: () -> Unit,
onCancel: () -> Unit = {}
) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Yes") { dialog, which ->
onConfirm()
}
.setNegativeButton("No") { dialog, which ->
onCancel()
}
.show()
}
// Dialog with icon
fun showAlertWithIcon(
title: String,
message: String,
iconId: Int
) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setIcon(iconId)
.setPositiveButton("OK", null)
.show()
}
// Dialog with neutral button
fun showThreeButtonDialog(
title: String,
message: String,
onPositive: () -> Unit = {},
onNegative: () -> Unit = {},
onNeutral: () -> Unit = {}
) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Yes") { _, _ -> onPositive() }
.setNegativeButton("No") { _, _ -> onNegative() }
.setNeutralButton("Cancel") { _, _ -> onNeutral() }
.show()
}
}
// 2. Input Dialogs
class InputDialogHelper(private val activity: AppCompatActivity) {
// Single input dialog
fun showInputDialog(
title: String,
hint: String,
onConfirm: (String) -> Unit
) {
val input = EditText(activity)
input.hint = hint
AlertDialog.Builder(activity)
.setTitle(title)
.setView(input)
.setPositiveButton("OK") { dialog, which ->
val text = input.text.toString()
onConfirm(text)
}
.setNegativeButton("Cancel", null)
.show()
}
// Multi-input dialog
fun showMultiInputDialog(
title: String,
fields: List<InputField>,
onConfirm: (Map<String, String>) -> Unit
) {
val container = activity.layoutInflater
.inflate(android.R.layout.select_dialog_multiselect, null) as ViewGroup
val inputs = mutableMapOf<String, EditText>()
for (field in fields) {
val layout = activity.layoutInflater.inflate(
android.R.layout.simple_list_item_1,
container,
false
) as ViewGroup
val label = EditText(activity)
label.hint = field.hint
label.inputType = field.inputType
container.addView(label)
inputs[field.key] = label
}
AlertDialog.Builder(activity)
.setTitle(title)
.setView(container)
.setPositiveButton("OK") { _, _ ->
val values = inputs.mapValues { it.text.toString() }
onConfirm(values)
}
.setNegativeButton("Cancel", null)
.show()
}
// Password input dialog
fun showPasswordDialog(
title: String,
hint: String = "Enter password",
onConfirm: (String) -> Unit
) {
val input = EditText(activity)
input.hint = hint
input.inputType = android.text.InputType.TYPE_CLASS_TEXT or
android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
AlertDialog.Builder(activity)
.setTitle(title)
.setView(input)
.setPositiveButton("OK") { _, _ ->
onConfirm(input.text.toString())
}
.setNegativeButton("Cancel", null)
.show()
}
// Data class for input field
data class InputField(
val key: String,
val hint: String,
val inputType: Int = android.text.InputType.TYPE_CLASS_TEXT
)
}
import android.view.ViewGroup
// 3. List Dialogs
class ListDialogHelper(private val activity: AppCompatActivity) {
// Single choice list
fun showSingleChoiceList(
title: String,
items: List<String>,
onSelected: (Int) -> Unit
) {
var selectedItem = 0
AlertDialog.Builder(activity)
.setTitle(title)
.setSingleChoiceItems(items.toTypedArray(), selectedItem) { dialog, which ->
selectedItem = which
}
.setPositiveButton("OK") { dialog, which ->
onSelected(selectedItem)
}
.setNegativeButton("Cancel", null)
.show()
}
// Multi-choice list
fun showMultiChoiceList(
title: String,
items: List<String>,
onSelected: (Set<Int>) -> Unit
) {
val selectedItems = mutableSetOf<Int>()
AlertDialog.Builder(activity)
.setTitle(title)
.setMultiChoiceItems(items.toTypedArray(), null) { dialog, which, isChecked ->
if (isChecked) {
selectedItems.add(which)
} else {
selectedItems.remove(which)
}
}
.setPositiveButton("OK") { dialog, which ->
onSelected(selectedItems)
}
.setNegativeButton("Cancel", null)
.show()
}
// Simple list dialog
fun showListDialog(
title: String,
items: List<String>,
onSelected: (String) -> Unit
) {
AlertDialog.Builder(activity)
.setTitle(title)
.setItems(items.toTypedArray()) { dialog, which ->
onSelected(items[which])
}
.show()
}
}
// 4. Toast Messages
class ToastHelper(private val context: Context) {
// Short toast
fun showShort(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
// Long toast
fun showLong(message: String) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show()
}
// Custom duration toast
fun showCustom(message: String, durationMs: Int) {
val toast = Toast.makeText(context, message, Toast.LENGTH_SHORT)
toast.duration = durationMs
toast.show()
}
// Toast with position
fun showWithPosition(
message: String,
gravity: Int,
xOffset: Int,
yOffset: Int
) {
val toast = Toast.makeText(context, message, Toast.LENGTH_SHORT)
toast.setGravity(gravity, xOffset, yOffset)
toast.show()
}
// Custom view toast
fun showCustomView(message: String) {
val inflater = LayoutInflater.from(context)
val layout = inflater.inflate(android.R.layout.simple_list_item_1, null)
// Assuming there's a TextView in the layout
// val textView = layout.findViewById<TextView>(android.R.id.text1)
// textView.text = message
val toast = Toast(context)
toast.view = layout
toast.duration = Toast.LENGTH_SHORT
toast.show()
}
}
// 5. Snackbar
class SnackbarHelper {
// Show simple snackbar
fun show(view: View, message: String) {
Snackbar.make(view, message, Snackbar.LENGTH_SHORT).show()
}
// Show long snackbar
fun showLong(view: View, message: String) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show()
}
// Snackbar with action
fun showWithAction(
view: View,
message: String,
actionText: String,
onActionClicked: () -> Unit
) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG)
.setAction(actionText) {
onActionClicked()
}
.show()
}
// Indefinite snackbar
fun showIndefinite(view: View, message: String) {
Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
.setAction("Dismiss") { }
.show()
}
// Custom snackbar with callback
fun showWithCallback(
view: View,
message: String,
onDismissed: () -> Unit
) {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT)
snackbar.addCallback(object : Snackbar.Callback() {
override fun onDismissed(transientBottomBar: Snackbar, event: Int) {
onDismissed()
}
})
snackbar.show()
}
// Snackbar with custom styling
fun showStyled(view: View, message: String, backgroundColor: Int) {
val snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT)
snackbar.view.setBackgroundColor(backgroundColor)
snackbar.show()
}
}
// 6. Progress Dialogs
class ProgressDialogHelper(private val activity: AppCompatActivity) {
// Simple progress dialog
fun showProgressDialog(title: String, message: String): AlertDialog {
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
val dialogView = inflater.inflate(android.R.layout.select_dialog_item, null)
builder.setTitle(title)
.setMessage(message)
.setView(dialogView)
.setCancelable(false)
val dialog = builder.create()
dialog.show()
return dialog
}
// Indeterminate progress
fun showIndeterminateProgress(title: String, message: String): AlertDialog {
return showProgressDialog(title, message)
}
// Horizontal progress dialog
fun showHorizontalProgress(
title: String,
message: String,
max: Int
): AlertDialog {
val builder = AlertDialog.Builder(activity)
val progressBar = android.widget.ProgressBar(activity, null, android.R.attr.progressBarStyleHorizontal)
progressBar.max = max
builder.setTitle(title)
.setView(progressBar)
.setCancelable(false)
val dialog = builder.create()
dialog.show()
return dialog
}
}
// Main demonstration
fun demonstrateMessageBoxes(activity: AppCompatActivity) {
println("=== Android Kotlin Message Box Examples ===\n")
// 1. Alert dialogs
println("--- 1. Alert Dialogs ---")
val alertDialogHelper = AlertDialogHelper(activity)
alertDialogHelper.showAlert("Information", "This is an alert dialog")
alertDialogHelper.showAlertWithCallback("Notice", "Operation completed") {
println("Callback executed")
}
alertDialogHelper.showConfirmDialog(
"Confirm",
"Are you sure you want to continue?",
onConfirm = { println("User confirmed") },
onCancel = { println("User cancelled") }
)
// 2. Input dialogs
println("\n--- 2. Input Dialogs ---")
val inputDialogHelper = InputDialogHelper(activity)
inputDialogHelper.showInputDialog("Enter Name", "Name") { name ->
println("Entered name: $name")
}
inputDialogHelper.showPasswordDialog("Password") { password ->
println("Password entered: ${password.length} chars")
}
// 3. List dialogs
println("\n--- 3. List Dialogs ---")
val listDialogHelper = ListDialogHelper(activity)
val items = listOf("Option 1", "Option 2", "Option 3", "Option 4")
listDialogHelper.showSingleChoiceList("Choose an option", items) { index ->
println("Selected: ${items[index]}")
}
listDialogHelper.showListDialog("Select item", items) { item ->
println("Selected: $item")
}
// 4. Toast messages
println("\n--- 4. Toast Messages ---")
val toastHelper = ToastHelper(activity)
toastHelper.showShort("Short toast message")
toastHelper.showLong("Long toast message")
toastHelper.showWithPosition(
"Positioned toast",
android.view.Gravity.BOTTOM or android.view.Gravity.END,
0,
100
)
// 5. Snackbar
println("\n--- 5. Snackbar ---")
// Note: Snackbar requires a View as anchor
println("Snackbar examples:")
println(" - show(): Simple snackbar")
println(" - showLong(): Long duration snackbar")
println(" - showWithAction(): With action button")
println(" - showIndefinite(): Until dismissed")
println(" - showStyled(): Custom styling")
// 6. Progress dialogs
println("\n--- 6. Progress Dialogs ---")
val progressDialogHelper = ProgressDialogHelper(activity)
val progressDialog = progressDialogHelper.showIndeterminateProgress(
"Loading",
"Please wait..."
)
// Simulate loading
activity.runOnUiThread {
// In real app, dismiss when operation completes
// progressDialog.dismiss()
}
println("\n=== All Message Box Examples Completed ===")
}
💻 文件对话框 kotlin
🟡 intermediate
⭐⭐⭐⭐
使用存储访问框架打开文件选择器、保存文件对话框和目录选择
⏱️ 30 min
🏷️ kotlin, android, ui, dialogs
Prerequisites:
Intermediate Kotlin, Android SDK
// Android Kotlin File Dialog Examples
// Using Storage Access Framework and Intent
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import android.content.ContentResolver
import android.database.Cursor
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity
// 1. Open File Dialog
class OpenFileDialogHelper(private val activity: AppCompatActivity) {
// Create open file launcher
private val openFileLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.also { uri ->
handleFileSelected(uri)
}
}
}
// Open single file
fun openFile(mimeType: String = "*/*") {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = mimeType
putExtra(Intent.EXTRA_TITLE, "Select a file")
}
openFileLauncher.launch(intent)
}
// Open multiple files
private val openMultipleFilesLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.let { intent ->
val clipData = intent.clipData
if (clipData != null) {
for (i in 0 until clipData.itemCount) {
val uri = clipData.getItemAt(i).uri
handleFileSelected(uri)
}
} else {
intent.data?.also { uri ->
handleFileSelected(uri)
}
}
}
}
}
fun openMultipleFiles(mimeType: String = "*/*") {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = mimeType
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
openMultipleFilesLauncher.launch(intent)
}
// Open specific file types
fun openImage() {
openFile("image/*")
}
fun openPdf() {
openFile("application/pdf")
}
fun openTextFile() {
openFile("text/*")
}
// Handle selected file
private fun handleFileSelected(uri: Uri) {
val fileName = getFileName(uri)
val fileSize = getFileSize(uri)
println("File selected: $fileName")
println("Size: $fileSize bytes")
println("URI: $uri")
// Read file content
val content = readFileContent(uri)
println("Content preview: ${content?.take(100)}...")
}
// Get file name
private fun getFileName(uri: Uri): String? {
var result: String? = null
if (uri.scheme == "content") {
val cursor: Cursor? = activity.contentResolver.query(
uri,
null,
null,
null,
null
)
cursor?.use {
if (it.moveToFirst()) {
val index = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (index >= 0) {
result = it.getString(index)
}
}
}
}
if (result == null) {
result = uri.path
val cut = result?.lastIndexOf('/')
if (cut != -1) {
result = result?.substring(cut!! + 1)
}
}
return result
}
// Get file size
private fun getFileSize(uri: Uri): Long {
val cursor: Cursor? = activity.contentResolver.query(
uri,
arrayOf(OpenableColumns.SIZE),
null,
null,
null
)
cursor?.use {
if (it.moveToFirst()) {
val sizeIndex = it.getColumnIndex(OpenableColumns.SIZE)
if (sizeIndex >= 0 && !it.isNull(sizeIndex)) {
return it.getLong(sizeIndex)
}
}
}
return 0L
}
// Read file content
private fun readFileContent(uri: Uri): String? {
return try {
activity.contentResolver.openInputStream(uri)?.bufferedReader()?.use { it.readText() }
} catch (e: Exception) {
println("Error reading file: ${e.message}")
null
}
}
}
// 2. Save File Dialog
class SaveFileDialogHelper(private val activity: AppCompatActivity) {
private val saveFileLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.also { uri ->
handleFileSaved(uri)
}
}
}
// Create new file
fun createFile(fileName: String, mimeType: String = "text/plain") {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = mimeType
putExtra(Intent.EXTRA_TITLE, fileName)
}
saveFileLauncher.launch(intent)
}
// Save text content
fun saveTextFile(fileName: String, content: String) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, fileName)
}
saveFileLauncher.launch(Intent.createChooser(intent, "Save file"))
}
// Save image
fun saveImageFile(fileName: String) {
createFile(fileName, "image/png")
}
// Handle file saved
private fun handleFileSaved(uri: Uri) {
// Take persistable permission
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
activity.contentResolver.takePersistableUriPermission(uri, takeFlags)
println("File saved: $uri")
// Write content to file
try {
activity.contentResolver.openOutputStream(uri)?.use { outputStream ->
val content = "Sample content written at ${java.util.Date()}"
outputStream.write(content.toByteArray())
}
println("Content written successfully")
} catch (e: Exception) {
println("Error writing file: ${e.message}")
}
}
// Write bytes to file
fun writeToFile(uri: Uri, data: ByteArray): Boolean {
return try {
activity.contentResolver.openOutputStream(uri)?.use { outputStream ->
outputStream.write(data)
outputStream.flush()
}
true
} catch (e: Exception) {
println("Error writing: ${e.message}")
false
}
}
}
// 3. Directory Selection
class DirectoryPickerHelper(private val activity: AppCompatActivity) {
private val directoryPickerLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.also { uri ->
handleDirectorySelected(uri)
}
}
}
// Open directory picker
fun openDirectory() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
putExtra(DocumentsContract.EXTRA_INITIAL_URI, null)
}
directoryPickerLauncher.launch(intent)
}
// Handle directory selected
private fun handleDirectorySelected(uri: Uri) {
// Take persistable permission
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
activity.contentResolver.takePersistableUriPermission(uri, takeFlags)
println("Directory selected: $uri")
// List files in directory
listFilesInDirectory(uri)
}
// List files in directory
private fun listFilesInDirectory(directoryUri: Uri) {
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
directoryUri,
DocumentsContract.getDocumentId(directoryUri)
)
val cursor: Cursor? = activity.contentResolver.query(
childrenUri,
arrayOf(
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE
),
null,
null,
null
)
cursor?.use {
println("Files in directory:")
while (it.moveToNext()) {
val nameIndex = it.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
val mimeIndex = it.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE)
val name = it.getString(nameIndex)
val mimeType = it.getString(mimeIndex)
println(" - $name ($mimeType)")
}
}
}
// Create subdirectory
fun createSubdirectory(parentUri: Uri, directoryName: String): Uri? {
return try {
val documentUri = DocumentsContract.createDocument(
activity.contentResolver,
parentUri,
DocumentsContract.Document.MIME_TYPE_DIR,
directoryName
)
documentUri
} catch (e: Exception) {
println("Error creating directory: ${e.message}")
null
}
}
}
// 4. Advanced File Operations
class AdvancedFileOperations(private val activity: AppCompatActivity) {
// Open file with custom filter
fun openWithFilter(mimeTypes: List<String>) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = if (mimeTypes.size == 1) mimeTypes[0] else "*/*"
if (mimeTypes.size > 1) {
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
}
}
activity.startActivityForResult(intent, 1001)
}
// Get file metadata
fun getFileMetadata(uri: Uri): FileMetadata? {
val cursor: Cursor? = activity.contentResolver.query(
uri,
arrayOf(
OpenableColumns.DISPLAY_NAME,
OpenableColumns.SIZE,
OpenableColumns.MIME_TYPE
),
null,
null,
null
)
return cursor?.use {
if (it.moveToFirst()) {
val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
val sizeIndex = it.getColumnIndex(OpenableColumns.SIZE)
val mimeIndex = it.getColumnIndex(OpenableColumns.MIME_TYPE)
FileMetadata(
name = it.getString(nameIndex),
size = it.getLong(sizeIndex),
mimeType = it.getString(mimeIndex)
)
} else {
null
}
}
}
// File metadata data class
data class FileMetadata(
val name: String,
val size: Long,
val mimeType: String
)
}
// 5. Recent Documents
class RecentDocuments(private val activity: AppCompatActivity) {
// Get recent files
fun getRecentDocuments(): List<Uri> {
val recentUris = mutableListOf<Uri>()
// Query recent documents
val projection = arrayOf(
OpenableColumns.DISPLAY_NAME,
OpenableColumns.DATE_MODIFIED
)
val cursor: Cursor? = activity.contentResolver.query(
android.provider.MediaStore.Files.getContentUri("external"),
projection,
null,
null,
"${OpenableColumns.DATE_MODIFIED} DESC LIMIT 10"
)
cursor?.use {
val idIndex = it.getColumnIndex(android.provider.BaseColumns._ID)
while (it.moveToNext()) {
val id = it.getLong(idIndex)
val contentUri = android.provider.MediaStore.Files.getContentUri("external", id)
recentUris.add(contentUri)
}
}
return recentUris
}
}
// Main demonstration
fun demonstrateFileDialogs(activity: AppCompatActivity) {
println("=== Android Kotlin File Dialog Examples ===\n")
// 1. Open file dialog
println("--- 1. Open File Dialog ---")
val openDialogHelper = OpenFileDialogHelper(activity)
println("Opening file dialog...")
openDialogHelper.openFile()
println("\nOpening image picker...")
openDialogHelper.openImage()
println("\nOpening multiple files...")
openDialogHelper.openMultipleFiles()
// 2. Save file dialog
println("\n--- 2. Save File Dialog ---")
val saveDialogHelper = SaveFileDialogHelper(activity)
println("Creating new file...")
saveDialogHelper.createFile("example.txt", "text/plain")
println("\nSaving text file...")
saveDialogHelper.saveTextFile("note.txt", "Sample content")
// 3. Directory picker
println("\n--- 3. Directory Picker ---")
val directoryPicker = DirectoryPickerHelper(activity)
println("Opening directory picker...")
directoryPicker.openDirectory()
// 4. Advanced operations
println("\n--- 4. Advanced Operations ---")
val advancedOps = AdvancedFileOperations(activity)
println("Opening with multiple MIME type filters...")
advancedOps.openWithFilter(listOf("image/*", "video/*", "audio/*"))
// 5. Recent documents
println("\n--- 5. Recent Documents ---")
val recentDocs = RecentDocuments(activity)
println("Getting recent documents...")
val recent = recentDocs.getRecentDocuments()
println("Found ${recent.size} recent documents")
println("\n=== All File Dialog Examples Completed ===")
}
💻 系统托盘(通知) kotlin
🟡 intermediate
⭐⭐⭐⭐
在系统托盘/状态栏显示具有各种选项的通知
⏱️ 30 min
🏷️ kotlin, android, notifications, ui
Prerequisites:
Intermediate Kotlin, Android SDK
// Android Kotlin Notification Examples
// Using NotificationManager and NotificationCompat
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import android.graphics.BitmapFactory
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
// 1. Basic Notification
class BasicNotificationHelper(private val context: Context) {
companion object {
private const val CHANNEL_ID = "basic_channel"
private const val NOTIFICATION_ID = 1001
}
private val notificationManager = NotificationManagerCompat.from(context)
init {
createNotificationChannel()
}
// Create notification channel (required for Android O+)
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Basic Notifications",
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = "Basic notification channel"
enableLights(true)
lightColor = Color.BLUE
}
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
}
// Show simple notification
fun showNotification(title: String, message: String) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Show notification with large text
fun showNotificationWithLargeText(title: String, message: String) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(message)
)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
}
// 2. Notification with Actions
class ActionNotificationHelper(private val context: Context) {
companion object {
private const val CHANNEL_ID = "action_channel"
private const val NOTIFICATION_ID = 1002
}
private val notificationManager = NotificationManagerCompat.from(context)
init {
createNotificationChannel()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Action Notifications",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Notifications with action buttons"
}
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
}
// Notification with action button
fun showNotificationWithAction(
title: String,
message: String,
actionText: String,
actionIntent: PendingIntent
) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.addAction(
android.R.drawable.ic_menu_send,
actionText,
actionIntent
)
.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Multiple actions
fun showNotificationWithMultipleActions(
title: String,
message: String,
actions: List<NotificationAction>
) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
actions.forEach { action ->
builder.addAction(action.icon, action.title, action.pendingIntent)
}
builder.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Data class for notification action
data class NotificationAction(
val icon: Int,
val title: String,
val pendingIntent: PendingIntent
)
}
// 3. Progress Notification
class ProgressNotificationHelper(private val context: Context) {
companion object {
private const val CHANNEL_ID = "progress_channel"
private const val NOTIFICATION_ID = 1003
}
private val notificationManager = NotificationManagerCompat.from(context)
init {
createNotificationChannel()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Progress Notifications",
NotificationManager.IMPORTANCE_LOW
)
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
}
// Show indeterminate progress
fun showIndeterminateProgress(title: String, message: String) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setProgress(0, 0, true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Show determinate progress
fun showProgress(title: String, message: String, max: Int, progress: Int) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setProgress(max, progress, false)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Update progress
fun updateProgress(max: Int, progress: Int, message: String? = null) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("Downloading")
.setContentText(message ?: "$progress%")
.setOngoing(true)
.setProgress(max, progress, false)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Complete progress
fun completeProgress(title: String, message: String) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setProgress(0, 0, false)
.setOngoing(false)
.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
}
// 4. Rich Notification with Image
class RichNotificationHelper(private val context: Context) {
companion object {
private const val CHANNEL_ID = "rich_channel"
private const val NOTIFICATION_ID = 1004
}
private val notificationManager = NotificationManagerCompat.from(context)
init {
createNotificationChannel()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Rich Notifications",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Rich notifications with images"
}
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
}
// Big picture notification
fun showBigPictureNotification(
title: String,
message: String,
imageBitmap: android.graphics.Bitmap
) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(message)
.setLargeIcon(imageBitmap)
.setStyle(
NotificationCompat.BigPictureStyle()
.bigPicture(imageBitmap)
.bigLargeIcon(null)
)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
// Inbox style notification
fun showInboxNotification(
title: String,
contentTitle: String,
lines: List<String>
) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(title)
.setContentText(contentTitle)
.setStyle(
NotificationCompat.InboxStyle()
.setSummaryText("${lines.size} new messages")
.addLine(lines[0])
)
.setPriority(NotificationCompat.PRIORITY_HIGH)
// Add remaining lines
for (i in 1 until minOf(lines.size, 5)) {
builder.setStyle(
NotificationCompat.InboxStyle()
.setSummaryText("${lines.size} new messages")
.addLine(lines[i])
)
}
builder.setAutoCancel(true)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
}
// 5. Notification Groups
class GroupNotificationHelper(private val context: Context) {
companion object {
private const val CHANNEL_ID = "group_channel"
private const val GROUP_ID = "notification_group"
private const val SUMMARY_ID = 1005
}
private val notificationManager = NotificationManagerCompat.from(context)
init {
createNotificationChannel()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Group Notifications",
NotificationManager.IMPORTANCE_HIGH
)
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
}
// Show grouped notifications
fun showGroupedNotifications(
notifications: List<GroupedNotification>
) {
// Show individual notifications
notifications.forEach { notification ->
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(notification.title)
.setContentText(notification.message)
.setGroup(GROUP_ID)
.setAutoCancel(true)
notificationManager.notify(notification.id, builder.build())
}
// Show summary notification
val summaryBuilder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("Group Summary")
.setContentText("${notifications.size} notifications")
.setGroup(GROUP_ID)
.setGroupSummary(true)
.setAutoCancel(true)
notificationManager.notify(SUMMARY_ID, summaryBuilder.build())
}
// Data class for grouped notification
data class GroupedNotification(
val id: Int,
val title: String,
val message: String
)
}
// 6. Notification Manager
class UnifiedNotificationManager(private val context: Context) {
private val basicHelper = BasicNotificationHelper(context)
private val actionHelper = ActionNotificationHelper(context)
private val progressHelper = ProgressNotificationHelper(context)
private val richHelper = RichNotificationHelper(context)
private val groupHelper = GroupNotificationHelper(context)
// Show simple notification
fun notify(title: String, message: String) {
basicHelper.showNotification(title, message)
}
// Show notification with action
fun notifyWithAction(
title: String,
message: String,
actionText: String,
actionIntent: PendingIntent
) {
actionHelper.showNotificationWithAction(title, message, actionText, actionIntent)
}
// Show progress
fun showProgress(title: String, message: String, max: Int, progress: Int) {
progressHelper.showProgress(title, message, max, progress)
}
// Cancel all notifications
fun cancelAll() {
val manager = NotificationManagerCompat.from(context)
manager.cancelAll()
}
// Cancel specific notification
fun cancel(id: Int) {
val manager = NotificationManagerCompat.from(context)
manager.cancel(id)
}
}
// Main demonstration
fun demonstrateSystemTray(context: Context, activity: AppCompatActivity) {
println("=== Android Kotlin Notification Examples ===\n")
// 1. Basic notifications
println("--- 1. Basic Notifications ---")
val basicHelper = BasicNotificationHelper(context)
basicHelper.showNotification(
"Basic Notification",
"This is a simple notification"
)
basicHelper.showNotificationWithLargeText(
"Long Notification",
"This is a notification with a very long message that " +
"should be displayed using the big text style in the notification drawer."
)
// 2. Action notifications
println("\n--- 2. Action Notifications ---")
val actionHelper = ActionNotificationHelper(context)
// Create pending intent
val intent = Intent(context, activity::class.java)
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
actionHelper.showNotificationWithAction(
"Action Notification",
"Click to perform action",
"Reply",
pendingIntent
)
// 3. Progress notifications
println("\n--- 3. Progress Notifications ---")
val progressHelper = ProgressNotificationHelper(context)
progressHelper.showIndeterminateProgress("Indeterminate", "Working...")
// Simulate progress
val max = 100
for (i in 0..100 step 10) {
progressHelper.updateProgress(max, i, "Download: $i%")
}
progressHelper.completeProgress("Complete", "Download finished!")
// 4. Rich notifications
println("\n--- 4. Rich Notifications ---")
val richHelper = RichNotificationHelper(context)
// Note: In real usage, you'd have actual bitmap
println("Rich notification types:")
println(" - Big picture style")
println(" - Inbox style")
println(" - Messaging style")
// 5. Group notifications
println("\n--- 5. Group Notifications ---")
val groupHelper = GroupNotificationHelper(context)
val notifications = listOf(
GroupNotificationHelper.GroupedNotification(
1,
"Message 1",
"First message"
),
GroupNotificationHelper.GroupedNotification(
2,
"Message 2",
"Second message"
),
GroupNotificationHelper.GroupedNotification(
3,
"Message 3",
"Third message"
)
)
groupHelper.showGroupedNotifications(notifications)
// 6. Unified manager
println("\n--- 6. Unified Notification Manager ---")
val unifiedManager = UnifiedNotificationManager(context)
println("Unified manager provides:")
println(" - notify(): Simple notifications")
println(" - notifyWithAction(): With action buttons")
println(" - showProgress(): Progress notifications")
println(" - cancelAll(): Cancel all notifications")
println("\n=== All Notification Examples Completed ===")
}