W sekcjach poniżej pokazujemy, jak połączyć Modifier.blur, BlurRadiusSpec, BlurStop i BlurScope w typowych wzorcach interfejsu. Wprowadzenie do tych interfejsów API znajdziesz w artykule Rozmycie w Compose.
Rozmycie krawędzi przewijanych lub przeciąganych treści
Podczas wyświetlania przewijanej listy, karuzeli lub selektora możesz użyć wielostopniowego rozmycia progresywnego, aby zmiękczyć elementy w pobliżu krawędzi kontenera, zachowując jednocześnie ostrość aktywnego obszaru centralnego.
Zanikanie górnej i dolnej krawędzi przewijanej listy
Aby złagodzić efekt przewijania elementów na liście pionowej, zastosuj BlurRadiusSpec.verticalGradient z wpisami BlurStop do LazyColumn. W tym przykładzie, w którym tekst piosenki jest przewijany wiersz po wierszu, punkty 0.dp między 0.42f i 0.58f utrzymują ostrość aktywnego wiersza na środku, podczas gdy wiersze u góry i u dołu stopniowo się rozmywają:
@Composable fun SongListTopBottomBlurExample(lyrics: List<String>) { // ... LazyColumn( state = listState, flingBehavior = flingBehavior, modifier = Modifier .width(330.dp) .height(480.dp) .padding(horizontal = 24.dp) .blur( blurRadiusSpec = BlurRadiusSpec.verticalGradient( stops = listOf( BlurStop(fraction = 0.00f, radius = 18.dp), // Heavily blurred top edge BlurStop(fraction = 0.24f, radius = 8.dp), // Soft upper transition BlurStop(fraction = 0.42f, radius = 0.dp), // Sharp center focus starts BlurStop(fraction = 0.58f, radius = 0.dp), // Sharp center focus ends BlurStop(fraction = 0.76f, radius = 8.dp), // Soft lower transition BlurStop(fraction = 1.00f, radius = 18.dp), // Heavily blurred bottom edge ), ), edgeTreatment = BlurredEdgeTreatment.Unbounded, ), contentPadding = PaddingValues(vertical = 36.dp), verticalArrangement = Arrangement.spacedBy(26.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { // ... } }
Zanikanie lewej i prawej krawędzi elementu sterującego w poziomie
Podobnie, aby skupić uwagę na środku komponentu, który można przewijać lub przeciągać w poziomie, takiego jak selektor kołowy, karuzela lub skala pomiarowa, zastosuj BlurRadiusSpec.horizontalGradient z wpisami BlurStop. Na poniższej skali wagi, którą można przeciągać, lewa i prawa krawędź są rozmyte, a kreski i cyfry w pobliżu środkowej wskazówki są całkowicie ostre:
@Composable fun HorizontalScaleBlurExample() { var selectedValue by remember { mutableFloatStateOf(65.0f) } val animatedValue by animateFloatAsState( targetValue = selectedValue, animationSpec = tween(durationMillis = 900, easing = FastOutSlowInEasing), label = "horizontalScaleSmoothScroll", ) Column( modifier = Modifier .width(340.dp) .background(Color(0xFF0F172A), RoundedCornerShape(24.dp)) .padding(vertical = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( text = "${(animatedValue * 10).roundToInt() / 10.0} kg", color = Color.White, fontSize = 44.sp, fontWeight = FontWeight.Black, ) Spacer(Modifier.height(16.dp)) Box( // ... ) { // Ruler ticks blurred at the left and right edges, sharp at center Canvas( modifier = Modifier .fillMaxSize() .blur( blurRadiusSpec = BlurRadiusSpec.horizontalGradient( stops = listOf( BlurStop(fraction = 0.00f, radius = 14.dp), BlurStop(fraction = 0.28f, radius = 0.dp), BlurStop(fraction = 0.72f, radius = 0.dp), BlurStop(fraction = 1.00f, radius = 14.dp), ), ), edgeTreatment = BlurredEdgeTreatment.Unbounded, ), ) { // Draw horizontal ruler ticks offset by `animatedValue` // ... } // ... } } }
Utwórz rozmycie po przekątnej za pomocą linearGradient
BlurRadiusSpec.linearGradient zmienia promień rozmycia wzdłuż odcinka linii między wartościami start i end DpOffset w przestrzeni współrzędnych warstwy.
Wewnątrz bloku lambda Modifier.blur możesz odczytać BlurScope.size, aby uruchomić progresywne rozmycie po przekątnej banera powitalnego od lewego górnego do prawego dolnego rogu, zachowując pełną ostrość obiektu w lewym górnym rogu i stopniowo rozmywając prawy dolny obszar za nałożonym tekstem:
@Composable fun DiagonalLinearBlurCardExample() { Box( modifier = Modifier .wrapContentSize() .background(Color(0xFFF8FAFC)) .padding(10.dp), ) { Image( painter = painterResource(id = R.drawable.dog), contentDescription = stringResource(id = R.string.dog_content_description), contentScale = ContentScale.Crop, modifier = Modifier .size(width = 300.dp, height = 380.dp) .blur { blurRadiusSpec = BlurRadiusSpec.linearGradient( start = DpOffset(0.dp, 0.dp), end = DpOffset(size.width, size.height), stops = listOf( BlurStop(fraction = 0.00f, radius = 0.dp), BlurStop(fraction = 0.50f, radius = 0.dp), BlurStop(fraction = 0.75f, radius = 14.dp), BlurStop(fraction = 1.00f, radius = 28.dp), ), ) edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(24.dp)) }, ) // ... } }
Tworzenie wyróżnienia z użyciem radialGradient
BlurRadiusSpec.radialGradient tworzy efekt okrągłego reflektora, zmieniając promień rozmycia na zewnątrz od punktu ostrości. Zdefiniuj center jako DpOffset, a fallOffRadius jako odległość Dp od center, przy której osiągana jest wartość endRadius. Jeśli pozostawisz center jako DpOffset.Unspecified, gradient będzie wyśrodkowany na elemencie kompozycyjnym, a jeśli pozostawisz fallOffRadius jako Dp.Unspecified, domyślnie będzie to połowa mniejszego wymiaru warstwy.
Na przykład zastosowanie BlurRadiusSpec.radialGradient do fragmentu tekstu i aktualizowanie center na podstawie zdarzeń przeciągania wskaźnika pozwala użytkownikowi przeciągać okrągłe podświetlenie czytanego tekstu po akapicie, rozmywając otaczający tekst, jak w tym przykładzie:
@Composable fun RadialSpotlightTextExample() { var touchOffset by remember { mutableStateOf(Offset.Unspecified) } Box( modifier = Modifier .size(width = 330.dp, height = 340.dp) .background(Color(0xFF0F172A), RoundedCornerShape(20.dp)) .pointerInput(Unit) { detectDragGestures( onDragStart = { offset -> touchOffset = offset }, onDrag = { change, _ -> touchOffset = change.position }, ) } .blur { val spotlightCenter = if (touchOffset.isSpecified) { DpOffset(touchOffset.x.toDp(), touchOffset.y.toDp()) } else { DpOffset(x = size.width / 2, y = size.height / 2) } blurRadiusSpec = BlurRadiusSpec.radialGradient( startRadius = 0.dp, endRadius = 10.dp, center = spotlightCenter, fallOffRadius = 215.dp, ) edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(20.dp)) } .padding(22.dp), ) { // ... } }
Tworzenie niestandardowej maski rozmycia za pomocą cieniowania
W przypadku złożonych przestrzennych rozkładów rozmycia, które wykraczają poza gradienty liniowe lub gradienty promieniowe, takich jak powtarzające się wzory, dowolne kształty lub szum proceduralny, przekaż niestandardowy obiekt Shader, np. AGSL RuntimeShader, do BlurRadiusSpec.shader. Program do cieniowania jest oceniany we współrzędnych pikseli warstwy od lewego górnego rogu, a jego próbkowany kanał alfa (0..1) skaluje maxRadius.
Utwórz cieniowanie i konfigurację BlurRadiusSpec.shader raz za pomocą funkcji typu „composable” remember, aby pojedyncza instancja była używana wielokrotnie w różnych rekompozycjach. Compose wywołuje blok konfiguracji z rozmiarem warstwy przy każdym rozwiązaniu, gdzie ustawiasz zmienne uniform zależne od rozmiaru:
@RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable fun ShaderBlurExample() { // Creates 6 diagonal frosted-glass bands across the image. val maskShader = remember { RuntimeShader( """ uniform float2 size; half4 main(float2 coord) { float2 uv = coord / size; float band = smoothstep(0.4, 0.6, fract((uv.x + uv.y) * 3.0)); return half4(0.0, 0.0, 0.0, half(band)); } """.trimIndent(), ) } val maskBlurSpec = remember(maskShader) { BlurRadiusSpec.shader(maxRadius = 24.dp) { sizePx -> maskShader.setFloatUniform("size", sizePx.width, sizePx.height) maskShader } } Image( painter = painterResource(id = R.drawable.dog), contentDescription = stringResource(id = R.string.dog_content_description), contentScale = ContentScale.Crop, modifier = Modifier .size(150.dp) .blur(blurRadiusSpec = maskBlurSpec), ) }