Jetpack Compose(3) —— 状态管理

这篇具有很好参考价值的文章主要介绍了Jetpack Compose(3) —— 状态管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上一篇文章拿 TextField 组件举例时,提到了 State,即状态。本篇文章,即讲解 State 的相关改概念。

一、什么是状态

与其它声明式 UI 框架一样,Compose 的职责非常单纯,仅作为对数据状态的反应。如果数据状态没有改变,则 UI 永远不会自行改变。在 Compose 中,每一个组件都是一个被 @Composable 修饰的函数,其状态就是函数的参数,当参数不变,则函数的输出就不会变,唯一的参数决定唯一输出。反言之,如果要让界面发生变化,则需要改变界面的状态,然后 Composable 响应这种变化。
下面还是拿个例子来说,做一个简单的计数器,有一个显示计数的控件,一个增加的按钮,每点击一次,则技术计数器加 1 ,一个减少的按钮,每点击一次,计时器减 1。
假如我们用此前的 View 视图体系,来写这个方法。代码大概像下面这样:

class MainActivity : AppCompatActivity() {
    // ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        binding.incrementBtn.setOnClickListener {
            binding.tvCounter.text = "${Integer.valueOf(binding.tvCounter.text.toString()) + 1 }"
        }

        binding.decrementBtn.setOnClickListener {
            binding.tvCounter.text = "${Integer.valueOf(binding.tvCounter.text.toString()) - 1 }"
        }
    }
}

显然上面这个代码,计数逻辑和 UI 的耦合度就很高。稍微优化一下:

class MainActivity : AppCompatActivity() {
    // ...
    private var counter: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        binding.incrementBtn.setOnClickListener {
            counter++
            updateCounter()
        }

        binding.decrementBtn.setOnClickListener {
            counter--
            updateCounter()
        }
    }

    private fun updateCounter() {
        binding.tvCounter.text = "$counter"
    }
}

这个代码的改动主要在于,新增了 counter 用于计数,本质上属于一种 “状态上提”, 原本 TextView 内部的状态 “mText”, 上提到了 Activity 中,这样,即使更换了计数器的 UI, 计数逻辑依然可以复用。

但是当前的代码,仍然有一些问题,比如计数逻辑在 Activity 中,无法到其它页面进行复用,进一步使用 MVVM 结构进行改造。引入 ViewModel, 将状态从 Activity 中上提到 ViewModel 中。

class CounterViewModel: ViewModel() {
    private var _counter: MutableStateFlow<Int> = MutableStateFlow(0)
    val counter: StateFlow<Int> get() = _counter

    fun incrementCounter() {
        _counter.value++
    }

    fun decrementCounter() {
        _counter.value--
    }
}

class MainActivity : AppCompatActivity() {
    // ...
    private val viewModel: CounterViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        binding.incrementBtn.setOnClickListener {
            viewModel.incrementCounter()
        }

        binding.decrementBtn.setOnClickListener {
            viewModel.decrementCounter()
        }

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.counter.collect {
                    binding.tvCounter.text = $it
                }
            }
        }
    }
}

有 Jetpack 库使用经验的应该非常熟悉上面的代码,将状态上提到 ViewModel 中,使用 StateFlow 或者 LiveData 包装起来,在 Ativity 中监听状态的变化,从而自动刷新 UI。

下面,我们在 Compose 中实现上述计数器:

@Composable
fun CounterPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        var counter = 0
        Text(text = "$counter")
        Button(onClick = { counter++ }) {
            Text(text = "increment")
        }
        Button(onClick = { counter-- }) {
            Text(text = "decrement")
        }
    }
}

我们写出上面的代码,运行。

Jetpack Compose(3) —— 状态管理

结果发现,无论怎么点击,Text 显示的值总是 0 ,我们的计数逻辑没有生效。为了说明这个问题,现在增加一点日志:

@Composable
fun CounterPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        var counter = 0
        Log.d("sharpcj", "counter text --> $counter")
        Text(text = "$counter")
        Button(onClick = {
            Log.d("sharpcj", "increment button click ")
            counter++
        }) {
            Text(text = "increment")
        }
        Button(onClick = {
            Log.d("sharpcj", "decrement button click ")
            counter--
        }) {
            Text(text = "decrement")
        }
    }
}

再次运行,点击按钮,看到日志如下:

2024-03-12 21:39:27.530 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:39:30.859 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 21:39:31.309 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 21:39:31.468 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 21:39:31.762 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 21:39:31.927 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 21:39:32.661 21949-21949 sharpcj                 com.sharpcj.hellocompose             D  decrement button click 

我们重新捋一捋,Compose 的组件实际上就是一个个函数,Compose 刷新 UI 的逻辑是,状态发生变化,触发了重组,函数被重新调用,然后由于参数发生了变化,函数输出改变了,最终渲染出的的画面才会发生变化。
再看上面的代码,我们期望是定义 counter 作为了 Text 组件的状态,点击 Button,改变 counter, 到这里都没有问题,那么问题处在了哪里呢?问题主要是 counter 发生了变化,没有触发重组,即函数没有被重新调用,日志也证明了这一点。
回看我们上面传统 View 视图的写法,此前,我们改变了状态,需要主动调用 updateCounter 方法去刷新 UI, 后面经过改造,我们把状态提升到 ViewModel 中,不论是使用 StateFlow 还是使用 LiveData 包装后,我们都需要在 Activity 中监听状态的变化,才能对状态的变化做出响应。针对上面的例子,我们现在清楚了,计数器不生效原因在于 counter 改变后,Compose 没有感知到,没有触发重组。下面需要开始学习 Compose 中的状态了。

二、Compsoe 中的状态 State

2.1 State

如同传统试图中,需要使用 StateFlow 或者 LiveData 将状态变量包装成一个可观察类型的对象。Compose 中也提供了可观察的状态类型,可变状态类型 MutableState 和 不可变状态类型 State。我们需要使用 State/MutableState 将状态变量包装起来,这样即可触发重组。更为方便的是,声明式 UI 框架中,不需要我们显示注册监听状态变化,框架自动实现了这一订阅关系。我们来改写上面的代码:

@Composable
fun CounterPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        val counter: MutableState<Int> = mutableStateOf(0)
        Log.d("sharpcj", "counter text --> ${counter.value}")
        Text(text = "${counter.value}")
        Button(onClick = {
            Log.d("sharpcj", "increment button click ")
            counter.value++
        }) {
            Text(text = "increment")
        }
        Button(onClick = {
            Log.d("sharpcj", "decrement button click ")
            counter.value--
        }) {
            Text(text = "decrement")
        }
    }
}

我们使用了 mutableStateOf() 方法初始化了一个 MutableState 类型的状态变量,并传入默认值 0 ,使用的时候,需要调用 counter.value
再次运行,结果发现,点击按钮,计数器值还是没有变化,日志如下:

2024-03-12 21:57:24.773  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:57:31.428  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 21:57:31.437  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:57:31.825  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 21:57:31.834  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:57:33.047  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 21:57:33.055  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:57:33.216  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 21:57:33.224  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:57:33.634  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 21:57:33.643  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 21:57:33.792  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 21:57:33.801  6791-6791  sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0

和上一次不一样了,这次发现,点击按钮之后, Text(text = "${counter.value}") 有重新执行,即发生了重组,但是执行的时候,参数没有改变,依然是 0,其实这里涉及到一个重组作用域的概念,就是重组是有一个范围的,关于重组作用范围,稍后再讲。这里需要知道,发生了重组,Text(text = "${counter.value}") 有重新执行,那么 val counter: MutableState<Int> = mutableStateOf(0) 也有重新执行,相当于重组时,counter 被重新初始化了,并赋予了默认值 0 。所以点击按钮发生了重组,但是计数器的值没有发生改变。要解决这个问题,则需要使用到 Compose 中的一个重要函数 remember

2.2 remember

我们先看看 remember 函数的源码:

/**
 * Remember the value produced by [calculation]. [calculation] will only be evaluated during the composition.
 * Recomposition will always return the value produced by composition.
 */
@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =
    currentComposer.cache(false, calculation)

remember 方法的作用是,对其包裹起来的变量值进行缓存,后续发生重组过程中,不会重新初始化,而是直接从缓存中取。具体使用如下:

@Composable
fun CounterPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        val counter: MutableState<Int> = remember { mutableStateOf(0) }
        Log.d("sharpcj", "counter text --> ${counter.value}")
        Text(text = "${counter.value}")
        Button(onClick = {
            Log.d("sharpcj", "increment button click ")
            counter.value++
        }) {
            Text(text = "increment")
        }
        Button(onClick = {
            Log.d("sharpcj", "decrement button click ")
            counter.value--
        }) {
            Text(text = "decrement")
        }
    }
}

