当前位置:   article > 正文

Android 屏幕适配★_android 屏幕密度

android 屏幕密度

1.像素密度、屏幕密度、分辨率等概念

①屏幕大小

屏幕大小指的是手机对角线的物理尺寸,以英寸(inch)为单位。英寸为国外的长度单位,它换算为国内的单位为:1英寸 = 2.54厘米。

一般手机尺寸有4英寸、4.5英寸、5.0英寸、5.2英寸、5.4英寸、5.99英寸、6.0英寸、6.2英寸等。

②屏幕分辨率

分辨率是手机屏幕的像素点总数,一般用屏幕宽的像素点数乘以屏幕高的像素点数。分辨率越大屏幕越细腻,能够显示的细节就越多。

常用的分辨率有320x240、640x480、1280x720、1280x960、1080x1920、2560x1440等,单位是像素。比如1080x1920表示屏幕宽度方向上有1080个像素,屏幕高方向上有1920个像素。

获取屏幕分辨率:

int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕宽

int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高

③像素密度densityDpi

像素密度(dpi,dots per inch;或PPI,pixels per inch)指每英寸的屏幕中包含的像素数量。

比如像素密度densityDpi为160,则表示每英寸屏幕中的像素点有160个,也就是说真实手机屏幕上,每2.54厘米就包含有160个像素点在里面,当然了,量长度还可以,像素点是看不见的,因为像素点非常非常的小。dpi为480的手机,理论是要比dpi为160的手机清晰很多很多,因为同样1英寸的屏幕大小,一个手机可以使用480个像素点来显示图像,一个只能用160个像素点来显示图像,效果肯定是差很多的。

④屏幕密度density

屏幕密度其实是像素密度的另外一种表示,是以160dpi=1.0为基准的。手机出厂之后屏幕密度,包括X,Y轴方向的像素密度都是固定值。

android以像素密度160dpi为基准对屏幕进行划分,当像素密度为160dpi时屏幕密度为1.0,像素密度为120dpi时屏幕密度为0.75,像素密度为320dpi时屏幕密度为2.0。

因此屏幕密度可以理解为密度的比例,也可以理解为dp换算为像素的比例(即1个dp等于几个像素)。标准的屏幕密度为160,它的密度比例就是1,即1个dp就等于1个像素。如果手机的像素密度densityDpi为320,则它是标准屏幕密度的两倍(320 / 160 = 2),则density = 2,表示1个dp就等于2个像素。举个例子,比如手机的像素密度densityDpi为320,然后你设置了一个控件的宽为60dp,则它显示到屏幕上的实际宽度为120像素,因为density = 2,所以60 * 2px = 120px。

获取像素密度和屏幕密度:

DisplayMetrics dm = new DisplayMetrics();

dm = getResources().getDisplayMetrics();

float density = dm.density; // 屏幕密度(像素比例:0.75/1.0/1.5/2.0)

int densityDPI = dm.densityDpi; // 像素密度(每寸像素:120/160/240/320)

float xdpi = dm.xdpi; //X轴方向的像素密度

float ydpi = dm.ydpi; //Y轴方向的像素密度

screenWidth = dm.widthPixels; // 屏幕宽

screenHeight = dm.heightPixels; // 屏幕高

 

2.多个drawable如何加载

做安卓屏幕适配的时候,首先要知道不同drawable的含义:

.drawable-ldpi (dpi=120, density=0.75)

.drawable-mdpi (dpi=160, density=1)

.drawable-hdpi (dpi=240, density=1.5)

.drawable-xhdpi (dpi=320, density=2)

.drawable-xxhdpi (dpi=480, density=3)

.drawable-xxxhdpi (dpi=640, density=4)

知道了不同drawable对应的dpi后,如何判断一款手机会用哪个drawable下的资源呢?先来看下DispalyMetrics这个类:

DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();

 //设备的绝对宽度,单位是px

public int widthPixels=dm.widthPixels;

//设备的绝对高度,单位是px

public int heightPixels=dm.heightPixels;

//水平方向的dpi

public float xdpi=dm.xdpi;

//竖直方向的dpi

public float ydpi=dm.ydpi;

//屏幕密度(和字体显示缩放因子scaledDensity一样)

public float density=dm.density;

//dpi,即单位尺寸的像素点

public int densityDpi=dm.densityDpi; 或者 densityDip=density*160;

判断一款手机会用哪个drawable下的资源,判断规则:优先找dpi最接近的,没有就选在drawable的。

即android系统适配drawable时,会首先在与设备对应的dpi目录下查找。如果没有找,则会遵循“先高再低”原则,然后按比例缩放图片。比如当前为xhdpi设备(项目中只有xxhdpi、xhdpi、xxhdpi、nodpi、mdpi、hdpi),则drawable的寻找顺序为:首先查找xhdpi目录,如果没找到就会查找xxhdpi,如果还没有找到就查找xxxhdpi,还没有找到就查找nodpi,如果还没有找到就查找hdpi,再找不到就查找mdpi,依次查找。如果在xxhdpi中找到目标图片,则压缩2/3来使用(因为系统认为它找到了一个比合适尺寸大的图片),如果在mdpi中找到图片,则放大2倍来使用(系统认为它找到了一个比适合尺寸小的图片,需要放大才能保证正常)。

比如:

b4ffb22a1651404da8a04c0aab8643a1.png

从打印出的信息可以看到densityDpi=480,所以优先选择drawable-xxhdpi文件下的资源。

由于scaledDensity = density * fontScale。其中fontScale代表用户设定的Android设备字体缩放比例,默认为1。也就是说,当用户没有改变Android设备的字体缩放比例时,sp、dp与px的换算是相同的。

