AndroidAutoSize icon indicating copy to clipboard operation
AndroidAutoSize copied to clipboard

getResources() 也不能解决所有问题,导致屏幕适配失效的另外一种原因以及解决方案

Open JessYanCoding opened this issue 5 years ago • 4 comments

我在 https://github.com/JessYanCoding/AndroidAutoSize/issues/13 中提到过,由于 DisplayMetrics#density 在整个程序运行期间都是 public 公有的,所有不光是 AndroidAutoSize 可以修改它的值,系统、三方库、以及其他代码,也都有权限修改它的值,一旦在某个页面绘制之前,DisplayMetrics#density 的值不是经过 AndroidAutoSize 计算过的正确值,那就会出现屏幕适配失效的问题。

只要 DisplayMetrics#density 的值在某个页面绘制之前,保证是经过 AndroidAutoSize 计算过的正确值,那就能大概率保证屏幕适配能够正常完成,所以我们现在的终极解决方案就是在页面显示到屏幕上的 0.0000000000000000000000000001 秒之前,将 DisplayMetrics#density 修改为正确的值,所以我们的解决方案是在 getResources() 中,重新设置 DisplayMetrics#density。

 @Override
    public Resources getResources() {
        //需要升级到 v1.1.2 及以上版本才能使用 AutoSizeCompat
        AutoSizeCompat.autoConvertDensityOfGlobal(super.getResources());//如果没有自定义需求用这个方法
        AutoSizeCompat.autoConvertDensity(super.getResources(), 667, false);//如果有自定义需求就用这个方法
        return super.getResources();
    }

由于在系统中所有参与单位转换的场景中都会调用 getResources() 方法,所以这就能保证 DisplayMetrics#density 的值始终是正确的。

但是接到一些 issues 的反馈,在实际开发中,即使重写 getResources() 也可能会导致屏幕适配失效,这里先抛开一些新手,由于不理解今日头条屏幕适配最基本的知识,而导致的适配问题。

我在这就谈谈即使在页面绘制的 0.0000000000000000000000000001 秒之前,保证 DisplayMetrics#density 的值为正确值,绘制出来的页面可能会屏幕失效吗?

答案是,有可能的,使用 XML 编写布局的项目可能会有这个问题。

关于适配失效的原因,这里就要谈到,关于系统源码层的东西,我在这里就简单说一下。

在 Activity#onCreate 中将页面的 Layout ID 传给 setContentView 方法后,其实不是立即进行页面绘制的,系统需要将 XML 的节点全部解析出来,并通过反射生成对应的 View 对象,在创建 View 对象的过程中需要创建 LayoutParams,并将 XML 中声明的 Width、Height、Margin 等属性全部存储到 LayoutParams 当中,在 View OnMeasure 时,会用到这个 LayoutParams 来确定 View 各个属性最终的值。

所以在 Activity#onCreate setContentView() 时,只是保证将 XML 中声明的 View 节点,全部转化为 View 对象,并将 XML 中声明的 View 属性全部保存在这个 View 对象中,这个页面的绘制还是要等到 ViewRootImpl 执行 performTraversals() 时才能完成。

View 对象的生成和页面完成绘制是有时间间隔的,在生成 View 对象,创建 LayoutParams 时,保存的 Width、Height、Margin 等属性是按照当时的 DisplayMetrics#density 计算的,如果在创建 LayoutParams 时 DisplayMetrics#density 的值不是正确值,这会导致 LayoutParams 中保存的 Width、Height、Margin 等属性的值也不是正确值,但是在 View onMeasure 中的默认逻辑,又会直接使用 LayoutParams 中保存的属性值,所以即使你在 onMeasure 之前保证 DisplayMetrics#density 是正确的值,也可能会出现适配失效的问题。

所以如果你的项目是通过 XML 来编写布局,你应该保证在 LayoutParams 创建时,DisplayMetrics#density 为正确值,所以解决方案是,重写 父布局 的 generateLayoutParams(AttributeSet),并在方法中设置正确的 DisplayMetrics#density, 注意是 父布局

@Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        AutoSizeCompat.autoConvertDensityOfGlobal(getResources());//如果没有自定义需求用这个方法
        AutoSizeCompat.autoConvertDensity(getResources(), 667, false);//如果有自定义需求就用这个方法
        return super.generateLayoutParams(attrs);
    }

以上是我分析系统源码时,发现的可能会导致屏幕失效的问题,给的这个解决方案,可能会解决你的问题,也可能解决不了,如果能解决你的问题,望留言告知,这个解决方案配合 getResources() 应该就比较稳定了。

如果还是解决不了你的问题,那就需要你自己打断点,具体问题具体分析了,看看是从 setContentView 到 onMeasure 的哪个阶段出现了问题,今日头条屏幕适配方案也就 DisplayMetrics#density 是公有可被随时修改,这一个问题,当然这个特性也给我们带来了很多便利,你们反馈的所有问题其实都是这一个问题导致的,只不过是不同的呈现方式而已,如果所有方案都解决不了适配失效,那就重写 onMeasure,自己测量,重写 onMeasure 是可以百分百解决所有适配问题的,只不过需要你有一定基础。

JessYanCoding avatar Jun 24 '20 07:06 JessYanCoding

