【问题标题】:Constraining a Composable to the bottom of a Column将 Composable 约束到列的底部
【发布时间】:2022-07-22 19:37:26
【问题描述】:

我和我的团队是 Compose 的新手,我们正在尝试为客户构建一个 TopUp 屏幕。屏幕由一个 Column 组成,该 Column 包含 TopAppBar 的一些 Padding(以 Composable 的形式)、两个 Card 可组合项、一个按钮、一个页脚可组合项,我需要将其约束到列的底部,最后是一些填充(再次以可组合的形式)在内容和底部导航栏之间留出一些空间。

现在我们正在使用具有固定 dp 值的垫片,但显然,这不会缩放,因此它在所有设备上都被限制在底部。实现这种外观的最佳方法是什么?

屏幕可组合:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun TopUpScreen() {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight()
            .padding(horizontal = 16.dp)
    ) {
        val viewModel = getViewModel<TopUpViewModel>()
        TopBarPadding()
        TopUpCardView(title = stringResource(id = R.string.choose_topup_amount)) {
            var selectedCardIndex by remember { mutableStateOf(-1) }
            Row(
                horizontalArrangement = Arrangement.SpaceAround,
                modifier = Modifier
                    .fillMaxWidth()
            ) {
                Card(
                    shape = RoundedCornerShape(11.dp),
                    elevation = if (selectedCardIndex == 0) 1.dp else 0.dp,
                    backgroundColor = if (selectedCardIndex == 0) colorResource(id = R.color.bottom_modal_drawer_background) else colorResource(
                        id = R.color.more_screen_item_background
                    ),
                    modifier = Modifier
                        .width(71.dp)
                        .height(56.dp)
                        .shadow(
                            if (selectedCardIndex == 0) 8.dp else 0.dp,
                            shape = RoundedCornerShape(11.dp)
                        )
                        .clickable {
                            selectedCardIndex = 0
                            viewModel.topUpAmount = 20.0
                        }
                ) {
                    Text(
                        "€20",
                        fontSize = 16.sp,
                        fontWeight = FontWeight.Normal,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.wrapContentHeight()
                    )
                }
                Card(
                    shape = RoundedCornerShape(11.dp),
                    elevation = if (selectedCardIndex == 1) 1.dp else 0.dp,
                    backgroundColor = if (selectedCardIndex == 1) colorResource(id = R.color.bottom_modal_drawer_background) else colorResource(
                        id = R.color.more_screen_item_background
                    ),
                    modifier = Modifier
                        .width(71.dp)
                        .height(56.dp)
                        .shadow(
                            if (selectedCardIndex == 1) 8.dp else 0.dp,
                            shape = RoundedCornerShape(11.dp)
                        )
                        .clickable {
                            selectedCardIndex = 1
                            viewModel.topUpAmount = 40.0
                        }
                ) {
                    Text(
                        "€40",
                        fontSize = 16.sp,
                        fontWeight = FontWeight.Normal,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.wrapContentHeight()
                    )
                }
                Card(
                    shape = RoundedCornerShape(11.dp),
                    elevation = if (selectedCardIndex == 2) 1.dp else 0.dp,
                    backgroundColor = if (selectedCardIndex == 2) colorResource(id = R.color.bottom_modal_drawer_background) else colorResource(
                        id = R.color.more_screen_item_background
                    ),
                    modifier = Modifier
                        .width(71.dp)
                        .height(56.dp)
                        .shadow(
                            if (selectedCardIndex == 2) 8.dp else 0.dp,
                            shape = RoundedCornerShape(11.dp)
                        )
                        .clickable {
                            selectedCardIndex = 2
                            viewModel.topUpAmount = 70.0
                        }
                ) {
                    Text(
                        "€70",
                        fontSize = 16.sp,
                        fontWeight = FontWeight.Normal,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.wrapContentHeight()
                    )
                }
                Card(
                    shape = RoundedCornerShape(11.dp),
                    elevation = if (selectedCardIndex == 3) 1.dp else 0.dp,
                    backgroundColor = if (selectedCardIndex == 3) colorResource(id = R.color.bottom_modal_drawer_background) else colorResource(
                        id = R.color.more_screen_item_background
                    ),
                    modifier = Modifier
                        .width(71.dp)
                        .height(56.dp)
                        .shadow(
                            if (selectedCardIndex == 3) 8.dp else 0.dp,
                            shape = RoundedCornerShape(11.dp)
                        )
                        .clickable {
                            selectedCardIndex = 3
                            viewModel.topUpAmount = 100.0
                        }
                ) {
                    Text(
                        "€100",
                        fontSize = 16.sp,
                        fontWeight = FontWeight.Normal,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.wrapContentHeight()
                    )
                }
            }
        }
        Spacer(modifier = Modifier.padding(16.dp))

        val focusManager = LocalFocusManager.current
        val keyboardController = LocalSoftwareKeyboardController.current
        TopUpCardView(title = stringResource(id = R.string.enter_custom_topup_amount)) {
            var customAmountTxt by remember { mutableStateOf(TextFieldValue()) }
            TextField(
                value = customAmountTxt,
                onValueChange = {
                    customAmountTxt = it
                },
                maxLines = 1,
                singleLine = true,
                leadingIcon = {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_euro),
                        contentDescription = stringResource(
                            R.string.euro_icon_desc
                        ),
                        modifier = Modifier.padding(
                            start = 16.dp,
                            end = 16.dp,
                            top = 12.dp,
                            bottom = 12.dp
                        )
                    )
                },
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Number,
                    imeAction = ImeAction.Done
                ),
                keyboardActions = KeyboardActions(onDone = {
                    focusManager.clearFocus()
                    keyboardController?.hide()
                }),
                shape = RoundedCornerShape(6.dp),
                colors = TextFieldDefaults.textFieldColors(
                    backgroundColor = colorResource(id = R.color.white),
                    textColor = colorResource(id = R.color.black),
                    focusedIndicatorColor = colorResource(id = R.color.white),
                    unfocusedIndicatorColor = colorResource(id = R.color.white),
                    disabledIndicatorColor = colorResource(id = R.color.white),
                    cursorColor = colorResource(id = R.color.black)
                ),
                textStyle = TextStyle(
                    color = Color.Black,
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Normal,
                    textAlign = TextAlign.Start
                ),
                modifier = Modifier
                    .height(50.dp)
                    .fillMaxWidth()
                    .shadow(8.dp, shape = RoundedCornerShape(6.dp))
            )
        }
        Spacer(modifier = Modifier.padding(32.fixedDp()))
        val context = LocalContext.current //todo:sp remove when you remove the toast
        MyButton(
            text = stringResource(id = R.string.continue_text),
            buttonType = MyButtonType.PRIMARY,
            onClick = {
                Toast.makeText(context, "[TODO] Navigate to card screen", Toast.LENGTH_SHORT).show()
            })
        //todo:sp replace the spacer implementation with something that will constraint the
        // footer to the bottom as it should
        Spacer(modifier = Modifier.height(130.dp))
        AcceptedCardsFooter()
        BottomBarPadding()
    }
}