再如:

f1fe29820e524843956748779816c524.png

从打印出的信息可以看到densityDpi=522,所以优先选择drawable-xxxhdpi文件下的资源,如果没有会用drawable-xxhdpi。

注意,2017年以后的android手机一般大小在5寸以上,分辨率至少720p*1080p,所以对应的dpi分别为:

720p*1280对应dpi 大约 300dpi

1080p*1920对应dpi 大约440 dpi

xhdpi对应320dpi,xxhdpi对应480dpi,所以手机适配一般只需要xhdpi和xxhdpi两套资源就可以。

对于平板,只能电视和车载系统的开发,一般xhdpi和xxhdpi用不到,ldpi、mdpi用的比较多

 

3.防止系统字体变化影响字体大小

当在设置—显示—字体大小里修改字体大小时,界面上字体会跟随该变化而变化,比如系统字体放大后,文字设置为sp的也跟随放大,(如果设置为dp则保存不变)。

为了解决这个问题,首先要明白字体适配问题的原理:

public void setTextSize(float size) {

   setTextSize(TypedValue.COMPLEX_UNIT_SP, size);

}

public void setTextSize(int unit, float size) {

    Context c = getContext();

    Resources r;

    if (c == null)

        r = Resources.getSystem();

    else

        r = c.getResources();

    setRawTextSize(TypedValue.applyDimension( unit, size, r.getDisplayMetrics()));

}

private void setRawTextSize(float size) {

    if (size != mTextPaint.getTextSize()) {

        mTextPaint.setTextSize(size);

        if (mLayout != null) {

            nullLayouts();

            requestLayout();

            invalidate();

        }

    }

}

可见,setTextSize(float size)最终调用的是setTextSize(int unit,float size)方法,只是设置了一个默认参数TypedValue.COMPLEX_UNIT_SP,也就是调用setTextSize(float size)方法会默认转换为sp单位。

下面看一下TypedValue.applyDimension(unit, size, r.getDisplayMetrics())方法:

public static float applyDimension(int unit, float value, DisplayMetrics metrics) {

    switch (unit) {

        case COMPLEX_UNIT_PX:

            return value;

        case COMPLEX_UNIT_DIP:

            return value * metrics.density;

        case COMPLEX_UNIT_SP:

            return value * metrics.scaledDensity;

        case COMPLEX_UNIT_PT:

            return value * metrics.xdpi * (1.0f/72);

        case COMPLEX_UNIT_IN:

            return value * metrics.xdpi;

        case COMPLEX_UNIT_MM:

            return value * metrics.xdpi * (1.0f/25.4f);

    }

    return 0;

}

这是一个现成的像素转换方法,根据传递过来的unit,来分别计算出不同单位对应的像素值是多少。

所以现在明白了,动态设置字体不适配设备的原因:sp单位会跟随系统字体大小变化而变化,每台设备的分辨率不同,这就导致了为什么有些设备会出现字体很大或很小的问题。

解决这个问题的办法就是动态设置TextSize适配:

TextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(context, sizeValue));

或者直接

TextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, sizeValue);

根据上面的源码,转换后的值会被当作px值,dp是与像素无关的单位, 使用 dp2px() 方法 将dp值转换为px,这样在每台设备上基本大小一致。

 

为了不让字体跟随设置里字体大小而变化,有两种方法:

方法一:直接设置字体为DP单位

可以在XML布局文件中直接设置字体单位为DP,或者在代码中动态设置:

TextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, sizeValue);

如果不想改变SP单位,可以使用方法二:

因为scaledDensity = density * fontScale。其中fontScale代表用户设定的Android设备字体缩放比例,默认为1.0。也就是说,当用户没有改变Android设备的字体缩放比例时,sp、dp的换算是相同的。所以,可以在Activity、Application、重写getResources()方法:

@Override

public Resources getResources() {

    Resources resources = super.getResources();

    if (resources != null && resources.getConfiguration().fontScale != 1.0f) {

        android.content.res.Configuration configuration = resources.getConfiguration();

        configuration.fontScale = 1.0f;

        resources.updateConfiguration( configuration, resources.getDisplayMetrics());

    }

    return resources;

}

自定义MyApplication继承自Application,记得在清单文件<application>标签内用属性 android:name="包名.路径.MyApplication" 引用。

 

4.防止显示大小变化影响显示

系统修改"显示大小"时原有UI样式保持不变:

public static void setDefaultDisplay(Context context) {

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {

        Configuration origConfig = context.getResources().getConfiguration();

        //获取手机出厂时默认的densityDpi

        origConfig.densityDpi = getDefaultDisplayDensity();

        context.getResources().updateConfigur ation(origConfig, context.getResources().getDisplayMetrics());

    }

}

public static int getDefaultDisplayDensity() {

    try {

        Class clazz = Class.forName( "android.view.WindowManagerGlobal");

        Method method = clazz.getMethod( "getWindowManagerService");

        method.setAccessible(true);

        Object iwm = method.invoke(clazz);

        Method getInitialDisplayDensity = iwm.getClass().getMethod("getInitialDisplayDensity", int.class);

        getInitialDisplayDensity.setAccessible(true);

        Object densityDpi = getInitialDisplayDensity.invoke(iwm, Display.DEFAULT_DISPLAY);

        return (int) densityDpi;

    } catch (Exception e) {

        e.printStackTrace();

        return -1;

    }

}

不想随显示大小而变化的控件或页面就可以调用setDefaultDisplay(this)来实现。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/270866
推荐阅读
相关标签
  

闽ICP备14008679号