繁体   English   中英

Jetpack Compose:使用 MVVM 在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM