本短文将讨论如何使用糖法语有条件地添加Modifier. 要做到这一点, 自定义扩展是唯一的方法. 首先, 我将演示在没有扩展的情况下可以做些什么, 然后探讨拥有这些扩展可以如何简化你的生活.
目录
- 没有扩展
- 使用扩展
- 扩展源代码
- 奖奖
- 最后的想法
1. 无扩展
我将以 alpha Modifier为例. 这种方法可用于所有Modifier, 尽管有些Modifier可能不容易适应 if-else 结构.
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.alpha(
if (my_condition) my_true_value
else my_false_value
)
)
因此, 我们需要使用 if-else 结构. 虽然这种方法运行良好, 但如果我们偶尔需要包含 alpha Modifier, 有时又不需要, 该怎么办呢?有两种方法:
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.alpha(
if (my_condition) my_true_value
else 1.0f // we have to add the alpha modifier at 1.0f even if we don't need it
)
)
我们的真正目的是:
val modifier = Modifier
.size(60.dp)
.background(Color.Blue)
if (my_condition) modifier.alpha(my_true_value)
Box(modifier = modifier)
使用这种方法, 我们只在必要时添加alpha Modifier. 不过, 我并不喜欢这种方法, 因为它需要在方框上方创建一个变量.
如前所述, 我在示例中使用了 alpha Modifier, 因为它很容易适应 if-else 结构. 不过, 在讨论其他Modifier时, 我们还是要力求清晰.
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue,
if (my_condition) CircleShape
else RectangleShape // You must specify the Rectangle shape explicitly.
)
)
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.padding(
if (my_condition) 12.dp
else 0.dp // You must specify 0.dp explicitly.
)
)
它迫使我们在可以省略Modifier的情况下使用一些默认值. 但是, 如前所述, 另一种方法是不可接受的.
2. 使用扩展
让我们重温一下上面提到的同样的情况, 但其方式要符合我们喜欢的写作风格.
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.thenOnTrue(my_condition) {
alpha(my_true_value) // Here alpha is added only when needed
}
)
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.thenOnTrue(my_condition) {
clip(CircleShape) // Here the shape is applied through clip only when needed
}
)
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.thenOnTrue(my_condition) {
padding(12.dp) // Here padding is added only when needed
}
)
我非常喜欢这种写法. 我们也可以将其应用于假条件, 甚至在真条件块中包含多个Modifier.
// Our extension must manage also false condition alone.
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.thenOnFalse(my_condition) {
alpha(my_false_value) // Here alpha is added only when needed
.padding(start = 12.dp) // Yes we can chain modifier inside the block condition!
}
)
// Our extension must be able to manage true and false condition in one shot
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.then(
my_condition,
onTrue = {
padding(vertical = 12.dp)
clip(CircleShape)
},
onFalse = {
alpha(0.5)
.shadow(2.dp)
}
)
)
// What about null ? I did it too, I'll show you the source code of
// theses extensions in the next section
3. 扩展源代码
因此, 初始部分由我们的三个函数签名组成:thenOnTrue, thenOnFalse和then.
@Composable
fun Modifier.thenOnTrue(
condition: Boolean,
block: @Composable Modifier.() -> Modifier
) = thenInternal(condition, onTrue = block)
@Composable
fun Modifier.thenOnFalse(
condition: Boolean,
block: @Composable Modifier.() -> Modifier
) = thenInternal(condition, onFalse = block)
@Composable
fun Modifier.then(
condition: Boolean,
onTrue: @Composable Modifier.() -> Modifier,
onFalse: @Composable Modifier.() -> Modifier
) = thenInternal(condition, onTrue, onFalse)
由于一切都可因式分解, 我只创建了一个名为thenInternal的函数扩展, 并在三个签名调用中使用了它. 以下是thenInternal的代码:
@Composable
private fun Modifier.thenInternal(
condition: Boolean,
onTrue: (@Composable Modifier.() -> Modifier)? = null,
onFalse: (@Composable Modifier.() -> Modifier)? = null
) = (if (condition) {
onTrue?.let { then(Modifier.it()) }
} else {
onFalse?.let { then(Modifier.it()) }
}) ?: this
这就是它的全部内容. 你可以将它与任何Modifier一起使用, 并通过连锁多个条件Modifier来实现你想要的任何效果!
所有Modifier的作用域都使用了
* @Composable, 因为我希望能够在onFalse或onTrue代码块中使用组成的作用域变量.
Box(
modifier = Modifier
.background(Color.Blue)
.thenOnTrue(my_condition) {
size(MyLocalTheme.smallBox)
// You can also use composable scoped variables
// like LocalContext.current
// or even "val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f)"
}
)
在Compose的早期时日
以前, 不建议在Modifier扩展中使用@Composable. 建议使用composed块来代替. 因此, 第一个版本是这样的:
// *** Old way *** Not recommended anymore
// Composable annotation here was not recommended
private fun Modifier.thenInternal(
condition: Boolean,
onTrue: (@Composable Modifier.() -> Modifier)? = null,
onFalse: (@Composable Modifier.() -> Modifier)? = null
) = (if (condition) {
onTrue?.let { composed { then(Modifier.it()) } } // so we used composed block
} else {
onFalse?.let { composed { then(Modifier.it()) } }
}) ?: this
4.奖励
作为奖励, 我们还可以使用完全相同的方法来处理空条件值.
@Composable
fun <T : Any> Modifier.thenOnNotNull(
condition: T?,
block: @Composable Modifier.(T) -> Modifier
) = thenInternal(condition, onNotNull = block)
@Composable
fun <T : Any> Modifier.thenOnNull(
condition: T?,
block: @Composable Modifier.() -> Modifier
) = thenInternal(condition, onNull = block)
@Composable
fun <T : Any> Modifier.then(
condition: T?,
onNotNull: @Composable Modifier.(T) -> Modifier,
onNull: @Composable Modifier.() -> Modifier
) = thenInternal(condition, onNotNull, onNull)
private fun <T : Any> Modifier.thenInternal(
condition: T?,
onNotNull: (@Composable Modifier.(T) -> Modifier)? = null,
onNull: (@Composable Modifier.() -> Modifier)? = null
) = (condition?.let {
onNotNull?.let { then(Modifier.it(condition))}
} ?: run {
onNull?.let { then(Modifier.it()) }
}) ?: this
充分利用以上函数:
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.thenOnNull(my_object_which_can_be_null) {
alpha(0.8f) // Here alpha is added only when needed
.otherModifier()
}
)
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.thenOnNotNull(my_object_which_can_be_null) {
padding(12.dp) // Here padding is added only when needed
.otherModifier()
}
)
Box(
modifier = Modifier
.size(60.dp)
.background(Color.Blue)
.then(
my_object_which_can_be_null,
onNotNull = {
padding(vertical = 12.dp)
clip(CircleShape)
},
onNull = {
alpha(0.5)
.shadow(2.dp)
}
)
)
5. 最后的想法
这一次, 我不会提供源代码库. 我认为这是不必要的, 因为所有代码都在这里展示, 而且仅由扩展组成.
我坚信, 像这样实用的扩展可以大大简化你的生活并提高可读性. 请随意尝试使用它们, 甚至分享你的经验!
如果你注意到本文的结构, 我首先确定了我想要的友好书写类型, 然后将其制作成扩展. 这种方法几乎适用于所有情况下的设计 -- 首先设想你想要什么, 然后执行它! 当然, 途中可能会有意想不到的发现, 尤其是在尝试新事物时. ^^