Android 屏幕适配
背景
背景就是要把之前在手机上做的一个项目移植到一个小屏设备上,但是前期小屏设备又没有到位,所以才会需要屏幕适配。
本文主要是记录作者在适配的过程中用到的一些知识以及参考的文章,做个总结,以便以后遇到时可以更方便的用起来。
adb命令修改设备屏幕参数
使用adb命令可以直接修改屏幕分辨率,这样大大的方便了屏幕适配的工作,你可以直接在已有的设备上看到不同分辨率下的效果。
- 查看屏幕尺寸
adb shell wm size
- 查看屏幕密度
adb shell wm density
- 同时查看屏幕尺寸和密度
adb shell wm size && adb shell wm density
- 设置屏幕尺寸
adb shell wm size 1440x3216
- 设置屏幕密度
adb shell wm density 640
- 同时设置屏幕尺寸和密度
adb shell wm size 1440x3216 && adb shell wm density 640
运行时代码适配屏幕
在应用启动时对屏幕进行适配,可以进行宽度适配或者高度适配。
经典文章,致敬前人:
-
今日头条的适配方案: 一种极低成本的Android屏幕适配方式(mp.weixin.qq.com/s/d9QCoBP6k…
-
JessYan的AndroidAutoSize介绍及仓库地址:
今日头条屏幕适配方案终极版正式发布(juejin.cn/post/684490…
AndroidAutoSize(github.com/JessYanCodi…
安卓系统在渲染前会把dp、sp转换成px,而转换的比例因子就是density和scaledDensity,具体关系就是:
- px = density * dp
- density = densityDpi / 160
插播:markdown插入公式参考(zhuanlan.zhihu.com/p/158156773…
dp与px的关联就是density,进行适配需要修改density,除了density,还有一个 scaledDensity,这个参数是字体缩放因子,一般和density相等,但是调节系统字体大小后会改变这个值。
density、scaledDensity和densityDpi都是displayMetrics中的变量,而displayMetrics可以通过3种方式获得,但每个的作用范围是不同的:
- 系统的屏幕尺寸:val systemDM = Resources.getSystem().displayMetrics
- app整体的屏幕尺寸:val appDM = activity.application.resources.displayMetrics
- activity的屏幕尺寸:val activityDM = activity.resources.displayMetrics
但是在Android8.0之后, appDM 和 activityDM 获取的同一个类。Resources.getSystem().displayMetrics这个是整个Android设备的,一般不能改。
屏幕适配的核心代码如下:
根据宽度进行屏幕适配
/**
* 根据UI设计尺寸的宽度适配屏幕
*
* @param application
* @param uiWidthDp
*/
fun adapterScreenByUIWidth(application: Application, uiWidthDp: Int) {
// app整体的屏幕尺寸
val appDM = application.resources.displayMetrics
if (targetDensity != 0f) {
appDM.density = targetDensity
appDM.scaledDensity = targetScaledDensity
appDM.densityDpi = targetDensityDpi
return
}
// 系统的屏幕尺寸
val systemDM = Resources.getSystem().displayMetrics
appDM.density = appDM.widthPixels.toFloat() / uiWidthDp
appDM.scaledDensity = appDM.density * (systemDM.scaledDensity / systemDM.density)
appDM.densityDpi = (160 * appDM.density).toInt()
application.registerComponentCallbacks(object : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {
adapterScreenByUIWidth(application, uiWidthDp)
}
override fun onLowMemory() {}
})
targetDensity = appDM.density
targetScaledDensity = appDM.scaledDensity
targetDensityDpi = appDM.densityDpi
}
根据高度进行屏幕适配
/**
* 根据UI设计尺寸的高度适配屏幕
*
* @param application
* @param uiHeightDp
*/
fun adapterScreenByUIHeight(application: Application, uiHeightDp: Int) {
// app整体的屏幕尺寸
val appDM = application.resources.displayMetrics
if (targetDensity != 0f) {
appDM.density = targetDensity
appDM.scaledDensity = targetScaledDensity
appDM.densityDpi = targetDensityDpi
return
}
// 系统的屏幕尺寸
val systemDM = Resources.getSystem().displayMetrics
appDM.density = appDM.heightPixels.toFloat() / uiHeightDp
appDM.scaledDensity = appDM.density * (systemDM.scaledDensity / systemDM.density)
appDM.densityDpi = (160 * appDM.density).toInt()
application.registerComponentCallbacks(object : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {
adapterScreenByUIHeight(application, uiHeightDp)
}
override fun onLowMemory() {}
})
targetDensity = appDM.density
targetScaledDensity = appDM.scaledDensity
targetDensityDpi = appDM.densityDpi
}
恢复系统屏幕密度
/**
* 恢复系统原来的density
*
* @param activity
*/
fun resetScreen(activity: Activity) {
// 系统的屏幕尺寸
val systemDM = Resources.getSystem().displayMetrics
// app整体的屏幕尺寸
val appDM = activity.application.resources.displayMetrics
// activity的屏幕尺寸
val activityDM = activity.resources.displayMetrics
activityDM.density = systemDM.density
activityDM.scaledDensity = systemDM.scaledDensity
activityDM.densityDpi = systemDM.densityDpi
appDM.density = systemDM.density
appDM.scaledDensity = systemDM.scaledDensity
appDM.densityDpi = systemDM.densityDpi
}