Compose - 屏幕适配(尺寸、主题、状态栏、国际)

一、尺寸适配

View体系适配跳转

参考文章

        目前 Compose 只能采用 dp 单位设置尺寸, Dp.roundToPx() 方法与 View 体系中 applyDimension() 方法一样最终都是通过 px = dp * density 公式将 dp 转为 px。

        连接 Compose 和 View 体系之间的桥梁是 AndroidComposeView 类,而 AndroidComposeView 就通过 fun Density(context: Context) 方法来初始化其内部声明的 density 对象,CompositionLocals 类的 ProvideCommonCompositionLocals 方法又通过 LocalDensity 来将 AndroidComposeView 持有的 density 对象暴露给外部,从而使得框架内部和开发者均可以通过 LocalDensity.current 来获取到当前的 Density 对象,也即通过此方法拿到了 Android 系统的 density 和 fontScale 两个参数。

        得益于 CompositionLocalProvider 特性,能细腻度的控制修改后的密度生效范围(当前Activity甚至某个组合中)而不会影响全局,由次解决了View体系适配中字节跳动方案的缺点。

1.1 dp

smallestWidth 方案也一样适用于Compose,通过 dimensionResource( ) 方法获取 res 中 dimen 值会转换为 Dp 类型,如果项目中已经使用了 smallestWidth 方案依然可以继续使用。

/**
 * 根据UI设计图得出动态密度适配不同屏幕
 * @param designWidth 填入UI设计图的屏幕短边dp值(绝对宽度)
 * @param designHeight 填入UI设计图的屏幕长边dp值(绝对高度)
 */
@Composable
private fun dynamicDensity(designWidth: Float, designHeight: Float): Float {
    val displayMetrics = LocalContext.current.resources.displayMetrics
    val widthPixels = displayMetrics.widthPixels    //屏幕短边像素(绝对宽度)
    val heightPixels = displayMetrics.heightPixels  //屏幕长边像素(绝对高度)
    val isPortrait = LocalContext.current.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT  //判断横竖屏
    return if (isPortrait) widthPixels / designWidth else heightPixels / designHeight //计算密度
}
private val fontScale = LocalDensity.current.fontScale
private val appDensity = Density(density = dynamicDensity(360F, 640F), fontScale = fontScale)

MaterialTheme(
    colorScheme = colorScheme,
    typography = Typography,
    content = {
        CompositionLocalProvider(LocalDensity provides appDensity ) {
            content()
        }
    }
)

1.2 sp

px = sp * fontScale * density,fontScale 代表当前系统字体的缩放比例,通过 LocalDensity.current.fontScale 获取该值,可以自由控制应用内文字的显示大小。

1.3 BoxWithConstraints

提供了一些现有参数,用来根据可用空间调用不同的布局方案。

单位dpminWidth、minHeight、maxWidth、maxHeight
单位px

constraints.minWidth

constraints.minHeight

constraints.maxWidth

constraints.maxHeight

BoxWithConstraints {
    //根据可用宽度显示不同布局方案
    if(maxWidth < 400.dp) {
        Column{...}
    } else {
        Row{...}
    }
}

二、主题适配(颜色)

参考文章1

参考文章2

创建项目之后,就会生成一个 项目名称+Theme 的 @Compose 函数,我们可以通过更改其中的颜色来完成对主题的修改。硬编码在配置的地方把值写死会无法支持主题切换,应该从主题中引用对应值,由于每个值都是实例还能通过 copy() 修改。

2.1 定义不同版本的颜色名称和值

方式一:在 Color.kt 中定义颜色的不同版本名称和值。 

object AppColors {
    //正文
    val textPrimary_Light = Color(0xCC000000)
    val textPrimary_Dark = Color(0xFF626567)
    //红色
    val red = Color(0xFFFF0000)
    val red50 = Color(0x80FF0000)
    //透明
    val transparent = Color(0x00FFFFFF)
}

方式二:在 res/values/colors.xml 中定义颜色的不同版本名称和值。

<resources>
    //正文
    <color name="textPrimary_Light">#CC000000</color>
    <color name="textPrimary_Dark">#FF626567</color>
    //红色
    <color name="red">#FFFF0000</color>
    <color name="red50">#80FF0000</color>
    //透明
    <color name="transparent">#00FFFFFF</color>
</resources>

