每当回收器视图中的项目聚焦时,我试图实现聚光灯效果。我在下面附上一张图片来说明我的意思。
这是drawable的代码
bg_glow.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<gradient
android:startColor="#80e8eaed"
android:gradientRadius="50dp"
android:endColor="#00000000"
android:type="radial" />
</shape>
</item>
</layer-list>
我创建了一个自定义视图,每当图像聚焦时就会添加此可绘制对象。这是代码。
override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
if (gainFocus) {
categorySpotlight.background = ContextCompat.getDrawable(context, R.drawable.bg_glow)
ivappCategory.strokeWidth = 2
} else {
categorySpotlight.background = ContextCompat.getDrawable(context, R.drawable.transparent)
ivappCategory.strokeWidth = 0
}
}
现在有一些问题:
您可以根据焦点项目图像中的主色更改渐变颜色。
如果您可以通过编程方式创建可绘制对象,那么您还可以更改阴影的颜色。
可以在 android 中以编程方式创建可绘制对象。看看这个:
public static GradientDrawable makeRadialGradient(int startColor, int endColor) {
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, new int[]{startColor, endColor});
gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
gd.setGradientRadius(500.0f);
gd.setGradientCenter(0.5f, 0.5f);
return gd;
}
现在您可以将此方法与您的自定义颜色一起使用,它将提供渐变可绘制(而不是位图)。 因此,如果你想使用渐变,那么你的目标 imageview 背景必须是渐变可绘制的(就像现在一样)。只需调用该方法即可:
makeRadialGradient(Color.parseColor("#D3D3D3"), Color.parseColor("#FFFFFF"));
现在,如果您想从主色制作渐变,那么您只需在方法中传递颜色代码,它就会为您提供您想要的渐变。
您必须更改这些值以获得最完美的结果。
根据您的喜好更改渐变半径、枢轴点和类型。
我终于明白了这一点。我们必须使用调色板从图像中提取主色。然后,我们可以使用 androidx.tv.material3 中的元素,例如卡片或其他支持发光属性的元素。
@Composable
fun PosterImage(
movie: Meta,
modifier: Modifier = Modifier,
isBackground: Boolean = false,
onDominantColor: (Color) -> Unit = {},
) {
val fallbackColor = Color.White
var dominantColor by remember { mutableStateOf(fallbackColor) }
val imageUrl = if (isBackground) movie.background else movie.poster
var hasError by remember { mutableStateOf(false) }
if (imageUrl != null) {
LoadImage(
imageUrl = imageUrl,
modifier = modifier
.fillMaxSize()
.customShadow(dominantColor),
onSuccess = { bitmap ->
extractDominantColor(bitmap, fallbackColor) {
dominantColor = it
onDominantColor(it)
}
},
onError = {
hasError = true
}
)
if (hasError) {
Text("Failed to load image")
}
}
}
@Composable
fun LoadImage(
imageUrl: String,
modifier: Modifier = Modifier,
onSuccess: (Bitmap) -> Unit = {},
onError: () -> Unit = {}
) {
val painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.crossfade(true)
.size(400, 320) // Adjust as needed
.data(imageUrl)
.placeholder(android.R.drawable.ic_menu_gallery)
.error(android.R.drawable.ic_menu_report_image)
.build()
)
LaunchedEffect(painter.state) {
if (painter.state is AsyncImagePainter.State.Success) {
val imageResult = (painter.state as AsyncImagePainter.State.Success).result
onSuccess(imageResult.drawable.toBitmap())
} else if (painter.state is AsyncImagePainter.State.Error) {
onError()
}
}
Image(
painter = painter,
contentDescription = null,
modifier = modifier,
contentScale = ContentScale.Crop
)
}
fun Modifier.customShadow(dominantColor: Color) = this.shadow(
elevation = 30.dp,
shape = RectangleShape,
clip = false,
ambientColor = dominantColor
)
private fun extractDominantColor(
bitmap: Bitmap,
fallbackColor: Color,
onColorExtracted: (Color) -> Unit
) {
// Run the palette extraction on a background thread
CoroutineScope(Dispatchers.IO).launch {
try {
var palette: Palette? = null
var dominantColor = fallbackColor
if (bitmap.config == Bitmap.Config.HARDWARE) {
// For hardware bitmaps, we need to convert to ARGB_8888 first
val convertedBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false)
palette = Palette.from(convertedBitmap).generate()
dominantColor = Color(palette.getDominantColor(fallbackColor.toArgb()))
} else {
// For other bitmap configurations, we can work directly with the original bitmap
palette = Palette.from(bitmap).generate()
dominantColor = Color(palette.getDominantColor(fallbackColor.toArgb()))
}
val dominantColorInt = dominantColor.toArgb()
val lightenedColorInt = ColorUtils.blendARGB(dominantColorInt, 0xFFFFFFFF.toInt(), 0.2f)
val lightenedColor = Color(lightenedColorInt)
dominantColor = lightenedColor
// Switch back to the main thread to update the UI
withContext(Dispatchers.Main) {
onColorExtracted(dominantColor)
}
} catch (e: Exception) {
// In case of any errors, fallback to the provided fallbackColor
Log.e("PosterImage", "Error extracting color fallback to default", e)
withContext(Dispatchers.Main) {
onColorExtracted(fallbackColor)
}
}
}
}
然后在任何你想要的地方使用该主导颜色。
@Composable
fun MovieCard(
modifier: Modifier = Modifier,
dominantColor: Color = Color.White,
onClick: () -> Unit,
title: @Composable () -> Unit = {},
image: @Composable BoxScope.() -> Unit,
) {
Log.d("MovieCard", "MovieCard dominantColor: ${dominantColor.value}")
StandardCardContainer(
modifier = modifier,
title = title,
imageCard = {
Surface(
onClick = onClick,
shape = ClickableSurfaceDefaults.shape(JetStreamCardShape),
glow = ClickableSurfaceDefaults.glow(
focusedGlow = Glow(
elevationColor = dominantColor,
elevation = 30.dp
)
),
scale = ClickableSurfaceDefaults.scale(focusedScale = 1f),
content = image
)
},
)
}