再次运行,这次终于正常了。

Jetpack Compose(3) —— 状态管理

看日志也正确了。每次点击都出发了重组,并且 counter 的值也没有重新初始化。

2024-03-12 22:18:53.744 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 0
2024-03-12 22:19:10.397 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 22:19:10.421 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 1
2024-03-12 22:19:10.967 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 22:19:10.981 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 2
2024-03-12 22:19:11.181 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 22:19:11.195 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 3
2024-03-12 22:19:11.649 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 22:19:11.663 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 4
2024-03-12 22:19:11.806 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 22:19:11.821 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 5
2024-03-12 22:19:12.364 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 22:19:12.377 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 4
2024-03-12 22:19:12.640 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 22:19:12.657 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 3
2024-03-12 22:19:13.204 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  increment button click 
2024-03-12 22:19:13.220 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 4
2024-03-12 22:19:13.747 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  decrement button click 
2024-03-12 22:19:13.761 19790-19790 sharpcj                 com.sharpcj.hellocompose             D  counter text --> 3

上面的代码中,我们创建 State 的方法如下:

val counter: MutableState<Int> = remember { mutableStateOf(0) }

使用时,通过 counter.value 来使用,这样的代码看起来就很繁琐,我们可以进一步精简写法。
首先, Kotlin 支持类型推导,所以可以写成下面这样:

val counter = remember { mutableStateOf(0) }

另外,借助于 Kotlin 委托语法,Compose 实现了委托方式赋值,使用 by 关键字即可,用法如下:

var counter by remember { mutableStateOf(0) }

并导入如下方法:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

在使用时,直接使用 counter++counter--

需要注意的一点是,没有使用委托方式创建的对象,类型是 MutableState 类型,我们用 val 声明,使用委托方式创建对象,对象类型是 MutableState 包装的对象类型,这里由于赋初始值为 0 ,根据类型推导,counter 就是 Int 型,由于要修改 counter 的值,所以须使用 var 将其声明为一个可变类型对象。

2.3 rememberSaveable

使用 remember 虽然解决了重组过程中,状态被重新初始化的问题,但是当 Activity 销毁重建时,状态值依然会重新初始化,比如横竖屏旋转,UiMode 切换等场景。在传统试图体系中,也存在这样的问题,对此的解决方案有很多,比如重写 Activity 的回调方法,在合适的时机,对数据进行保存和恢复,又或者使用 ViewModel 存放数据,这些方法对于 Compose 当然也有效,但是考虑到在使用 Compose 时,应该弱化 Activity 生命周期的概念,所以前者不适合在 Compose 中使用,而使用 ViewModel 依然是一种优秀的选择,后文再介绍。但是把所有的数据都放到 ViewModel 中,是否是最好的呢,这个要根据具体场景,进行甄别。举个例子,
针对这种场景,Compose 提供了 rememberSaveable 这个方法来解决这种场景的问题。

var counter by rememberSaveable { mutableStateOf(0) }

用法与 remember 方法用法类似,区别在于,rememberSaveable 在横竖屏旋转,UiMode 切换等场景中,能够对其包裹的数据进行缓存。那是否说明 rememberSaveable 可以在所有的场景替换 remember , remember 方法就没用了? rememberSaveable 方法比 remember 方法功能更强劲,代价就是性能要差一些,具体使用根据实际场景来选择。

到这里,状态相关的知识点,应该就很清楚了,再回头看上一篇文章中的 TextField 组件,应该能明白为什么那样写了。

三、 Stateless 和 Stateful

声明式 UI 的组件一般都可以分为 Stateless 组件和 Stateful 组件。
所谓 stateless 是指这个组件除了依赖参数以外,不依赖其它任何状态。比如 Text 组件,

Text("Hello, Compose")

相对的,某个组件除了参数以外,还持有或者访问了外部的状态,称为 stateful 组件。比如上一篇文章中提到的 TextField 组件,

var text by remember { mutableStateOf("文本框初始值") }
TextField(value = text, onValueChange = {
    text = it
})

Stateless 是不依赖于外部状态,仅依赖传入进来的参数,它是一个“纯函数”,即唯一输入,对应唯一输出。也就是参数不变,UI 就不会变化,它的重组只能是来自上层的调用,因此 Compose 编译器对其进行了优化,当 Stateless 的参数没有变化时,它就不会参与重组,重组的范围局限于 Stateless 外部。另外 Stateless 不耦合任何业务,功能更纯粹,所以复用性更好,也更容易测试。
基于此,我们应该尽可能地将 stateful 组件改造成 stateless 组件,这个过程称之为状态上提。

