今日头条屏幕适配方案



原理:

根据设计图宽度,算出density(可以理解为密度),继而算出dpi,替换系统中的dpi.则XML即可按照UI所给设计图设置宽度.达到适配目的.

转自浪货:https://blog.csdn.net/u013000152/article/details/80855315

使用

直接在Application的onCreate方法中调用(Density类直接参照附录)

Density.setDensity(this, 375f);
  • 1

这个地方我们需要注意375f这个参数,针对这个参数,我们来好好说说。

375这个值是一个UI图的参照值,单位是dp,我参照的是1334*750像素的图

image

然后我们来看下px与dp的转换图

Android单位:dp
MDPI1px=1dp
HDPI1.5px=1dp
XHDPI2px=1dp
XXHDPI3px=1dp
XXXHDPI4px=1dp

因为我们UI使用的是蓝湖的UI设计,可以根据这些不同的DPI来切换显示不同布局的不同宽度dp值

然后我们继续看那个参数值375,因为我参照的是XHDPI的图进行绘制的,2px=1dp,375这个值就是宽度的像素750px/2px=375dp

然后我们切换到XHDPI进行看看是不是375dp

image

那么,我们就可以参照上面这个图给出的UI进行设置了,我这个地方给了个测试例子,375dp的话,要想能看出是否精准适配的话,我们可以这样,采用LineaLayout的vertical布局,靠左边设置一个宽度为200dp宽度的widget,靠右设置一个175dp宽度的widget,如果他们两个相交的地方刚好撘上,那么,这就是一个精准的适配

image

其他几个不同像素的就不展示了,我都测试过了,非常完美解决

对了,还有一个问题,有的UI给出的并不是像我们这种dp方式的,有的UI给的单位是px,其实,这也是很好解决的,比如UI给的是1080*1920的图,那我们就按照XHDPI 2px=1dp的方式来适配,那么,我们设置setDensity的值就为1080/2=540dp,然后UI凡是标注多少px,我们就在布局中给这个px除以2,然后设置到布局上即可

注意:我们在做dp与px换算的时候,尽量保持这个结果dp值在320~560之间,为什么呢?看下面表述
  • 1

我们看上面的换算表格MDPI 1px=1dp,既然1px=1dp,那么我们怎么不直接给setDensity设置750呢,这样,我还省去了计算,直接参照MDPI的图来做,好,我们就这么来一次,给大家看看效果,最后再来说原因。

参照是750*1334的UI,setDensity的值也设置750,然后设置左widget的宽度为400dp,右边的widget设置375dp,看图

image

确实能宽度适配,但大家看看,这个字体和页面明显有缩小的感觉,这种适配虽然宽度达到了,但明显不是我们想要的,那是什么原因导致的呢?这时候,我们就要引出一个关键因素像素密度dpi,我来列出两个公式

dp=px/dpi*160

dpi=px*160/dp
  • 1
  • 2
  • 3

这个计算公式是系统通过我们给定xml的值来计算出显示在屏幕上的布局,附录的代码就是利用的这个公式给系统设置计算参数,然后我们把刚刚MDPI适配的方式计算一下dpi

750*160/750=160dpi
  • 1

160这个密度值明显过低,导致1密度下面占据太多的像素,界面看起来很拥挤,然后我们看下XHDPI的值,750的px等于375dp

750*160/375=320dpi
  • 1

dpi的值是系统给的,大概查阅了流行的机子的dpi,就是我上面列举的范围内,在我们知道像素宽度的时候,尽量参考Xhdpi来做,这也是我们在适配图片的时候,喜欢参照xhdpi的原因

其实,当我说到这的时候,原因其实差不多也都说出来了,虽然dpi是系统给的,但我们可以通过getDisplayMetrics替换掉这个dpi,上面的公式也给了,我们可以通过getDisplayMetrics拿到widthPixels屏幕的宽度像素值,然后再加上我们通过换算拿到的dp值,那dpi的值也就可以知道了,然后将这些值都设置到系统里面,让系统在解析xml布局的时候,按照我们给他设定的值来计算,然后显示在界面上,这就是适配的原理。


有人说,我是老项目了,如果按照动态这么适配的话,那岂不之前的都要改?确实,但不要担心,还有另一种适配方式,smallWidth适配,修改系统的值属于动态方式来做,那么sw适配方式就属于静态适配了,sw也是根据上面的公式,计算出对应的dp值生成xml,生成xml的java代码也有,是一个哥们做的,大家可以看看他的文章,看这,也挺不错的,计算方式需要自己知道自己适配的机型的dpi,然后通过公式计算出dp值,然后将dp值设置到java代码中,然后运行一下,就可以了,将生成的values放到res文件夹下面就可以了

附录

Application里面调用

    @Override
    public void onCreate() {
     //记住,这个值需要自己根据UI图计算的哦,可不要直接抄
     Density.setDensity(this, 360);//360为UI提供设计图的宽度
    }
  • 1
  • 2
  • 3
  • 4
  • 5

Density.java

public class Density {
    private static float appDensity;
    private static float appScaledDensity;
    private static DisplayMetrics appDisplayMetrics;
    /**
     * 用来参照的的width
     */
    private static float WIDTH;

    public static void setDensity(@NonNull final Application application, float width) {
        appDisplayMetrics = application.getResources().getDisplayMetrics();
        WIDTH = width;
        registerActivityLifecycleCallbacks(application);

        if (appDensity == 0) {
            //初始化的时候赋值
            appDensity = appDisplayMetrics.density;
            appScaledDensity = appDisplayMetrics.scaledDensity;

            //添加字体变化的监听
            application.registerComponentCallbacks(new ComponentCallbacks() {
                @Override
                public void onConfigurationChanged(Configuration newConfig) {
                    //字体改变后,将appScaledDensity重新赋值
                    if (newConfig != null && newConfig.fontScale > 0) {
                        appScaledDensity = application.getResources().getDisplayMetrics().scaledDensity;
                    }
                }

                @Override
                public void onLowMemory() {
                }
            });
        }
    }


    private static void setDefault(Activity activity) {
        setAppOrientation(activity);
    }

    private static void setAppOrientation(@Nullable Activity activity) {

        float targetDensity = 0;
        try {
            targetDensity = appDisplayMetrics.widthPixels / WIDTH;
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        float targetScaledDensity = targetDensity * (appScaledDensity / appDensity);
        int targetDensityDpi = (int) (160 * targetDensity);

        /**
         *
         * 最后在这里将修改过后的值赋给系统参数
         *
         * 只修改Activity的density值
         */

        DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
        activityDisplayMetrics.density = targetDensity;
        activityDisplayMetrics.scaledDensity = targetScaledDensity;
        activityDisplayMetrics.densityDpi = targetDensityDpi;
    }


    private static void registerActivityLifecycleCallbacks(Application application) {
        application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                setDefault(activity);
            }
            @Override
            public void onActivityStarted(Activity activity) {
            }
            @Override
            public void onActivityResumed(Activity activity) {
            }
            @Override
            public void onActivityPaused(Activity activity) {
            }
            @Override
            public void onActivityStopped(Activity activity) {
            }
            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }
            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }
}

展开阅读全文

没有更多推荐了,返回首页