如何在Jetpack Compose中对接API并展示用户数据?
解决Jetpack Compose中从dummyjson获取用户数据并展示的问题
一、修正数据类与API接口的匹配问题
dummyjson的/users接口返回结构包含limit/skip/total/users字段,你生成的数据类ApiDataX需要与接口返回类型统一,重命名为Users:
// 替换原ApiDataX,确保和接口返回类型一致 data class Users( val limit: Int, val skip: Int, val total: Int, val users: List<User> )
二、修复API接口路径
Retrofit的baseUrl已包含末尾斜杠,接口注解中的路径不能加开头斜杠,否则会覆盖baseUrl:
interface UserService{ @GET("users/{id}") // 去掉开头的/ suspend fun getUser(@Path("id") id :Int): User @GET("users") // 去掉开头的/ suspend fun getAllUsers():Users }
三、完善ViewModel与Repository解耦
ViewModel不应直接调用API,通过Repository实现解耦,同时添加状态管理处理加载、成功、错误场景:
class UserViewModel( private val repository: UserRepository = UserRepository(UserApi.retrofitService) ) : ViewModel() { // 用StateFlow适配Compose的状态收集 private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading) val uiState: StateFlow<UserUiState> = _uiState.asStateFlow() init { fetchAllUsers() } private fun fetchAllUsers() { viewModelScope.launch { try { val usersResponse = repository.getAllUsers() _uiState.value = UserUiState.Success(usersResponse.users) } catch (e: Exception) { _uiState.value = UserUiState.Error(e.message ?: "加载失败") } } } } // 定义UI状态,覆盖全场景 sealed class UserUiState { object Loading : UserUiState() data class Success(val users: List<User>) : UserUiState() data class Error(val message: String) : UserUiState() }
四、集成ViewModel到Compose UI
修改Screen组件,根据ViewModel状态展示对应内容,并传递真实用户数据到Item:
@Composable fun Screen() { val viewModel: UserViewModel = viewModel() val uiState by viewModel.uiState.collectAsStateWithLifecycle() // 安全收集生命周期感知状态 Surface(modifier = Modifier.fillMaxSize()){ Column(verticalArrangement = Arrangement.spacedBy(10.dp)){ Spacer(Modifier.height(10.dp)) Head() when(uiState) { UserUiState.Loading -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } is UserUiState.Success -> { LazyColumn(modifier = Modifier.padding(15.dp, 10.dp)){ items((uiState as UserUiState.Success).users) { user -> Item(user = user) } } } is UserUiState.Error -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text(text = (uiState as UserUiState.Error).message, color = Color.Red) } } } } } }
五、更新Item组件展示真实数据
使用网络图片加载用户头像(需添加Coil依赖),并显示真实用户名:
// 先在build.gradle(Module)添加Coil依赖 // implementation("io.coil-kt:coil-compose:2.5.0") @Composable fun Item(user: User){ Box( modifier = Modifier .fillMaxWidth() .padding(10.dp) ){ Row( horizontalArrangement = Arrangement.spacedBy(50.dp), verticalAlignment = Alignment.CenterVertically ){ AsyncImage( model = user.image, contentDescription = "${user.firstName} ${user.lastName}", modifier = Modifier .size(75.dp) .clip(CircleShape), placeholder = painterResource(id = R.drawable.habesha_guy_11), error = painterResource(id = R.drawable.habesha_guy_11) ) Column(verticalArrangement = Arrangement.spacedBy(5.dp)){ Text( text = "${user.firstName} ${user.lastName}", fontFamily = FontFamily.SansSerif, fontWeight = FontWeight(500) ) Button( onClick = {}, contentPadding = PaddingValues(horizontal = 40.dp, vertical = 0.dp), modifier = Modifier .border(1.dp, Color.Red, RectangleShape) .height(30.dp), colors = buttonColors(backgroundColor = Color.Transparent), elevation = elevation( defaultElevation = 0.dp, pressedElevation = 0.dp, disabledElevation = 0.dp, hoveredElevation = 0.dp, focusedElevation = 0.dp ) ){ Text(text = "Follow", color = Color.Red) } } } } }
六、修复MainActivity
在setContent中调用Screen组件:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MemberListTheme { Screen() } } } }
七、添加网络权限
在AndroidManifest.xml中添加网络访问权限:
<uses-permission android:name="android.permission.INTERNET" />
内容的提问来源于stack exchange,提问作者Yimiy Yussef
相关产品推荐
相关产品推荐