3.1 状态上提

状态上提,通常的做法就是将内部状态移除,以参数的形式传入。以及需要回调给调用方的事件,也以参数形式传入。
还是以上面计数器的代码为例,为了简洁,去掉前面添加的 log, 代码如下:

@Composable
fun CounterPage() {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        var counter by remember{ mutableStateOf(0) }
        Text(text = "$counter")
        Button(onClick = {
            counter++
        }) {
            Text(text = "increment")
        }
        Button(onClick = {
            counter--
        }) {
            Text(text = "decrement")
        }
    }
}

这里计数器主要是依赖了内部状态 counter, 同时两个按钮的点击事件,会改变 counter。状态上提之后,该方法如下:

@Composable
fun CounterPage(counter: Int, onIncrement: () -> Unit, onDecrement: () -> Unit) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(text = "$counter")
        Button(onClick = {
            onIncrement()
        }) {
            Text(text = "increment")
        }
        Button(onClick = {
            onDecrement()
        }) {
            Text(text = "decrement")
        }
    }
}

这样,Counter 组件,就变成了 stateless 组件,不再与业务耦合,职责更加单一,可复用性和可测试性都更强了。此外,状态上提,有助于单一数据源模型的打造。

四、状态管理

我们再来看一下在 Compose 中应该如何管理状态。

4.1 使用 stateful 管理状态

简单的的 UI 状态,且与业务无关的状态,适合在 Compose 中直接管理。
比如我有一个菜单列表,点一开关,展开一个菜单,再点一下,收起菜单,列表的状态,仅由点击开关这一单一事件决定。并且,列表的状态与任何外部业务无关。那么这种就适合在 Compose 内部进行管理。

4.2 使用 StateHolder 管理状态

当业务有一定的复杂度之后,我们可以将业务逻辑相关的状态统一封装到一个 StateHoler 进行管理。剥离 Ui 逻辑,让 Composable 专注 UI 布局。

4.3 使用 ViewModel 管理状态

从某种意义上讲,ViewModel 也是一种特殊的 StateHolde。单因为它是保存在 ViewModelStore 中,所以有一下特点:

  • 存活范围大,可以脱离 Composition 存在,被所有 Composable 共享。
  • 存活时间长,不会因为横竖屏切换或者 UiMode 切换导致数据丢失。

因此,ViewModel 适合管理应用程序全局状态,而且 ViewModel更倾向于管理哪些非 UI 的业务状态。

以上管理方式可以同时使用,结合具体的业务灵活搭配。

4.4 LiveData、Rxjava、Flow 转 State

在 MVVM 架构中,使用 ViewModel 来管理状态,如果是新项目,把状态直接定义 State 类型就可以了。

对于传统试图项目,一般使用 LiveData、Rxjava 或者 Flow 这类响应式数据框架。而在 Compose 中需要 State 触发重组,刷新 UI,也有相应的方法,将上述响应式数据流转换为 Compose 中的 State。当上有数据变化时,可以驱动 Composable 完成重组。具体方法如下:

拓展方法 依赖库
LiveData.observeAsState() androidx.compose:runtime-livedata
Flow.collectAsState() 不依赖三方库,Compose 自带
Observable.subscribeAsState() androidx.compose:runtime-rxjava2 或者 androidx.compose:runtime-rxjava3

五、小结

本文主要讲解了 Compose 中状态的概念。最后做个小结,文章来源地址https://www.toymoban.com/news/detail-839120.html

  • Compose UI 依赖状态变化,触发重组,驱动界面更新。
  • 使用 remember 和 rememberSaveable 进行状态持久化。remember 保证在 recompose 过程中状态稳定,rememberSaveable 保证 Activity 自动销毁重建过程中状态稳定。
  • 状态上提,尽可能将 Stateful 组件转换为 Stateless 组件。
  • 视情况使用 Stateful、StateHoler、ViewModel 管理状态。
  • 将 LiveData、RxJava、Flow 数据流转换为 State。

