簡體   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