安卓jetpack compose学习笔记-状态基础学习

发布于:2025-06-24 ⋅ 阅读:(17) ⋅ 点赞:(0)

目录

一、MutableState可变状态

二、数据缓存

一)remember

1、介绍

2、代码示例

3、复杂的UI状态和逻辑

二)ViewModel

三)Http请求

三、总结


        在安卓应用中,除了UI之外,另一个关键就是数据的处理。对于Compose UI来说,数据的处理和UI的渲染是天然分开的。比如,使用remember()获取一个MutableState对象,这个数据变化之后,会有机制通知使用这个对象的Compose函数进行更新。

        如果自己创建了一个非MutableState对象,这个对象的改变,不会引发Compose UI的重组,表现在界面就是界面不会有任何反应。可以说,MutableState就是Compose中数据的“双向绑定”。

        如果是不变数据,直接在代码中进行声明即可。如果是可变数据,则必须使用MutableState。

一、MutableState可变状态

        MutableState是Compose中状态管理的基础,当MutableState.value被更新时,MutableState会通知使用过这个对象的Compose组件进行重组,即进行页面刷新。当MutableState.value是自定义对象时,对他更新需要一个对象整个更新,即MutableState.value=newValue不能使用MutableState.value.changeStauts这种改变状态的方法,不然不会通知Compose组件进行重组。

        MutableState通知Compose组件进行数据更新时一种单向流数据,即MutableState数据 => Compose组件。如果Compose组件想要更新MutableState,需要使用事件的方式对MutableState数据进行更新,即MutableState数据 <= Compose组件。

二、数据缓存

        可变数据的分层:

        1、Composition Scope,即组合作用域,也可以理解为Compose函数这一级的作用域。代表功能是remember函数。

        2、Activity/Fragment Scope,可以理解为页面容器这一级的作用域。代表功能是ViewModel。

        3、Application Scope,即应用作用域。可以将数据以文件、数据库、远程存储等方式进行存取,即可实现应用级别的数据作用域,或者也可以采用第三方框架。

一)remember

1、介绍

        remember是缓存机制。当Compose组件进行重组时会重新执行代码,所以MutableState需要被包含在remember()函数中,remember()可以保证Compose的多次重组中,使用的对象是同一个MutableState,而不是每次重组都重新创建一遍MutableState对象。

        remember缓存的状态可以跨重组存在,但是不能跨Activity或者跨进程存在。可以把remember创建的对象提到上级的Compose,进而在多个Compose中引用同一个缓存对象。

2、代码示例

        代码示例:

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview

@Composable
fun RememberCompose() {
    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.fillMaxSize()
    ) {
        var mutableState = remember { mutableStateOf("A") }
        Text(
            text = mutableState.value
        )
        Button(
            onClick = {
                mutableState.value = "A"
            }
        ) {
            Text("设置为A")
        }

        Button(
            onClick = {
                mutableState.value = "B"
            }
        ) {
            Text("设置为B")
        }
    }
}

@Preview
@Composable
fun PreRememberCompose() {
    RememberCompose()
}

        效果:

        备注:rememberSavable缓存的状态可以跨Activity或者跨进程存在,当手动退出应用时,缓存数据才会被清空。实现原理就是把数据以Bundle的形势进行保存。类似这类API就不详细介绍了,可以根据需求在网络上寻找对应的API。

3、复杂的UI状态和逻辑

        在Compose中定义一个remember对象的方式,适合少量UI状态,简单的UI逻辑。

        对于一些较为复杂的UI状态和逻辑,可以使用一个类将相关状态整合到一起,这个叫StateHolder。这种方式需要注意要对MutableState.value进行赋值更新,否则会无法更新Compose UI。

二)ViewModel

        viewModel()会从ViewModelStore中获取实例,如果没有实例,就创建一个并存入ViewModelStore。ViewModelStore可能是Activity或者Fragment。

        定义ViewModel的方式就是定义一个类,然后继承ViewModel。然后可以用viewModel()函数获取这个类的实例。

        代码:

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel

class CounterViewModel : ViewModel() {
    private val _counter = mutableStateOf(0)
    val counter: State<Int> = _counter

    fun increment() {
        _counter.value = _counter.value + 1
    }

    fun decrement() {
        if (_counter.value > 1) {
            _counter.value = _counter.value - 1
        }
    }
}

