TL;DR
在 compose 中将主题应用于可绘制对象可能有点棘手,因为
Xml 可绘制对象无法访问 Jetpack Compose 中的颜色定义。因此,您应该在 Xml 中定义颜色和主题,并让 Jetpack Compose 了解这些值。
在 Compose 中使用 AndroidView 来加载 xml drawable:
AndroidView(factory = { context ->
val contextThemeWrapper = ContextThemeWrapper(context, R.style.RedTheme)
val drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.your_drawable, contextThemeWrapper.theme)
ImageView(context).apply { setImageDrawable(drawable) }
})
这样,drawable 将尊重您传递给ContextThemeWrapper 的主题样式。
在 Xml 中定义主题
除了您定义的主题RedTheme 和GreenTheme,您还需要声明这些属性:
您还需要定义样式属性:
<declare-styleable name="ThemeStyle">
<attr name="colorPrimary" format="color" />
<attr name="colorSecondary" format="color" />
</declare-styleable>
将主题映射到撰写
定义一个AppTheme 类:
object AppTheme {
val colors: AppColors
@Composable
@ReadOnlyComposable
get() = LocalColors.current
val style: Int
@Composable
@ReadOnlyComposable
get() = LocalStyleRes.current
}
internal val LocalStyleRes: ProvidableCompositionLocal<Int> = staticCompositionLocalOf { R.style.RedTheme }
@Composable
fun AppTheme(
@StyleRes styleRes: Int,
context: Context,
content: @Composable () -> Unit,
) {
val themeReader = ThemeReader(context, styleRes)
val colors = AppColors(
primary = Color(themeReader.colorPrimary),
secondary = Color(themeReader.colorSecondary),
)
CompositionLocalProvider(
LocalColors provides colors,
LocalStyleRes provides styleRes
) {
content()
}
}
还有一个AppColor 类:
data class AppColors(
val primary: Color,
val secondary: Color,
)
internal val LocalColors: ProvidableCompositionLocal<AppColors> = staticCompositionLocalOf {
AppColors(
primary = Color.White,
secondary = Color.White
)
}
从 Xml 读取主题
创建一个可以读取样式主题属性的ThemeReader 类。
class ThemeReader(
context: Context,
@StyleRes styleRes: Int
) {
private val attributes: IntArray = intArrayOf(R.attr.primary, R.attr.success, R.attr.error)
private val typedArray: TypedArray = context.obtainStyledAttributes(styleRes, attributes)
val colorPrimary: Int = typedArray.getColor(R.styleable.ThemeStyle_colorPrimary, -1)
val colorSecondary: Int = typedArray.getColor(R.styleable.ThemeStyle_colorSecondary, -1)
}
加载 Xml Drawable
为确保您加载的 xml drawable 知道您的主题颜色,您应该不使用 Compose 加载您的 drawable,例如:
Image(
painter = painterResource(iconTypeResId),
contentDescription = null
)
而不是以经典方式将其加载为 ImageView 并将其包装到 Compose AndroidView 中。让我们为此创建一个可组合的:
@Composable
fun XmlDrawable(
@DrawableRes drawableResId: Int,
) {
@StyleRes val styleRes: Int = AppTheme.style
AndroidView(factory = { context ->
val contextThemeWrapper = ContextThemeWrapper(context, styleRes)
val drawable = ResourcesCompat.getDrawable(context.resources, drawableResId, contextThemeWrapper.theme)
ImageView(context).apply { setImageDrawable(drawable) }
})
}
现在我们可以应用我们所做的了。
class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// instantiate our custom theme
AppTheme(
styleRes = R.style.RedTheme, // set the theme you wanna use
context = requireContext()
) {
XmlDrawable(drawableResId = R.drawable.your_drawable) // load the xml drawable
// you can also access any AppColor within the composition tree like: AppTheme.colors.primary
}
}
}
}
参考