安卓屏幕适配解决方案

安卓屏幕适配解决方案

国内安卓手机众多,由于安卓是开源的,不同厂商都可以定制属于自己的系统,定制这就导致了屏幕的不同意,给开发人员带来了很多困扰啊,屏幕适配就是很明显的一个问题。

我们先引入几个概念 dp px dpi density

  • dp:dp=dip只是说法不同
  • px:像素 (这个就不用多说了)
  • dpi:每英寸的像素点数。
  • density:每平方英寸像素点数。

这里给出他们之间的换算公式:

px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
  • 这里备注一下为啥换算计算为用到160dp这个数值,怎么说呢,这就是好像一个基准,一个参照物,1厘米一样,有了1厘米做基准你才有1米。
  • 为啥会取160dp呢,我也不清楚,可能是因为第一款机型是160dpi吧。

好的,上面的一些概念性的问题我们介绍问题了,现在言归正传我们该怎么来进行安卓原生屏幕的适配呢?

  • 做过网页的都知道我们在css中我们有个 @media only screen and()语法,这个可以帮助我们在一套html上使用多套css。>语法,这个可以帮助我们在一套html上使用多套css。
  • 又或者你做过微信小程序开发也会知道微信小程序为了解决屏幕适配问题提出了 rpx 作为统一单位,以此来解决屏幕适配的问题。
  • 再或者你做过flutter开发,你也会知道在flutter框架里我们可以用 flutter_screenutil 框架来解决屏幕适配性问题,这种框架呢,我们给它一个屏幕参照数据就可以根据这个数据来把我们的组件按照给的数据进行处理了。
// 默认设置宽度1080px,高度1920px
ScreenUtil.instance = ScreenUtil.getInstance()..init(context);


上面的都是别人处理和我们安卓没啥关系,咱们也用不了别人的,说出来就是气气你们,不过虽然用不了但是我们可以借鉴一下别人的思路,下面我提出的安卓屏幕解决方案就借鉴了flutter的屏幕适配框架的解决思路。

安卓我们使用的dp单位最后都会被android sdk自动转换为px,然后进行绘制组件显示等等一系列的操作。
那么我们看上面的公司 dp 是怎么转换为 px的呢,px = density * dp ,dp乘以每平方英寸的像素点就是px了。那么dp我们在写xml布局的时候就已经写死了,那么px我们也改不了,现在只能在 desity 上做手脚了,我们先来看看这个 density 怎么计算的得来的:
density = dpi / 160;(每英寸的像素点数/160)
那么dpi又是怎么计算出来的呢,下面我举个例子,一部手机它的分辨率为1920*1080,屏幕尺寸为5寸,那么dpi就是440。(注意这里的440是近似之后不是精确值,不过影响不大)

dpi=√(width^2 x height^2)/屏幕尺寸

这样我们就计算出了dpi的值,从而我们也计算出来我们需要的density的值。可以看出density的值也是由屏幕分辨率以及屏幕尺寸大小计算得出的。
如果我们能自己指定系统的这个 density 的值,是不是也能控制最后绘制出来的px值,答案是肯定的。
density 是 DisplayMetrics 中的成员变量,而 DisplayMetrics 实例通过 Resources#getDisplayMetrics 可以获得,而Resouces通过Activity或者Application的Context获得。
1.DisplayMetrics#density 就是上述的density
2.DisplayMetrics#densityDpi 就是上述的dpi
3.DisplayMetrics#scaledDensity 字体的缩放因子,正常情况下和density相等,但是调节系统字体大小后会改变这个值
图片也是通过这个把dp进行转换px,所以这里更改以后图片也能进行适配,不过要注意图片本身的分辨率哦。

这里我的适配思路,是给定义一个手机的屏幕的参照物(比如宽800 高1280 尺寸7寸)然后如果你需要适配的机型(宽1080 高1280 尺寸5寸),我们计算出来的比例值为592,就会用宽去除以这个592得出一个最佳比例值。
第一步 根据的参考数据,算出一个density值,这个值在你给的标准数据上的模型机是完全适配的。
第二步 我们根据算出来的desity值,计算得出一个比例值比如现在的数据模型我们算出 800/1.35=592.595293 这个数值就是我们需要的一个比例值。
第三步 获取当前屏幕的宽度然后除以第二部计算出来的比例值得出我们需要density。
第四步 更改系统默认的density值即可。
(字体大小计算思路类似,这里省略。)
(这里是以宽度作为标准,如有特殊需要可以改为高度)

