hay Superficie componible en Jetpack Compose que representa un superficie material. Una superficie le permite configurar cosas como el color de fondo o el borde, pero parece que se podría hacer lo mismo usando modificadores. ¿Cuándo debo usar el componible de Surface y qué beneficios me brinda?
valeriy katkov
Superficie composable hace que el código sea más fácil e indica explícitamente que el código usa un superficie material. Veamos un ejemplo:
Surface(
color = MaterialTheme.colors.primarySurface,
border = BorderStroke(1.dp, MaterialTheme.colors.secondary),
shape = RoundedCornerShape(8.dp),
elevation = 8.dp
) {
Text(
text = "example",
modifier = Modifier.padding(8.dp)
)
}
y el resultado:
El mismo resultado se puede lograr sin Surface:
val shape = RoundedCornerShape(8.dp)
val shadowElevationPx = with(LocalDensity.current) { 2.dp.toPx() }
val backgroundColor = MaterialTheme.colors.primarySurface
Text(
text = "example",
color = contentColorFor(backgroundColor),
modifier = Modifier
.graphicsLayer(shape = shape, shadowElevation = shadowElevationPx)
.background(backgroundColor, shape)
.border(1.dp, MaterialTheme.colors.secondary, shape)
.padding(8.dp)
)
pero tiene algunos inconvenientes:
- La cadena de modificadores es bastante grande y no es obvio que implemente una superficie de material
- Debo declarar una variable para la forma y pasarla a tres modificadores diferentes
- Usa contenidoColorPara para averiguar el color del contenido mientras que Surface lo hace bajo el capó. como resultado el
backgroundColor
se utiliza en dos lugares también. - tengo que calcular la elevacion en pixeles
Surface
ajusta los colores para la elevación (en caso de tema oscuro) de acuerdo con el diseño de materiales. Si desea el mismo comportamiento, debe manejarlo manualmente.
Para ver la lista completa de funciones de Surface, es mejor echar un vistazo a la documentación.
-
El bloqueo de la propagación táctil podría ser quizás la función más importante de Surface, en comparación con otras vistas de contenido/componibles.
– Anm
13 de septiembre de 2021 a las 14:44
La superficie es el equivalente de CardView
en el sistema de vista.
Por Surface
puede establecer la elevación para la vista (tenga en cuenta que esto no es lo mismo con Modifier.shadow)
Surface
es un Box
con un Modifier.surface()
y los colores y la elevación del material, verifica que la elevación de los ancestros esté siempre encima de ellos, y solo se sobrecarga por debajo bloqueando la propagación del toque detrás de la superficie con pointerInput(Unit) {}
.
@Composable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
content: @Composable () -> Unit
) {
val absoluteElevation = LocalAbsoluteElevation.current + elevation
CompositionLocalProvider(
LocalContentColor provides contentColor,
LocalAbsoluteElevation provides absoluteElevation
) {
Box(
modifier = modifier
.surface(
shape = shape,
backgroundColor = surfaceColorAtElevation(
color = color,
elevationOverlay = LocalElevationOverlay.current,
absoluteElevation = absoluteElevation
),
border = border,
elevation = elevation
)
.semantics(mergeDescendants = false) {}
.pointerInput(Unit) {},
propagateMinConstraints = true
) {
content()
}
}
}
Y Modifier.surface()
private fun Modifier.surface(
shape: Shape,
backgroundColor: Color,
border: BorderStroke?,
elevation: Dp
) = this.shadow(elevation, shape, clip = false)
.then(if (border != null) Modifier.border(border, shape) else Modifier)
.background(color = backgroundColor, shape = shape)
.clip(shape)
Otra cosa interesante es que es Box
con propagateMinConstraints = true
parámetro que obliga al primer descendiente a tener las mismas restricciones o dimensiones mínimas
Surface(
modifier = Modifier.size(200.dp),
onClick = {}) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {}
}
Spacer(modifier = Modifier.height(20.dp))
Surface(
modifier = Modifier.size(200.dp),
onClick = {}) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Green, RoundedCornerShape(6.dp))
) {}
}
}
Spacer(modifier = Modifier.height(20.dp))
Box(
modifier = Modifier.size(200.dp)
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Red, RoundedCornerShape(6.dp))
) {
Column(
modifier = Modifier
.size(50.dp)
.background(Color.Green, RoundedCornerShape(6.dp))
) {}
}
}
En el primer ejemplo en Surface
efectivo Column
tener 200.dp tamaño a pesar de que tiene Modifier.size(50.dp)
.
En segundo ejemplo Box
en el interior Column
tiene un tamaño de 50.dp porque no es un descendiente directo de Surface
.
En el tercer ejemplo si reemplazamos Surface
(Caja con propagateMinConstraints true) con Box
permite que el descendiente directo use sus propias restricciones o dimensiones.