在 Android 開發中,良好的用戶反饋機制至關重要。Jetpack Compose 提供了現代化的 UI 構建方式,但提示信息(Toast/Snackbar)的管理往往顯得分散。本文將介紹如何優雅地封裝提示信息,提升代碼可維護性。
一、基礎封裝方案
1. 簡單 Snackbar 封裝
@Composable
fun SnackbarHelper(hostState: SnackbarHostState,message: String?,duration: SnackbarDuration = SnackbarDuration.Short,actionLabel: String? = null,onAction: (() -> Unit)? = null,onDismiss: () -> Unit = {}
) {val scope = rememberCoroutineScope()LaunchedEffect(message) {message?.let {hostState.showSnackbar(message = it,duration = duration,actionLabel = actionLabel).also { result ->if (result == SnackbarResult.ActionPerformed) {onAction?.invoke()}}onDismiss()}}
}
2. 簡單 Toast 封裝
@Composable
fun ToastHelper(message: String?) {val context = LocalContext.currentLaunchedEffect(message) {message?.let {Toast.makeText(context, it, Toast.LENGTH_SHORT).show()}}
}
二、進階狀態管理方案
1. 消息狀態模型
sealed class MessageState {object Idle : MessageState()data class Snackbar(val message: String,val duration: SnackbarDuration = SnackbarDuration.Short,val action: SnackbarAction? = null) : MessageState()data class Toast(val message: String) : MessageState()
}data class SnackbarAction(val label: String,val onClick: () -> Unit,val withDismiss: Boolean = true
)
2. 消息管理器 ViewModel
class MessageManager : ViewModel() {private val _messageState = mutableStateOf<MessageState>(MessageState.Idle)val messageState: State<MessageState> = _messageStatefun showSnackbar(message: String,duration: SnackbarDuration = SnackbarDuration.Short,action: SnackbarAction? = null) {_messageState.value = MessageState.Snackbar(message, duration, action)}fun showToast(message: String) {_messageState.value = MessageState.Toast(message)}fun reset() {_messageState.value = MessageState.Idle}
}
3. 消息處理器組件
@Composable
fun MessageHandler(state: MessageState,snackbarHostState: SnackbarHostState,onMessageShown: () -> Unit
) {val context = LocalContext.currentLaunchedEffect(state) {when (state) {is MessageState.Snackbar -> {val result = snackbarHostState.showSnackbar(message = state.message,duration = state.duration,actionLabel = state.action?.label)if (result == SnackbarResult.ActionPerformed) {state.action?.onClick?.invoke()}if (state.action?.withDismiss != false) {onMessageShown()}}is MessageState.Toast -> {Toast.makeText(context, state.message, Toast.LENGTH_SHORT).show()onMessageShown()}MessageState.Idle -> Unit}}
}
三、完整使用示例
1. 項目級封裝
@Composable
fun MessageScaffold(messageManager: MessageManager = viewModel(),content: @Composable (PaddingValues) -> Unit
) {val snackbarHostState = remember { SnackbarHostState() }Scaffold(snackbarHost = { SnackbarHost(hostState = snackbarHostState) }) { padding ->Box(modifier = Modifier.fillMaxSize()) {content(padding)MessageHandler(state = messageManager.messageState.value,snackbarHostState = snackbarHostState,onMessageShown = { messageManager.reset() })}}
}
2. 具體頁面使用
@Composable
fun ProductDetailScreen() {val messageManager: MessageManager = viewModel()MessageScaffold {Column(modifier = Modifier.padding(it)) {// 頁面內容...Button(onClick = {messageManager.showSnackbar(message = "商品已加入購物車",action = SnackbarAction(label = "查看",onClick = { /* 導航到購物車 */ }))}) {Text("加入購物車")}Button(onClick = {messageManager.showToast("收藏成功")}) {Text("收藏商品")}}}
}
四、高級功能擴展
1. 消息隊列支持
class QueuedMessageManager : ViewModel() {private val _messageQueue = mutableStateListOf<MessageState>()private var isProcessing = falsefun sendMessage(message: MessageState) {_messageQueue.add(message)processNext()}private fun processNext() {if (isProcessing || _messageQueue.isEmpty()) returnisProcessing = true_currentMessage.value = _messageQueue.removeAt(0)}fun onMessageProcessed() {isProcessing = falseprocessNext()}// 其余實現與基礎MessageManager類似...
}
2. 自動消失控制
fun MessageManager.showTimedSnackbar(message: String,durationMillis: Long = 3000,action: SnackbarAction? = null
) {showSnackbar(message = message,duration = if (action != null) SnackbarDuration.Indefinite else SnackbarDuration.Short,action = action)if (action == null) {viewModelScope.launch {delay(durationMillis)reset()}}
}
五、最佳實踐建議
- 統一入口:所有提示信息通過同一管理器發出
- 狀態集中:使用密封類管理所有提示狀態
- 自動清理:消息顯示后自動重置狀態
- 可擴展性:設計應考慮未來可能的消息類型擴展
- 測試友好:ViewModel 應易于單元測試
六、總結
通過這種封裝方式,我們獲得了:
- 統一的提示信息管理入口
- 類型安全的提示狀態處理
- 良好的代碼組織和可維護性
- 易于擴展的新消息類型支持
- 簡化的業務代碼調用方式
這種模式特別適合中大型項目,能夠有效管理應用中的各種用戶反饋信息,使開發者可以更專注于核心業務邏輯的實現。