Jetpack Compose - FloatingActionButton 展开/折叠 的多级悬浮菜单

3,590 阅读2分钟

Compose实现FloatingActionButton 展开/折叠动画也太简单了吧😍
我们自定义一个MultiFloatingActionButton的Compose函数
文章的末尾会有完整的代码


MultiFloatingActionButton最终效果图

1.大家看一下,我们最终提供出来的调用的示例:

//初始化2个Item弹出菜单
val expandFbItemList: MutableList<MultiFabItem> = mutableListOf(
        MultiFabItem(...),
        MultiFabItem(...)
    )
    val context = LocalContext.current
    //创建一个MultiFloatingActionButton组件
    MultiFloatingActionButton(srcIcon = Icons.Filled.Add,
        showLabels = true,items = expandFbItemList) {
        //item被点击了
        Toast.makeText(context.applicationContext,"点击了:${it.label}",Toast.LENGTH_SHORT).show()
    }

2.我们需要实现展开和折叠功能,首先需要定义一个状态类

enum class MultiFabState {
    Collapsed,
    Expanded
}

3.我们需要外部能修改菜单内的数据,定义一个Item实体bean

class MultiFabItem(
    //Fab中心Icon
    val icon: ImageVector,
    //提示文本内容
    val label: String,
    //Fab中心Icon颜色
    val srcIconColor: Color = Color.White,
    //提示文本内容颜色
    val labelTextColor: Color = Color.White,
    //提示文本内容区域背景色
    val labelBackgroundColor: Color = Color.Black.copy(alpha = 0.6F),
    //Fab按钮背景色
    val fabBackgroundColor: Color = Color.Unspecified,
)

4.定义一个MultiFloatingActionButton的Compose方法

@Composable
fun MultiFloatingActionButton(
    modifier: Modifier = Modifier,
    //“+”号按钮的图片
    srcIcon: ImageVector,
    //“+”号按钮的颜色
    srcIconColor: Color = Color.White,
    //“+”号按钮区域背景色
    fabBackgroundColor: Color = Color.Unspecified,
    //是否显示item的label内容
    showLabels: Boolean = true,
    //所有展开的菜单Item
    items: List<MultiFabItem>,
    //菜单Item的点击回调
    onFabItemClicked: (item: MultiFabItem) -> Unit
)

5.定义动画,用于折叠和展开菜单显示的动画

//下面【省略号】的部分代码,文章【末尾会有全量的代码】【贴图】,有需要的可以去看一下。
//当前菜单默认状态处于:Collapsed
val currentState = remember { mutableStateOf(MultiFabState.Collapsed) }
//创建过渡对象,用于管理多个动画值,并且根据状态变化运行这些值
val transition = updateTransition(targetState = currentState, label = "")
//用于+号按钮的旋转动画
val rotateAnim: Float by transition.animateFloat(.....) { state ->
    //根据state来设置最终的角度
    if (state.value == MultiFabState.Collapsed) 0F else -45F
}
//透明度动画
val alphaAnim: Float by transition.animateFloat(....) { state ->
   if (state.value == MultiFabState.Expanded) 1F else 0F
}
//记录每个Item的收缩动画的Transition
val shrinkListAnim:MutableList<Float> = mutableListOf()
items.forEachIndexed { index, _ ->
    val shrinkAnim by transition.animateFloat(.....)
    //添加到收缩列表中
    shrinkListAnim.add(index,shrinkAnim)
}

6.使用Box包裹我们的所有FloatingActionButton

Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) {
  items.forEachIndexed{index, item ->
      Row(....){
        if(showLabels){
          Text(....)
        }
        FloatingActionButton(....)
      }
  }
   FloatingActionButton(....)
}

7.完整代码(前往github查看详细代码)

MultiFloatingActionButton.kt