TopUpCardView:

@Composable
fun TopUpCardView(
    title: String,
    modifier: Modifier = Modifier,
    mainBody: @Composable () -> Unit
) {
    Card(
        shape = RoundedCornerShape(13.dp),
        elevation = 10.dp,
        modifier = modifier
            .fillMaxWidth()
            .height(131.dp)
    ) {
        Column(modifier = Modifier.padding(vertical = 20.dp, horizontal = 16.dp)) {
            Text(
                text = title,
                fontSize = 16.sp,
                fontWeight = FontWeight.Bold,
                textAlign = TextAlign.Start
            )
            Spacer(modifier = Modifier.padding(9.dp))
            mainBody()
        }
    }
}

页脚:

@Composable
fun AcceptedCardsFooter(isTransparent: Boolean = false) {
    Row(modifier = Modifier
        .fillMaxWidth()
        .clip(RoundedCornerShape(10.dp))
        .background(
            if (isTransparent) {
                Color.Transparent
            } else {
                colorResource(id = R.color.registration_note_background)
            }
        )
        .padding(bottom = 12.dp, top = 12.dp, start = 16.dp, end = 14.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(text = LocalContext.current.getString(R.string.accepted_cards),
            fontSize = 12.sp,
            color = colorResource(id = R.color.Black_100)
        )
        Row(verticalAlignment = Alignment.CenterVertically) {
            Image(painter = painterResource(id = R.drawable.visa), contentDescription = "")
            Spacer(modifier = Modifier.padding(17.fixedDp()))
            Image(painter = painterResource(id = R.drawable.mastercard), contentDescription = "")
            Spacer(modifier = Modifier.padding(10.fixedDp()))
            Image(painter = painterResource(id = R.drawable.american_express), contentDescription = "")
        }
    }
}

【问题讨论】:

    标签: android kotlin android-jetpack-compose


    【解决方案1】:

    最简单的方法是使用weight修饰符:

    Spacer(modifier = Modifier.height(130.dp).weight(1f))
    

    【讨论】:

    • 可悲的是,我之前尝试过,我再次尝试,但它似乎根本不影响布局。
    【解决方案2】:

    将底部填充添加到第一个 ,以便在内容和底部栏之间留出空间。之后将 weight 赋予第二个 Column 以使页脚约束到底部。

    例如,

     Column(
            modifier = Modifier.fillMaxSize().padding(bottom = 15.dp, start = 16.dp, end = 16.dp )
        ) {
            Column(
                modifier = Modifier.fillMaxSize().weight(1f).padding(horizontal = 16.dp)
            ) {
                // top bar
                // two cards
                // button
            }
    
            // add footer here
        }
    

    【讨论】:

      【解决方案3】:

      我会使用 ConstraintLayout 并包装您需要始终位于屏幕底部的视图。

      ConstraintLayout {
          // Create references for the composables to constrain
          val id_of_your_footer = createRefs()
      
          YourFooterComposable(
              modifier = Modifier.constrainAs(id_of_your_footer) {
                  bottom.linkTo(parent.bottom)
              }
          )
      }
      

      只要父视图是根视图,页脚就会被限制在屏幕底部。

      或者,您可以使用自动设置了这些约束的脚手架:

      Scaffold(topBar={//place a top bar composable here}, 
               content={//all your content composables here}, 
               bottomBar={//your footer composable here})
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-08-25
        • 2017-05-15
        • 1970-01-01
        • 1970-01-01
        • 2020-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多