![](/img/trans.png)
[英]Refresh Data - Kotlin - Flows - MVVM - Jetpack Compose
[英]Jetpack Compose: Displaying data in compose using MVVM
需要一些帮助来解释为什么来自 viewmodel 的数据没有显示在可组合的 function MainContent 中。 我尝试将 MVVM 风格与协程一起使用,但没有 DI,我认为这会更容易,但不知何故,我无法让它工作。
视图模型正在工作,因为 log.d 正在显示来自服务器的正确数据,但不知何故,我无法让它显示在
文本(文本 = viewModel.posts[it].phrase)
任何帮助将不胜感激。 该程序的 github 链接位于https://github.com/somaria/LearnChnCompose
package com.gamecrawl.learnchncompose
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import com.gamecrawl.learnchncompose.ui.theme.LearnChnComposeTheme
import io.ktor.client.*
import io.ktor.client.engine.android.*
import io.ktor.client.features.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.features.logging.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel: MainViewModel by viewModels()
setContent {
LearnChnComposeTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
MainContent(viewModel)
}
}
}
}
}
class MainViewModel : ViewModel() {
private var _posts = mutableListOf(Post("12", "test phrase", true))
var posts get() = _posts; set(value) {
_posts = value
}
init {
CoroutineScope(Dispatchers.IO).launch {
_posts = KtorClient.httpClient.get("https://learnchn.herokuapp.com/") {
header("Content-Type", "application/json")
}
Log.d("HomeViewModel", "init: ${_posts[1].phrase}")
Log.d("HomeViewModel", "init: ${_posts[1].id}")
}
}
fun addPost(post: Post) {
CoroutineScope(Dispatchers.IO).launch {
val addedpost: Post = KtorClient.httpClient.post("https://learnchn.herokuapp.com/add") {
header("Content-Type", "application/json")
body = post
}
}
}
}
@Composable
fun MainContent(viewModel: MainViewModel) {
Column {
LazyColumn {
items(viewModel.posts.size) {
Text(text = viewModel.posts[it].phrase)
}
}
Button(onClick = {
viewModel.addPost(Post("test", "adding post 222", true))
}) {
Text(text = "Add Post")
}
}
}
@Serializable
data class Post(
val id: String,
val phrase: String,
val published: Boolean
)
object KtorClient {
val json = Json {
encodeDefaults = true
ignoreUnknownKeys = true
isLenient = true
}
val httpClient = HttpClient(Android) {
install(HttpTimeout) {
socketTimeoutMillis = 200000
requestTimeoutMillis = 200000
connectTimeoutMillis = 200000
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Log.d("TAG", "log: $message")
}
}
}
install(JsonFeature) {
serializer = KotlinxSerializer(json)
}
defaultRequest {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
}
}
}
posts
的数据类型是MutableList<Post>
。 这意味着对该变量的更改不会导致 function 重新组合。 加载 UI 时,变量没有任何数据,因为您在异步协程中获取数据。 但是,更新变量时,不会重新组合 UI。
要解决此问题,您必须将_posts
声明为来自 compose 库的MutableState<List<Post>>
。 通过以下方式重新配置您的 ViewModel:
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() {
private val _posts = mutableStateOf(listOf<Post>()) // <- requires init value
val posts: State<List<Post>> = _posts // <- keep both variables immutable 'val'
/* always expose the immutable form of State */
init {
CoroutineScope(Dispatchers.IO).launch {
/* _posts.value is used now due to the datatype change */
_posts.value = KtorClient.httpClient.get("https://learnchn.herokuapp.com/") {
header("Content-Type", "application/json")
}
Log.d("HomeViewModel", "init: ${_posts.value[1].phrase}")
Log.d("HomeViewModel", "init: ${_posts.value[1].id}")
}
}
fun addPost(post: Post) {
CoroutineScope(Dispatchers.IO).launch {
val addedpost: Post = KtorClient.httpClient.post("https://learnchn.herokuapp.com/add") {
header("Content-Type", "application/json")
body = post
}
}
}
}
现在,由于您的公共posts
变量是State<T>
类型,您需要对可组合的 function 进行更改:
@Composable
fun MainContent(viewModel: MainViewModel) {
val posts = viewModel.posts.value // <- grab the value of the state variable.
/* The function will recompose whenever there's a change in posts */
Column {
LazyColumn {
items(posts.size) {
Text(text = posts[it].phrase)
}
}
Button(onClick = {
viewModel.addPost(Post("test", "adding post 222", true))
}) {
Text(text = "Add Post")
}
}
}
这应该对您的问题有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.