大话Compose筑基(2)

开启生长之旅!这是我参加「日新计划 2 月更文应战」的第 7 天,点击检查活动概况

咱们略微改动一下鬼话Compose筑基(1) – ()介绍状况的实例代码为:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, true)
    setContent {
        Puzzle5()
    }
}
@Composable
private fun Puzzle5() {
    println("Puzzle5")
    var name by mutableStateOf("Hello")
    Text(name)
    lifecycleScope.launch {
        delay(3000)
        name = "Hello World"
    }
}

运行后发现并不是咱们的预期效果。Text始终显示的是Hello。是没有触发重组吗?咱们通过控制台日志能够看到每3秒打印了Puzzle5,那么阐明是有重组产生的,可是留意这个时分name在重组的时分又会被mutableStateOf重新创立并给与初始值Hello,所以造成下面的Text(name)仍是显示的Hello,那要怎么改呢?有两种处理思路分别是有状况无状况

有状况or无状况

  • 有状况 -> remember
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, true)
    setContent {
        Puzzle5()
    }
}
@Composable
private fun Puzzle5() {
    println("Puzzle5")
    var name by remember { mutableStateOf("Hello") }
    Text(name)
    lifecycleScope.launch {
        delay(3000)
        name = "Hello World"
    }
}

这里保持让Puzzle5可组合函数拥有name状况,也便是有状况。留意在创立状况的时分咱们用了rememberapi去保存了这个状况。
可组合函数能够运用rememberAPI 将目标存储在内存中。体系会在初始组合期间将由remember核算的值存储在组合中,并在重组期间返回存储的值。remember既可用于存储可变目标,又可用于存储不可变目标。
在可组合项中声明MutableState目标的办法有三种:

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remember { mutableStateOf(default) }

这些声明是等效的,以语法糖的方式针对状况的不同用法供给。您选择的声明应该能够在您编写的可组合项中生成可读性最高的代码。

尽管remember可协助您在重组后保持状况,但不会协助您在装备更改后保持状况。为此,您有必要运用rememberSaveablerememberSaveable会自动保存可保存在Bundle中的任何值。关于其他值,您能够将其传入自定义 Saver 目标。

  • 无状况 -> 状况提高
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, true)
    //把状况交给了OnCreate办法
    var name by mutableStateOf("Hello")
    setContent {
        //也能够把状况提高到这个当地。假如这样的话这个当地就有必要用remember了,否则setContent重组的时分会覆盖
        //var name by remember { mutableStateOf("Hello") }
        //name状况作为参数给到Puzzle5函数运用,lamda交给调用函数去完成
        Puzzle5(name) {
            lifecycleScope.launch {
                delay(3000)
                //改动了状况后Puzzle5产生重组
                name = "Hello World"
            }
        }
    }
}
//不再有状况,状况提高了
@Composable
private fun Puzzle5(name: String, onValueChange: () -> Unit) {
    println("Puzzle5")
    Text(name)
    onValueChange.invoke()
}

运行一下,嘿嘿!是咱们的预期结果。三秒后页面变成了Hello World,日志在初次组合的时分打印一次,name改动后重组的时分打了一次。因为Puzzle不负责创立状况name,所以重组的时分不会覆盖之前的状况的值。这个时分咱们的可组合函数Puzzle5便是无状况,他把状况提高给OnCreate函数了。

有状况和无状况咱们应该怎么取舍呢?其实这个要根据运用的环境来定,在一开始咱们其实不用过多的关怀这个点,但需知一般在调用方不需求控制状况,而且不必自行办理状况便可运用状况的情况下,“有状况”会十分有用。可是,具有内部状况的可组合项往往不易重复运用,也更难测试

无状况可组合项是指不保持任何状况的可组合项。完成无状况的一种简单办法是运用状况提高。

在开发可重复运用的可组合项时,您通常想要同时供给同一可组合项的有状况和无状况版别。有状况版别关于不关怀状况的调用方来说很便利,而无状况版别关于需求控制或提高状况的调用方来说是必要的。

感兴趣的xdm能够看看官方文档专门讲状况提高的文档提高状况的场景 | Jetpack Compose | Android Developers,这个不属于筑基范畴故不会在本文深化。

重组

前面我现已了解了组合状况可组合函数组合起来便是组合可组合函数重组效果域订阅状况状况改动会再次执行引用了该状况可组合函数,这个过程便是重组了。咱们用一个计数器的完成来当比如看看:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, true)
    setContent {
        println("SetContent")
        var clickCount by remember { mutableStateOf(0)}
        ClickCounter(clickCount) {
            clickCount++
        }
    }
}
@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
    println("ClickCounter")
    Button(onClick = onClick) {
        println("Button")
        Text("I've been clicked $clicks times")
    }
}
大话Compose筑基(2)

这里有3处打印了日志,每次点击都会打印这三个日志,阐明都产生了重组。那么肯定有人问,就改一个状况造成整个setContent的lamda表达式里边的一切函数重组,而且实际情况界面肯定比这复杂多了,而且里边还可能有成本高傲操作(在数据读取很多数据,很多绘制等),那么我这功能怎么办?那不掉帧?不ANR?带着这个疑问咱们把上面的代码略微改一下看看:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, true)
    setContent {
        println("SetContent")
        var clickCount by remember { mutableStateOf(0)}
        val scrollState = rememberScrollState()
        Column (modifier = Modifier.fillMaxWidth().verticalScroll(scrollState)){
            ClickCounter(clickCount) {
                clickCount++
            }
            Puzzle2()
        }
    }
}
@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
    println("ClickCounter")
    Button(onClick = onClick) {
        println("Button")
        Text("I've been clicked $clicks times")
    }
}
@Composable
fun Puzzle2() {
    println("我重组了")
    repeat (100) {
        Text(text = "第${it + 1}个Text")
    }
}

现在咱们在界面加个组合函数Puzzle2来渲染一个列表,也加上一个日志打印。当咱们点击按钮改动状况的时分打印出来日志并没有"我重组了"。阐明在setContent的lamda表达式重组的时分,跳过了Puzzle2。那这是为什么呢?这个就有必要说到重组的机制和重组效果域了。

重组效果域

Compose 的重组会影响功能吗?聊一聊 recomposition scope – ()看这篇就够了。仔细看后就了解重组效果域了,这篇文章就不再多说了。

小结

这篇咱们通过比如介绍了组合函数的有状况和无状况。有状况要用到rememberapi,无状况会涉及到状况提高。然后看了两个重组的比如,看了一篇大神的文章认识了重组效果域。那么下篇咱们就通过生命周期来进一步把握重组的机制。

开启生长之旅!这是我参加「日新计划 2 月更文应战」的第 7 天,点击检查活动概况