Android Compose 無網絡狀態界面處理全方案
引言
在移動應用開發中,網絡連接不穩定是常見場景。優雅地處理無網絡狀態能顯著提升用戶體驗。Jetpack Compose 提供了強大的工具來實現各種網絡狀態下的界面展示。本文將全面介紹在 Compose 中處理無網絡狀態的多種方案。
一、基礎網絡狀態檢測
1. 網絡狀態檢測工具類
class NetworkMonitor(private val context: Context) {val isOnline: Booleanget() {val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManagerval networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)return networkCapabilities?.let {it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)} ?: false}
}
2. 網絡狀態 ViewModel
class NetworkViewModel(private val networkMonitor: NetworkMonitor) : ViewModel() {private val _isOnline = mutableStateOf(networkMonitor.isOnline)val isOnline: State<Boolean> = _isOnlinefun checkNetworkStatus() {_isOnline.value = networkMonitor.isOnline}
}
二、簡單界面處理方案
1. 全屏覆蓋式
@Composable
fun SimpleScreen() {val isOnline by networkViewModel.isOnline.collectAsState()Box(modifier = Modifier.fillMaxSize()) {// 主內容MainContent()// 無網絡覆蓋層if (!isOnline) {Surface(color = MaterialTheme.colors.background.copy(alpha = 0.9f),modifier = Modifier.fillMaxSize()) {OfflineContent()}}}
}@Composable
fun OfflineContent() {Column(modifier = Modifier.fillMaxSize().padding(16.dp),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Icon(Icons.Filled.WifiOff, contentDescription = null, modifier = Modifier.size(64.dp))Spacer(Modifier.height(16.dp))Text("無網絡連接", style = MaterialTheme.typography.h5)Text("請檢查您的網絡設置", style = MaterialTheme.typography.body1)Spacer(Modifier.height(24.dp))Button(onClick = { /* 重試 */ }) {Text("重試連接")}}
}
三、復雜界面處理方案
1. 局部替換法
@Composable
fun ComplexScreen() {val isOnline by networkViewModel.isOnline.collectAsState()Scaffold(topBar = { AppBar() },bottomBar = { BottomBar() }) { padding ->Column(modifier = Modifier.padding(padding)) {// 不依賴網絡的部分LocalFeatures()// 依賴網絡的部分if (isOnline) {OnlineContent()} else {NetworkErrorCard(title = "網絡內容",message = "此部分需要網絡連接",onRetry = { /* 重試 */ })}// 更多內容...}}
}
2. 分區塊處理
@Composable
fun DashboardScreen() {val networkState by rememberNetworkState()LazyColumn {item { HeaderSection() }item { if (networkState.isOnline) {LiveDataSection()} else {OfflinePlaceholder(icon = Icons.Filled.Update,title = "實時數據")}}item { if (networkState.isOnline) {RecommendationsSection()} else {OfflinePlaceholder(icon = Icons.Filled.Star,title = "個性化推薦")}}}
}
3. 漸進式顯示
@Composable
fun NewsFeedScreen(viewModel: NewsViewModel = viewModel()) {val uiState by viewModel.uiState.collectAsState()when {uiState.isLoading -> FullScreenLoading()!uiState.isOnline && uiState.cachedItems.isEmpty() -> FullScreenError()!uiState.isOnline -> {Column {CachedNewsList(uiState.cachedItems)OfflineBanner()}}else -> NewsList(uiState.items)}
}
四、高級網絡狀態管理
1. 增強版網絡狀態管理器
class AdvancedNetworkMonitor(context: Context) {sealed class NetworkState {object Available : NetworkState()object Unavailable : NetworkState()data class Limited(val type: ConnectionType) : NetworkState()}enum class ConnectionType { WIFI, CELLULAR, ETHERNET, VPN, OTHER }private val _state = mutableStateOf<NetworkState>(NetworkState.Available)val state: State<NetworkState> = _stateinit {val cm = context.getSystemService(ConnectivityManager::class.java)val request = NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build()cm.registerNetworkCallback(request, object : ConnectivityManager.NetworkCallback() {override fun onAvailable(network: Network) {_state.value = NetworkState.Available}override fun onLost(network: Network) {_state.value = NetworkState.Unavailable}override fun onCapabilitiesChanged(network: Network,capabilities: NetworkCapabilities) {_state.value = when {capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkState.Limited(ConnectionType.WIFI)capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkState.Limited(ConnectionType.CELLULAR)capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> NetworkState.Limited(ConnectionType.VPN)else -> NetworkState.Available}}})}
}
2. 狀態監聽Composable
@Composable
fun rememberNetworkState(): State<NetworkState> {val context = LocalContext.currentval networkMonitor = remember { AdvancedNetworkMonitor(context) }return networkMonitor.state
}
五、UI組件庫
1. 離線占位符組件
@Composable
fun OfflinePlaceholder(title: String,message: String = "需要網絡連接",icon: ImageVector = Icons.Filled.CloudOff,onRetry: (() -> Unit)? = null
) {Card(elevation = 4.dp,modifier = Modifier.fillMaxWidth().padding(8.dp)) {Column(modifier = Modifier.padding(16.dp),horizontalAlignment = Alignment.CenterHorizontally) {Icon(imageVector = icon,contentDescription = null,tint = MaterialTheme.colors.error,modifier = Modifier.size(48.dp))Spacer(Modifier.height(8.dp))Text(title, style = MaterialTheme.typography.h6)Text(message, style = MaterialTheme.typography.body2)onRetry?.let {Spacer(Modifier.height(16.dp))Button(onClick = it) {Text("重試")}}}}
}
2. 頂部橫幅通知
@Composable
fun NetworkStatusBanner() {val networkState by rememberNetworkState()val showBanner = networkState is NetworkState.UnavailableAnimatedVisibility(visible = showBanner,enter = slideInVertically { -it },exit = slideOutVertically { -it }) {Surface(color = MaterialTheme.colors.error,modifier = Modifier.fillMaxWidth()) {Row(modifier = Modifier.padding(8.dp),verticalAlignment = Alignment.CenterVertically) {Icon(Icons.Filled.WifiOff, "離線", tint = MaterialTheme.colors.onError)Spacer(Modifier.width(8.dp))Text("離線模式 - 部分功能不可用", color = MaterialTheme.colors.onError)}}}
}
六、最佳實踐建議
- 分層處理:根據界面復雜度選擇全屏覆蓋或局部替換
- 緩存策略:盡可能顯示緩存內容并明確標注
- 明確反饋:讓用戶清楚知道當前是離線狀態
- 恢復途徑:提供明顯的重試或刷新選項
- 狀態細分:區分完全離線、弱網等不同狀態
- 視覺一致:保持離線UI與應用設計風格一致
- 性能考慮:避免不必要的網絡狀態監聽和重組
結語
在Compose中處理無網絡狀態需要綜合考慮用戶體驗、界面復雜度和技術實現。本文介紹的各種方案可以根據實際需求靈活組合使用。