Какие модификаторы и где нужно применить, чтобы все элементы помещались в альбомной ориентации?
import ...
data class ArtInfo(
val artId: Int,
val drawableRes: Int,
val title: String,
val author: String,
val year: Int,
)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ComposeTheme {
Surface(
modifier = Modifier
.statusBarsPadding()
.navigationBarsPadding()
.fillMaxSize(),
color = MaterialTheme.colorScheme.onPrimary
) {
ArtSpaceApp()
}
}
}
}
}
@Composable
fun ArtSpaceApp(modifier: Modifier = Modifier) {
val arts = listOf(
...
)
var artState by remember {
mutableStateOf(arts[0])
}
fun navigateToNextArt() {
...
}
fun navigateToPreviousArt() {
...
}
Column(
modifier = modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(20.dp))
Art(imageResource = artState.drawableRes)
Spacer(modifier = Modifier.height(60.dp))
AboutArt(
title = artState.title,
author = artState.author,
year = artState.year
)
NavigationArts(
{ navigateToNextArt() },
{ navigateToPreviousArt() }
)
}
}
@Composable
fun Art(@DrawableRes imageResource: Int) {
Surface(
modifier = Modifier
.border(width = 32.dp, color = Color.White)
.shadow(elevation = 16.dp),
) {
Image(
painter = painterResource(id = imageResource),
contentDescription = null
)
}
}
@Composable
fun AboutArt(
modifier: Modifier = Modifier
.background(color = colorResource(id = R.color.background_about_art)),
title: String,
author: String,
year: Int,
) {
Surface(
modifier = modifier
.padding(16.dp)
) {
Column(
modifier = modifier
.fillMaxWidth()
) {
Text(
text = title,
fontSize = 24.sp,
fontWeight = FontWeight.W300,
)
Row {
Text(
text = author,
fontWeight = FontWeight.W800
)
Text(
text = " ($year)"
)
}
}
}
}
@Composable
fun NavigationArts(
navigateToNext: () -> Unit,
navigateToPrevious: () -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(onClick = ... }) {
Text(text = "Previous")
}
Button(onClick = ... }) {
Text(text = "Next")
}
}
}
Проблема в том, что Art(...) - изображение занимает все пространство по вертикали, не оставляя место под AboutArt(...) - описание изображения и NavigationArts(...) - кнопки навигации между изображениями.
Ответы (2 шт):
@Composable
fun ArtSpaceApp(modifier: Modifier = Modifier) {
...
Column(
modifier = modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(20.dp))
Column(
modifier = modifier,
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Art(
imageResource = artState.drawableRes,
modifier = Modifier
.padding(top = 32.dp, bottom = 16.dp)
.requiredHeightIn(min = 0.dp, max = 500.dp)
)
Spacer(modifier = Modifier.height(24.dp))
AboutArt(
title = artState.title,
author = artState.author,
year = artState.year,
)
}
NavigationArts(
{ navigateToNextArt() },
{ navigateToPreviousArt() }
)
}
}
@Composable
fun Art(
@DrawableRes imageResource: Int,
modifier: Modifier = Modifier,
) {
Surface(modifier = modifier.shadow(elevation = 16.dp)) {
Surface(
modifier = Modifier
.border(width = 32.dp, color = Color.White)
) {
Image(
modifier = Modifier.padding(32.dp),
painter = painterResource(id = imageResource),
contentDescription = null,
contentScale = ContentScale.Inside
)
}
}
}
Art(), Spacer() и AboutArt() обернул в отдельный контейнер Column, чтобы кнопки навигации были "прибиты" к нижней части экрана. В самом Art() контейнер Surface пришлось обернуть в еще один контейнер, иначе не отображалась тень modifier.shadow(elevation = 16.dp).
На лучшую реализацию не претендую)
Размещение нужно такое
отступ
картинка
отступ
подпись
отступ до самого низа
навигация
И чтобы подпись была всегда видна, и чтобы картинка ужималась в размерах.Проблема в том, что картинка не знает сколько высоты ей можно взять. Column не лимитирует размер. Modifier.weight(1f)
не помогает, ведь применяясь к Surface
он растягивает его по всей высоте, а если применить к картинке, то навигация прижмется к подписи.
У вас слишком много вложенных блоков. Убираем лишнее
data class ArtState(
@DrawableRes val drawableRes: Int,
val title: String,
val author: String,
val year: Int
)
@Composable
fun Art(
@DrawableRes imageResource: Int,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier
.shadow(elevation = 16.dp)
.border(width = 32.dp, color = Color.White)
) {
Image(
modifier = Modifier.padding(32.dp),
painter = painterResource(id = imageResource),
contentDescription = null,
contentScale = ContentScale.Inside
)
}
}
@Composable
fun NavigationArts(
navigateToNext: () -> Unit,
navigateToPrevious: () -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(onClick = navigateToNext) { Text(text = "Previous") }
Button(onClick = navigateToPrevious) { Text(text = "Next") }
}
}
@Composable
fun AboutArt(
title: String,
author: String,
year: Int,
modifier: Modifier = Modifier
) {
Surface(
modifier = modifier
.background(color = Color.Gray)
.padding(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
Text(
text = title,
fontSize = 24.sp,
fontWeight = FontWeight.W300,
)
Row {
Text(
text = author,
fontWeight = FontWeight.W800
)
Text(text = " ($year)")
}
}
}
}
Вот решение через слоты. Нет смысла комбинировать Spacer + padding
, если можно задать просто padding. Также убрал лишнюю вложенность Column->Column
@Composable
fun ArtSpaceApp(artState: ArtState, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
MyLayout(
modifier = Modifier.weight(1f),
content = {
Art(
imageResource = artState.drawableRes,
modifier = Modifier
// убрал лишние Spacer
.padding(top = (32.dp + 20.dp), bottom = (16.dp + 24.dp))
)
},
bottomContent = {
AboutArt(
title = artState.title,
author = artState.author,
year = artState.year,
)
}
)
NavigationArts(
{ },
{ }
)
}
}
@Composable
fun MyLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
bottomContent: @Composable () -> Unit,
) {
SubcomposeLayout(modifier = modifier) { constraints: Constraints ->
// тут bottom определяет свой размер. По ширине пусть берет все, а по высоте не более ей нужного
val bottomPlaceable: Placeable = subcompose(SlotsEnum.Bottom, bottomContent).map {
it.measure(Constraints(maxWidth = constraints.maxWidth))
}.first()
// а тут основной контент определяет свой размер. Из общей высоты вычитаем bottom и пусть умещается
// по высоте, а по ширине там сработает ContentScale
val contentPlaceable: Placeable = subcompose(SlotsEnum.Main, content).map {
it.measure(
Constraints(
maxWidth = constraints.maxWidth,
maxHeight = constraints.maxHeight - bottomPlaceable.height
)
)
}.first()
// размещаем
layout(constraints.maxWidth, constraints.maxHeight) {
// центрируем
contentPlaceable.placeRelative((constraints.maxWidth - contentPlaceable.width) / 2, 0)
// приклеиваем bottom к картинке
bottomPlaceable.placeRelative(0, contentPlaceable.height)
}
}
}
enum class SlotsEnum {
Main,
Bottom
}
Если нужно вывести мелкую картинку, то подпись может быть слишком высоко, поэтому в MyLayout можно передать минимальную высоту и сделать что-то типа
maxHeight = min(myminHeight, constraints.maxHeight - bottomPlaceable.height)
Попробую то же самое с ConstraintLayout. Если получится, то дополню ответ.