2.2 定义颜色集基类

  1. 在 Theme.kt 中创建颜色集类。
  2. 构造中塞入颜色名称供创建实例的时候指定具体颜色。
  3. 定义对应的属性并通过 MutableState 包裹提供状态,以便发生改变时所有引用该颜色的组合项会重组。
  4. 提供 copy() 支持调用处修改某个颜色。(可选)
class ThemeColors(
    //定义需要的颜色名称
    textPrimary: Color,
    background: Color,
) {
    //颜色是有状态的,发生改变时所有引用这个颜色的组合项都会重组
    var textPrimary by mutableStateOf(textPrimary)
        private set
    var background by mutableStateOf(background)
        private set
    //提供copy支持调用处修改某个颜色
//    fun copy(
//        textPrimary: Color = this.textPrimary,
//        background: Color = this.background,
//    ): ThemeColors = ThemeColors(
//        textPrimary = textPrimary,
//        background = background,
//    )
}

2.3 创建不同的颜色集实例(不同的主题色)

在 Theme.kt 中,创建不同的颜色集实例(即不同的主题),构造中指定具体的颜色值。暂不推荐从xml中读取,通过主题调用的颜色显示错误,未找到原因

//亮色主题(举例从 Color.kt 中读取)
private val LightColors = ThemeColors(
    textPrimary = AppColors.TextPrimary_Light,
    background = AppColors.transparent,
)
//暗色主题(举例从 colors.xml 中读取)
private val DarkColors = ThemeColors(
    textPrimary = Color(R.color.textPrimary_Dark),
    background = Color(R.color.transparent),
)

 2.4 定义全局调用入口

在 Theme.kt 中,声明一个由 CompositionLocal 包装的变量用来获取颜色集实例,声明一个单例用作全局调用入口,还包含了对类型的判断。

//声明一个CompositionLocal变量供调用处获取自定义的颜色集实例
//颜色集对象中的颜色值是确定的不会更改的,因此使用staticCompositionLocalOf 的提高性能
private val LocalAppColors = staticCompositionLocalOf { LightColors }

//声明一个单例,用作全局入口
object AppTheme {
    //用于获取自定义的颜色集
    val colors: ThemeColors
        @Composable get() = LocalAppColors.current
    //提供枚举类用于传参判断类型
    enum class ColorsType {
        LIGHT, DARK
    }
}

2.5 提供切换功能

@Composable
fun AppTheme(
    colorsType: AppTheme.ColorsType = AppTheme.ColorsType.LIGHT,    //指定颜色集
    darkTheme: Boolean = isSystemInDarkTheme(),    //自动识别是否开启暗黑模式
    content: @Composable () -> Unit    //需要包裹的内容
) {
    //获取指定的颜色方案并添加动画(避免切换的时候一闪)
    val themeColors = if (darkTheme) addAnimationToAPPColors(DarkColors) else{
        when (colorsType) {
            AppTheme.ColorsType.LIGHT -> addAnimationToThemeColors(LightColors)
            AppTheme.ColorsType.DARK -> addAnimationToThemeColors(DarkColors)
        }
    }
    //提供出去
    CompositionLocalProvider(LocalAppColors provides themeColors){
        //同时使用MaterialTheme的话就在这里包裹一下
        content()
    }
}

2.6 添加切换动画(避免直接一闪)

/**
 * 为自定义颜色集添加过度动画(避免直接一闪)
 */
@Composable
private fun addAnimationToThemeColors(themeColors: ThemeColors) = ThemeColors(
    textPrimary = animateColorAsState(themeColors = targetColor.textPrimary, TweenSpec(600), label = "textPrimary").value,
    background = animateColorAsState(themeColors = targetColor.background, TweenSpec(600), label = "background ").value,
)

2.7 组合项中调用颜色

Text(text = "", color = AppTheme.colors.textPrimary)

三、沉浸式状态栏

谷歌 Accompamost 的 SystemUiController 已废弃,建议使用 androidx.activity 1.8.0-alpha03+ 添加的 enableEdgeToEdge() 方法。

implementation "androidx.activity:activity-ktx:1.8.0-alpha07"
onCreate() {
        WindowCompat.setDecorFitsSystemWindows(window,false)    //使内容全屏
        enableEdgeToEdge()  //使透明状态栏
}

四、国际化

一样是提供多套strings文件。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值