前言
想必点进来的同学都吃过屏幕适配的苦,现在要么在寻找合适的解决方案,要么进来凑凑热闹,看看有无更好的方案。因为谷歌碎片化的问题,不管是SDK开发还是APP开发,只要有UI都无法绕过屏幕适配。当然了,谷歌官方也给出一些适配方法,但无法满足所有开发场景,并且不同的设备看起来效果也不尽相同。这时国内大厂站出来了,字节跳动团队带来了一份不错的参考答案,下面分享一下我是怎么抄作业的😁
一.SDK适配
首先作为SDK开发,直接改今日头条的方案就可以满足绝大多数的适配需求了。下面先来看下今日头条的适配方案:https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA
根据今日头条的适配方案,我们来重写一个屏幕适配工具类:
//今日头条适配法,以一边进行适配
private var sNoncompatDensity: Float = 0.0f
private var sNoncompatScaleDensity: Float = 0.0f
//目标DPI
private const val setDpi = 360.0f
private var normalDensity: Float = 0.0f
private var normalScaledDensity: Float = 0.0f
private var normalDensityDpi: Int = 0
private var targetDensity: Float = 0.0f
private var minWith = 1.0f
//修改屏幕密度方法
fun setCustomDensity(activity: Activity) {
val activityDisplayMetrics = activity.resources.displayMetrics
val appDisplayMetrics = activity.application.resources.displayMetrics
val activityOrientation = activity.resources.configuration.orientation
if (sNoncompatDensity == 0.0f){
normalDensity = activityDisplayMetrics.density
normalScaledDensity = activityDisplayMetrics.scaledDensity
normalDensityDpi = activityDisplayMetrics.densityDpi
sNoncompatDensity = appDisplayMetrics.density
sNoncompatScaleDensity = appDisplayMetrics.scaledDensity
//监听字体大小是否改变
activity.application.registerComponentCallbacks(object : ComponentCallbacks{
override fun onConfigurationChanged(newConfig: Configuration) {
if (newConfig.fontScale > 0){
sNoncompatScaleDensity = activity.resources.displayMetrics.scaledDensity
}
}
override fun onLowMemory() {
}
})
}
targetDensity = if (activityOrientation == Configuration.ORIENTATION_PORTRAIT) {
//竖屏
minWith = 0.9f
val densityPortrait: BigDecimal = BigDecimal((activityDisplayMetrics.widthPixels / setDpi).toString()).setScale(1,BigDecimal.ROUND_HALF_UP)
densityPortrait.toFloat()
} else{
//横屏
val densityLand: BigDecimal = BigDecimal((activityDisplayMetrics.heightPixels / setDpi).toString()).setScale(1,BigDecimal.ROUND_HALF_UP)
densityLand.toFloat()
}
val targetScaleDensity: Float = targetDensity * (sNoncompatScaleDensity / sNoncompatDensity)
val targetDensityDpi: Int = (160 * targetDensity).toInt()
activityDisplayMetrics.density = targetDensity
activityDisplayMetrics.scaledDensity = targetScaleDensity
activityDisplayMetrics.densityDpi = targetDensityDpi
}
//还原屏幕密度方法
fun resetDensity(activity: Activity) {
if (sNoncompatDensity != 0.0f && normalDensity != 0.0f) {
val activityDisplayMetrics = activity.resources.displayMetrics
activityDisplayMetrics.density = normalDensity
activityDisplayMetrics.scaledDensity = normalScaledDensity
activityDisplayMetrics.densityDpi = normalDensityDpi
}
}
//dp转px
fun dp2px(activity: Activity, dpValue: Int): Int {
if (targetDensity.equals(0.0f)) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dpValue.toFloat(),
activity.resources.displayMetrics).toInt()
}
return (dpValue * targetDensity + 0.5f).toInt()
}
这个工具类使用起来也很简单,Layout文件宽度设置为360,在需要展示的时候调用一下setCustomDensity方法修改屏幕精度,在关闭或者销毁UI时调用resetDensity方法还原屏幕密度,这样就不会对宿主APP本身的适配有影响啦。
二. APP适配
相比较上面的SDK适配,APP就显得麻烦多了,不但需要适配自己的UI,还需要去适配依赖的第三方插件的UI,这时就不能通过简单的设置/恢复屏幕密度来实现了。那咋办呢?别慌,JessYan大神针对今日头条的适配思路,开发了一个开源框架,还包含了详细的接入文档,感兴趣的同学可以去尝试一番。