大佬好,当我在父布局中设置 父布局 的 generateLayoutParams(AttributeSet)时 app没有出现适配失效的问题,但在xml中不能预览布局,点开错误和警告信息出现:

java.lang.IllegalArgumentException: you must set design_width_in_dp in your AndroidManifest file   at me.jessyan.autosize.utils.Preconditions.checkArgument(Preconditions.java:39)   at me.jessyan.autosize.AutoSizeConfig.getDesignWidthInDp(AutoSizeConfig.java:432)   at me.jessyan.autosize.AutoSizeCompat.autoConvertDensityOfGlobal(AutoSizeCompat.java:54)   at com.basic.modular.view.widget.AutoSizeLinearLayout.generateLayoutParams(AutoSizeLinearLayout.java:27)   at com.basic.modular.view.widget.AutoSizeLinearLayout.generateLayoutParams(AutoSizeLinearLayout.java:11)   at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1125)   at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1097)   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)   at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1126)   at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1097)   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)   at android.view.LayoutInflater.inflate(LayoutInflater.java:682)   at android.view.LayoutInflater.inflate(LayoutInflater.java:501)

senonwx avatar Sep 07 '20 07:09 senonwx

大佬好,当我在父布局中设置 父布局 的 generateLayoutParams(AttributeSet)时 app没有出现适配失效的问题,但在xml中不能预览布局,点开错误和警告信息出现:

java.lang.IllegalArgumentException: you must set design_width_in_dp in your AndroidManifest file   at me.jessyan.autosize.utils.Preconditions.checkArgument(Preconditions.java:39)   at me.jessyan.autosize.AutoSizeConfig.getDesignWidthInDp(AutoSizeConfig.java:432)   at me.jessyan.autosize.AutoSizeCompat.autoConvertDensityOfGlobal(AutoSizeCompat.java:54)   at com.basic.modular.view.widget.AutoSizeLinearLayout.generateLayoutParams(AutoSizeLinearLayout.java:27)   at com.basic.modular.view.widget.AutoSizeLinearLayout.generateLayoutParams(AutoSizeLinearLayout.java:11)   at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1125)   at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1097)   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)   at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1126)   at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1097)   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)   at android.view.LayoutInflater.inflate(LayoutInflater.java:682)   at android.view.LayoutInflater.inflate(LayoutInflater.java:501)

override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { if (!isInEditMode) { try { val context = this.context if (context is CustomAdapt) { AutoSizeCompat.autoConvertDensityOfGlobal(super.getResources()) AutoSizeCompat.autoConvertDensity(super.getResources(), context.sizeInDp, context.isBaseOnWidth) } } catch (ignored: Exception) {} } return super.generateLayoutParams(attrs) }

magiQAQ avatar Mar 22 '23 10:03 magiQAQ

没有设置desiginInDp吗?

------------------ Original ------------------ From: "JessYanCoding/AndroidAutoSize" @.>; Date: Wed, Mar 22, 2023 06:37 PM @.>; @.@.>; Subject: Re: [JessYanCoding/AndroidAutoSize] getResources() 也不能解决所有问题,导致屏幕适配失效的另外一种原因以及解决方案 (#299)

大佬好,当我在父布局中设置 父布局 的 generateLayoutParams(AttributeSet)时 app没有出现适配失效的问题,但在xml中不能预览布局,点开错误和警告信息出现:

java.lang.IllegalArgumentException: you must set design_width_in_dp in your AndroidManifest file   at me.jessyan.autosize.utils.Preconditions.checkArgument(Preconditions.java:39)   at me.jessyan.autosize.AutoSizeConfig.getDesignWidthInDp(AutoSizeConfig.java:432)   at me.jessyan.autosize.AutoSizeCompat.autoConvertDensityOfGlobal(AutoSizeCompat.java:54)   at com.basic.modular.view.widget.AutoSizeLinearLayout.generateLayoutParams(AutoSizeLinearLayout.java:27)   at com.basic.modular.view.widget.AutoSizeLinearLayout.generateLayoutParams(AutoSizeLinearLayout.java:11)   at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1125)   at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1097)   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)   at android.view.LayoutInflater.rInflate_Original(LayoutInflater.java:1126)   at android.view.LayoutInflater_Delegate.rInflate(LayoutInflater_Delegate.java:72)   at android.view.LayoutInflater.rInflate(LayoutInflater.java:1097)   at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)   at android.view.LayoutInflater.inflate(LayoutInflater.java:682)   at android.view.LayoutInflater.inflate(LayoutInflater.java:501)

override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams { if (!isInEditMode) { try { val context = this.context if (context is CustomAdapt) { AutoSizeCompat.autoConvertDensityOfGlobal(super.getResources()) AutoSizeCompat.autoConvertDensity(super.getResources(), context.sizeInDp, context.isBaseOnWidth) } } catch (ignored: Exception) {} } return super.generateLayoutParams(attrs) }

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

yiwanwanwan avatar Mar 29 '23 07:03 yiwanwanwan

在RecyclerView的requestLayout也加下

override fun requestLayout() {
    AutoSizeCompat.autoConvertDensityOfGlobal(context.resources)
    super.requestLayout()
}

hwmin avatar Jul 05 '23 07:07 hwmin