Skip to content

Instantly share code, notes, and snippets.

@TheMelody
Last active June 5, 2021 05:18
Show Gist options
  • Save TheMelody/657530d6b68455bc139b8598bc7ea1ea to your computer and use it in GitHub Desktop.
Save TheMelody/657530d6b68455bc139b8598bc7ea1ea to your computer and use it in GitHub Desktop.
Compose BottomSheet控件封装 - MultiBottomSheetLayout.kt
/**
* 组合式BottomSheetDialog
* @param sheetModifier 仅影响BottomSheet布局,不影响mainContent布局
* @param sheetElevation bottomSheet背景的阴影
* @param sheetShape bottomSheet的视图shape
* @param mainContent activity的contentView
* @param sheetContent bottomSheet弹出来的contentView
* @param topLeftIcon 内置的顶部左侧Icon
* @param topCenterIcon 内置的顶部中心点的Icon
* @param topRightIcon 内置的顶部右侧Icon
*/
@SuppressLint("ModifierParameter")
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MultiBottomSheetLayout(
sheetModifier: Modifier = Modifier,
sheetElevation: Dp = 0.dp,
sheetShape: Shape = MaterialTheme.shapes.large,
mainContent: @Composable (sheetScreen: (MultiBottomSheet.Intent) -> Unit) -> Unit,
sheetContent: @Composable (arguments: Bundle?) -> Unit,
topLeftIcon: (@Composable (onClosePressed: () -> Unit) -> Unit)? = null,
topCenterIcon: (@Composable (onClosePressed: () -> Unit) -> Unit)? = null,
topRightIcon: (@Composable (onClosePressed: () -> Unit) -> Unit)? = null
) {
val scope = rememberCoroutineScope()
val scaffoldState = rememberBottomSheetScaffoldState()
//记录BottomSheet
var currentBottomSheet: MultiBottomSheet.Intent? by remember{
mutableStateOf(null)
}
//关闭的时候需要置空
if(scaffoldState.bottomSheetState.isCollapsed){
currentBottomSheet = null
}
//执行关闭BottomSheet
val closeSheet: () -> Unit = {
scope.launch {
scaffoldState.bottomSheetState.collapse()
}
}
//展开BottomSheet
val openSheet: (MultiBottomSheet.Intent) -> Unit = {
scope.launch {
currentBottomSheet = it
scaffoldState.bottomSheetState.expand()
}
}
BottomSheetScaffold(sheetPeekHeight = 0.dp, scaffoldState = scaffoldState,
sheetElevation = sheetElevation,
sheetShape = sheetShape,
// 可以通过外部传入【Modifier.padding(top = xx.dp)】
// 来设置BottomSheet弹出来的视图【距离】『屏幕顶部的距离』
modifier = sheetModifier,
sheetContent = {
currentBottomSheet?.let { currentSheetIntent ->
BottomSheetWithTopClose(
content = {
sheetContent(currentSheetIntent.arguments)
},
topLeftIcon = topLeftIcon,
topCenterIcon = topCenterIcon,
topRightIcon = topRightIcon,
onClosePressed = closeSheet
)
}
}) { paddingValues ->
Box(Modifier.padding(paddingValues)){
mainContent(openSheet)
}
}
}
sealed class MultiBottomSheet() {
//支持传入Bundle,通过Bundle去解析数据,渲染新BottomSheet页面内容
class Intent(val arguments:Bundle? = null):MultiBottomSheet()
}
@Composable
private fun BottomSheetWithTopClose(
onClosePressed: () -> Unit,
modifier: Modifier = Modifier,
topLeftIcon: (@Composable (onClosePressed: () -> Unit) -> Unit)? = null,
topCenterIcon: (@Composable (onClosePressed: () -> Unit) -> Unit)? = null,
topRightIcon: (@Composable (onClosePressed: () -> Unit) -> Unit)? = null,
content: @Composable () -> Unit
) {
Box(modifier.fillMaxWidth()) {
content()
if(topLeftIcon != null){
IconButton(
onClick = onClosePressed,
modifier = Modifier
.align(Alignment.TopStart)
.padding(16.dp)
.size(29.dp)
) {
topLeftIcon(onClosePressed)
}
}
if (topCenterIcon != null) {
IconButton(
onClick = onClosePressed,
modifier = Modifier
.align(Alignment.TopCenter)
.padding(16.dp)
.size(29.dp)
) {
topCenterIcon(onClosePressed)
}
}
if(topRightIcon != null){
IconButton(
onClick = onClosePressed,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(16.dp)
.size(29.dp)
) {
topRightIcon(onClosePressed)
}
}
}
}
/**
* 测试代码,仅供参考
**/
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeDemo1Theme {
Surface(
color = Color(0xFF9A9777),
modifier = Modifier
.fillMaxWidth()
.wrapContentSize()
) {
//topRightIcon和topCenterIcon都是可选参数
MultiBottomSheetLayout(
sheetModifier = Modifier.padding(top = 46.dp),
sheetElevation = 10.dp,
sheetShape = BottomSheetShape,
topCenterIcon = {
Icon(
modifier = Modifier.width(60.dp).height(5.dp),
painter = painterResource(id = R.drawable.ic_h_line_svg),
tint = Color.Black.copy(alpha = 0.25F),
contentDescription = null
)
},
topRightIcon = {
Icon(Icons.Filled.Close, tint = Color.Black.copy(alpha = 0.45F), contentDescription = null)
},
sheetContent = { bundle ->
//👇👇底部弹出来的BottomSheet👇👇
PopBottomScreen(arguments = bundle)
}, mainContent = { openSheet ->
//主界面内容,使用:openSheet(MultiBottomSheet.Intent())触发Intent
//会回调上面👆👆sheetContent={}👆👆这里
MainContent(openSheet)
})
}
}
}
}
}
/**
* 测试ContentView
**/
@Composable
fun MainContent(openSheet: (MultiBottomSheet.Intent) -> Unit) {
Column(Modifier.fillMaxSize(),verticalArrangement = Arrangement.SpaceEvenly,horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Compose BottomSheet",color = Color.White)
Button(onClick = { openSheet(MultiBottomSheet.Intent()) }) {
Text(text = "Bottom Sheet => 单纯的显示")
}
Button(onClick = { openSheet(MultiBottomSheet.Intent(Bundle().apply {
putString("INTENT_VALUE","测试Bundle传入数据:"+ getRandomString(Random.nextInt(10)))
putInt("INTENT_RANDOM_VALUE", Random.nextInt(10000))
putString("INTENT_RANDOM_VALUE2", "清风明月")
})) }) {
Text(text = "Bottom Sheet => Bundle传入数据")
}
}
}
/**
* 随机字符串
**/
private fun getRandomString(length: Int) : String {
val allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
/**
* 底部弹出的bottomSheet的contentView
**/
@Composable
fun PopBottomScreen(arguments: Bundle?){
if(null == arguments){
TestScreen1()
}else{
TestScreen2(arguments = arguments)
}
}
/**
* 测试:不需要参数
**/
@Composable
fun TestScreen1() {
Box(modifier = Modifier
.fillMaxSize()
.background(Color.Yellow, shape = RectangleShape)) {
//这里用Coil加载网络图片,不会用的童鞋可以用本地图片代替
val imagePainter = rememberCoilPainter(
request = "https://dss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=3275952959,4263475242&fm=55&app=54&f=JPEG?w=1140&h=640",
requestBuilder = {transformations(RoundedCornersTransformation(30F))},
fadeIn = true,
)
Image(
painter = imagePainter,
contentDescription = null,
modifier = Modifier.align(Alignment.Center),
)
when (imagePainter.loadState) {
is ImageLoadState.Loading -> {
CircularProgressIndicator(Modifier.align(Alignment.Center))
}
is ImageLoadState.Error -> {
Image(painter = painterResource(id = R.drawable.ic_load_image_failed), contentDescription =null,modifier = Modifier.align(Alignment.Center),)
}
}
}
}
/**
* 测试传参
**/
@Composable
fun TestScreen2(arguments: Bundle?) {
val keySet = arguments?.keySet()
Box(modifier = Modifier
.fillMaxSize()
.background(Color.White, shape = RectangleShape)){
keySet?.let {keySetValue->
Column(modifier = Modifier.align(Alignment.Center)) {
//循环Bundle里面的数据
for (key in keySetValue){
Text(text = "${arguments.get(key)}", Modifier.padding(16.dp),color = Color.Black,fontSize = 15.sp)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment