android 上的 onFocus 聚光灯效果

问题描述 投票:0回答:2

每当回收器视图中的项目聚焦时,我试图实现聚光灯效果。我在下面附上一张图片来说明我的意思。
enter image description here

我已经使用背景绘制实现了类似的效果,如下所示。
enter image description here

这是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
    }
}

现在有一些问题:

  1. 径向渐变看起来并不完全像我想要实现的目标。
  2. 它不是动态的。我想根据图像中的主色更改阴影的颜色。
java android android-animation android-resources android-tv
2个回答
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"));

现在,如果您想从主色制作渐变,那么您只需在方法中传递颜色代码,它就会为您提供您想要的渐变。

您必须更改这些值以获得最完美的结果。

根据您的喜好更改渐变半径、枢轴点和类型。


0
投票

我终于明白了这一点。我们必须使用调色板从图像中提取主色。然后,我们可以使用 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
            )
        },
    )
}
© www.soinside.com 2019 - 2024. All rights reserved.