Compose尝鲜体验

142 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

Compose尝鲜体验

一、Compose和Android传统UI区别

​ Compose全新的UI框架相比较与传统UI区别在于,传统UI由于应用的状态因用户的操作发生变化的时候,我们通常改变的方式是button.setText(String)img.setImageBitmap(Bitmap)这种方式去更改UI,通过我们手动去更改试图会提高我们出错的概率,并且我们软件的可维护性也会增加。加入一个数据在多个视图上呈现,这个时候我们很容易忘记一更新与之相关联的所有视图。为了解决这个问题Google在Jetpack中新增了DataBind库,来进行数据和视图的双向绑定,从而实现以数据去驱动界面的更新。与之相比Compose的出现不仅直观的解决的这个问题,并且让复杂界面的绘制变得简单、易组合,并且在Compose中添加动画效果也非常简单

二、声明性范式转变

​ 在Compose的声明性方法中,可组合项时相对无状态的,可以通过调用不同参数的同一可组合函数来更新界面,在拆分可组合项时,应根据场景将组合项拆分的粒度尽量小,功能尽量单一,这个可以在状态更改时触发重组时的范围尽量小,一个可组合项应该只关注和它相关的状态。

Compose 界面中数据从高级对象向下流向其子级的图示。

​ 用户与界面元素进行了交互,导致触发一个事件。应用逻辑响应该事件,然后系统根据需要使用新参数自动再次调用可组合函数。可以从下图中看出如果可组合项只关注相关的状态,那状态发生变更时,重组的范围就会极大的减小(Compose的智能重组)

说明界面元素如何通过触发由应用逻辑处理的事件来响应交互的图示。

在可组合项进行重组时,应避免去依赖可组合函数重组产生的附带效应,应为在重组时一些与某些状态无关的可组合项不会发生重组,如果这样操作你会遇见一些不可预测的行为。并且在可组合函数中应该避免去做一些成本比较高昂的操作,比如IO操作等。以下操作应该避免的可组合函数中进行

  • 对共享对象进行写入
  • 更新ViewModel中的可观察项

三、状态和组合

状态就是所谓可组合项触发重组的值,MutableState时Compose运行时集成的可观察类型,在观察的可组合项中value 如有任何更改,系统会安排重组读取 value 的所有可组合函数。

interface MutableState<T> : State<T> {
    override var value: T
}

在可组合函数中如果要保证状态不因重组而发生重置改变,我们需要使用remember来使可组合项记住单个对象。使可组合函数发生重组时,状态不会因为重组而发生重置,在可组合项中声明 MutableState 对象的方法有三种:

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

在Android中使用的常见可观察类型也可以创建State的函数

  • LiveData<T>.observeAsState()
  • Flow<T>.collectAsState()
有状态与无状态

无状态可组合项是指不包含任何状态的可组合项。实现无状态的一种简单方法是使用状态提升。同时也推荐的组件封装的时候建议封装一层无状态组件,将状态提升到状态相关的组件里,这样我们可以编写多个不同样式的状态组件

@Composable
fun CouponOfState(
    coupon_name: String,
    coupon_tips: String,
    coupon_time: String,
    isSelected: Boolean,
    onClick: () -> Unit
) {
    //关联状态
    val bgColor by animateColorAsState(if (isSelected) white else MaterialTheme.colors.primary)
    val couponNameColor by animateColorAsState(if (isSelected) blackFF2D2D2D else MaterialTheme.colors.secondary)
    val couponTipseColor by animateColorAsState(if (isSelected) redFFFF4D48 else MaterialTheme.colors.orderText)
    val couponTimeColor by animateColorAsState(if (isSelected) grayFF898989 else  MaterialTheme.colors.couponEndTime)
    val lineColor by animateColorAsState(if (isSelected) red80FF4D48 else MaterialTheme.colors.couponLineColor)
    val drawContentColor by animateColorAsState(if (isSelected) red1AFF4D48 else MaterialTheme.colors.drawContentColor)
    val buttonText =
        (if (isSelected) stringResource(id = R.string.pay_cancel) else stringResource(id = R.string.pay_select))
    //状态无关的优惠券组件 这样我们可以在无状态组件上衍生多种样式的相同组件
    Coupons(
        modifier = Modifier,
        coupon_name = coupon_name,
        coupon_tips = coupon_tips,
        coupon_time = coupon_time,
        button_text = buttonText,
        bgColor = bgColor,
        couponNameColor = couponNameColor,
        couponTipseColor = couponTipseColor,
        couponTimeColor = couponTimeColor,
        lineColor = lineColor,
        drawContentColor = drawContentColor,
        drawArcColor = MaterialTheme.colors.primary,
        onClick = onClick
    )
}

我们在进行状态提升的时候需要遵循以下原则:

  • 单一可信来源 :通过移动状态,而不是复制状态,确保只有一个状态来源。

  • 封装:只有有状态 组合项才能修改其状态,保证状态的内部性。

  • 可共享:可与多个可组合项共享提升的状态。如果想在另一个组合项中共享此状态,就需要将此状态进行提升,在提升状态时应该避免状态的过渡提升,状态应该只在与之相关的可组合项的最小范围内

  • 可拦截:无状态可组合项的调用方可以在更改状态之前决定忽略或修改事件