这里我们给的数据模型应该是什么,我们给的数据模型应该是我们设计师图稿上的大小数据,或者你布局使用显示模拟器的大小数据。

下面,我贴出我的终极代码:

/**
 * 这里我参考了今日头条的屏幕适配方案以及flutter框架屏幕适配解决方案进行了设计
 * 思路如下:
 * 选择一个设计的屏幕大小
 * 比如这里我使用 Nexus 7(2012) API7  分辨率为:800 x 1200 屏幕尺寸:7英寸 作为参考标准
 *    px = density * dp;
 *    density = dpi / 160;
 *    px = dp * (dpi / 160);
 *  我们需要传入一个标准数据即可
 *  即使一个标准手机的 宽度 高度 尺寸
 *  所有屏幕将会按照这个标准去进行比例缩放
 */
public class setCustomDensity {

    private static float sNoncompatDensity;
    private static float sNoncompatScaledDensity;
    private static Application application;
    public static void setCustomDensity(@NonNull Activity activity,int width,int height,int size){
        application=activity.getApplication();
        DisplayMetrics appDisplayMetrics=application.getResources().getDisplayMetrics();
        //判断是否是第一次初始化操作
        //赋值
        if (sNoncompatDensity==0){
            sNoncompatDensity=appDisplayMetrics.density;
            sNoncompatScaledDensity=appDisplayMetrics.scaledDensity;
            /**
             * 如果在系统设置中切换字体,再返回应用,字体并没有变化。于是还得监听下字体切换,
             * 调用 Application#registerComponentCallbacks 注册下 onConfigurationChanged 监听即可。
             */
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    if (newConfig!=null&&newConfig.fontScale>0){
                        sNoncompatScaledDensity=application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {

                }
            });
        }
        //开始进行适配计算
        //得到相应机型的dpi
        //appDisplayMetrics.widthPixels得到设备真实宽(单位px)
        //800 1.35  800宽度屏幕
        //1080 1.82  1080宽度屏幕
        //1440 2.43  1440宽度屏幕
        double inpn = Math.sqrt(Math.pow(width,2)+Math.pow(height,2))/size/160;
        //这里还有一个精确度问题
        //如果你的app布局不需要那么精度的布局一般都需要,误差应在2-5dp内
        //因为 1.这里是double类型  2.上面计算取的数值都会有误差
        double bl = width/(Double.parseDouble(String.valueOf(inpn).substring(0,4)));
        double value = appDisplayMetrics.widthPixels/bl;
        /**
         * 计算字体大小
         * 原字体数值*当前屏幕比例值
         */
        float targetScaleDensity= (float) (value*(sNoncompatScaledDensity/sNoncompatDensity));
        int targetDensityDpi=(int)(160*inpn);
        /**
         * targetDensity单位dp 即使现在屏幕dp与标准160dp的一个比例值
         * 用于下面的字体大小计算
         * 以及屏幕组件大小设置
         * 这里以宽度作为比值
         * 如果对高度有要求建议使用高度作为比值进行计算
         * 一般来说我们以宽度作为比值然后高度设置为自适应即滚动状态
         */
        appDisplayMetrics.density= (float) value; //*****
        appDisplayMetrics.scaledDensity=targetScaleDensity; //*****
        appDisplayMetrics.densityDpi=targetDensityDpi; //换算 标准是160 当前屏幕每英寸的dpi

    }

}

使用前需要在setContentView()方法之前调用哦。

使用了适配方案的效果如下:

平板 竖屏横屏


手机 1080 x 1980 5寸 竖屏横屏


标准手机(即是给定数据的模型机子) 800 x 1280 7寸 竖屏横屏

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v3xCJuyU-1572699105732)(http://zhx01.xiaoxingxing.online/2019/11/02/f825824e4010a35c80980e28cf98d0fa.png)]

联系方式:
qq:634448817
tel:15720989670

发布了20 篇原创文章 · 获赞 3 · 访问量 438

猜你喜欢

转载自blog.csdn.net/weixin_41078100/article/details/102876409
今日推荐