@Composable
fun CounterScreen() {
    var viewModel: CounterViewModel = viewModel()
    CounterComponent(
        viewModel.counter,
        viewModel::increment,
        viewModel::decrement
    )
}

@Composable
fun CounterComponent(x0: State<Int>, x1: () -> Unit, x2: () -> Unit) {
    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.fillMaxSize()
    ) {
        Text(
            text = "value: ${x0.value}"
        )
        Button(
            onClick = x1
        ) {
            Text("increment")
        }
        Button(
            onClick = x2
        ) {
            Text("decrement")
        }
    }
}

@Preview
@Composable
fun PreCounterScreen() {
    CounterScreen()
}

        效果:

三)Http请求

        首先定义Http的普通类定义:

import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.statement.bodyAsText
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json

suspend fun main() {
    println(Http.visit("http://www.bai1du.com"))
}

object Http {
    suspend fun visit(url: String): String {
        var httpClient = HttpClient(CIO) {
            install(ContentNegotiation) {
                json(Json {
                    prettyPrint = true
                    isLenient = true
                })
            }
            engine {
                requestTimeout = 15000
            }
        }

        var errorMessage = ""
        try {
            var response = httpClient.get(url) {
                headers {
                    append(
                        "User-Agent",
                        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
                    )
                }
            }

            if (response.status.value == 200) {
                val content = response.bodyAsText()
                println("百度首页内容长度: ${content.length}")
                var take = content.take(100)
                println("前100个字符: $take")
                return take
            } else {
                println("请求失败,状态码: ${response.status}")
                errorMessage += "请求失败,状态码: ${response.status}\n"
            }
        } catch (e: Exception) {
            println("visit $url error!")
            errorMessage += "message: ${e.message}\n"
            errorMessage += "toString: ${e.toString()}\n"
            e.printStackTrace()
        } finally {
            httpClient.close()
        }
        return "http get error!\n$errorMessage"
    }
}

        然后定义ViewModel:

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class UrlViewModel : ViewModel() {
    private val _urlData = mutableStateOf(UrlData())
    val urlData: State<UrlData> = _urlData

    data class UrlData(
        var url: String = "http://www.baidu.com",
        var content: String = ""
    )

    fun fetchData() {
        viewModelScope.launch {
            var url = _urlData.value.url

            _urlData.value = UrlData(
                url,
                Http.visit(url)
            )
        }
    }

    fun updateUrl(url: String){
        _urlData.value = UrlData(
            url,
            ""
        )
    }

    fun clearContent() {
        var url = _urlData.value.url

        _urlData.value = UrlData(
            url,
            ""
        )
    }
}

        最后编写UI代码:

import android.annotation.SuppressLint
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import cn.yy.httptest.ui.theme.HttpTestTheme

@SuppressLint("StateFlowValueCalledInComposition")
@Composable
private fun AppCompose() {

    var viewModel: UrlViewModel = viewModel()
    UrlView(
        viewModel.urlData,
        viewModel::updateUrl,
        viewModel::fetchData,
        viewModel::clearContent
    )
}

@Composable
fun UrlView(
    urlData: State<UrlViewModel.UrlData>,
    updateUrl: (String) -> Unit,
    fetchData: () -> Unit,
    clearContent: () -> Unit
) {
    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.fillMaxSize()
    ) {
        Text(
            text = "url: ${urlData.value.url}"
        )
        TextField(
            value = urlData.value.url,
            onValueChange = updateUrl
        )
        Button(
            onClick = {
                fetchData()
            }
        ) {
            Text("访问")
        }
        Button(
            onClick = {
                clearContent()
            }
        ) {
            Text("clear content")
        }
        Text(
            text = "content: ${urlData.value.content}"
        )
    }
}

@Preview(showBackground = true)
@Composable
fun AppComposePreview() {
    HttpTestTheme {
        AppCompose()
    }
}

        效果:

        这里是通过ViewModel的方式获取http网站数据,也可以用这种方式获取文件数据等。

三、总结

        Compose UI的数据管理有两个关键点:

        1、MutableState状态的保存及更新。

        2、remember、ViewModel等方式的不同范围的缓存机制。

        

        

        


网站公告

今日签到

点亮在社区的每一天
去签到