到了这里,关于Jetpack Compose(3) —— 状态管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Jetpack Compose(6)——动画

    目录 一、低级别动画 API 1.1 animate*AsState 1.2 Animatable 1.3 Transition 动画 1.3.1 updateTransition 1.3.2 createChildTransition 1.3.3 封装并复用 Transition 动画 1.4 remeberInfiniteTransition —— 无限循环的 transition 动画 1.5 小结 二、Android Studio 对 Compose 动画调试的支持 三、AnimationSpec 动画规格 3.1 Sprin

    2024年04月26日
    浏览(20)
  • Jetpack Compose 学习汇总

    关于 Jetpack Compose 的学习本想只是简单的快速学习一下,结果万万没想到,竟然一下子折腾了好几个月。。。 下面将之前记录的 Jetpack Compose 相关的学习博文进行一个汇总链接整理,方便我以后自己查阅,也希望能帮到一些有正在学习 Compose 的道友。 Jetpack Compose 中的基础组件

    2024年02月12日
    浏览(28)
  • Jetpack Compose(4)——重组

    目录 一、状态变化 1.1 状态变化是什么 1.2 mutableStateListOf 和 mutableStateMapOf 二、重组的特性 2.1 Composable 重组是智能的 2.2 Composable 会以任意顺序执行 2.3 Composable 会并发执行 2.4 Composable 会反复执行 2.5 Composable 的执行是“乐观”的 三、重组范围 四、参数类型的稳定性 4.1 稳定和不

    2024年04月08日
    浏览(27)
  • Vue中的Pinia状态管理工具 | 一篇文章教会你全部使用细节

    Pinia(发音为/piːnjʌ/,如英语中的“peenya”)是最接近piña(西班牙语中的菠萝)的词 ; Pinia开始于大概2019年,最初是 作为一个实验为Vue重新设计状态管理 ,让它用起来适合组合式API(Composition API)。 从那时到现在,最初的设计原则依然是相同的,并且目前同时兼容Vue2、

    2024年02月11日
    浏览(31)
  • Jetpack Compose之选择器

    选择器主要是指Checkbox复选框,单选开关Switch,滑杆组件Slider等用于提供给用户选择一些值和程序交互的组件,比如像复选框Checkbox,可以让用户选择一个或者多个选项,它可以将一个选项打开或者是关闭,通常用来做线上调查问卷或者是模拟考试的场景程序中,再比如滑杆组

    2023年04月11日
    浏览(23)
  • Android Jetpack Compose 别裁

    一、简介 二、compose优缺点 三、compose好学吗 四、Android Jetpack Compose 跟 fluter 哪个更好 五、Android Jetpack Compose 跟 fluter 技能学习选择 之所以叫Android Jetpack Compose别裁是希望能取舍网上的对compose的资料,推出别出心裁的文章,文章结束都会有一个案例,通过实践学习,让学习的

    2024年02月03日
    浏览(46)
  • Jetpack Compose中的动画

    Jetpack Compose中没有沿用Android原有的View动画和属性动画,而是新创建了一套全新的动画系统API,这是理所当然的,因为旧的动画系统主要是基于View体系的,而Compose中需要针对的是Composable可组合函数进行处理,那么势必要创造一套新的玩具出来,同时,这也无疑增加了开发者

    2024年02月08日
    浏览(31)
  • Jetpack Compose中的Accompanist

    accompanist是Jetpack Compose官方提供的一个辅助工具库,以提供那些在Jetpack Compose sdk中目前还没有的功能API。 权限 依赖配置: 单个权限申请 例如,我们需要获取相机权限,可以通过 rememberPermissionState(Manifest.permission.CAMERA) 创建一个 PermissionState 对象,然后通过 PermissionState.statu

    2024年02月03日
    浏览(26)
  • Jetpack Compose UI架构

    Jetpack Compose是我职业生涯中最激动人心的事。它改变了我工作和问题思考的方式,引入了易用且灵活的工具,几乎可轻松实现各种功能。 早期在生产项目中尝试了Jetpack Compose后,我迅速着迷。尽管我已有使用Compose创建UI的经验,但对新的Jetpack Compose驱动特性的组织和架构引发

    2024年02月11日
    浏览(37)
  • Jetpack compose实现MVI

    MVI(Model-View-Intent)是一种前端架构模式,其目标是使状态管理更具可预测性,便于开发和调试。它将应用程序视为一个函数,该函数接受一系列的意图(Intent)作为输入,然后返回一个新的状态作为输出。在 Jetpack Compose 中,你可以使用 Kotlin Flow 和 State 来实现 MVI。 下面是

    2024年02月16日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包