Android 关于 px、dp、sp 的那些事

来源:互联网 发布:手机游戏下载java 编辑:程序博客网 时间:2024/05/01 14:12

Android 的碎片化导致市场上有几百种不同尺寸和分辨率的机型,所以有 dp 和 sp 的出现,可以尽可能地保证在大部分机型的显示效果是一致的。

概念

px,pixel,像素,屏幕上的一个物理像素点
dp,device independent pixels,又称 dip,设备独立像素,和像素密度(每英寸的像素点)有关
sp,scaled pixels,缩放像素,用于文字展示

sp 和 dp 类似,但多了一个 缩放比例,这是因为系统允许用户自己定义文字的大小(小,正常,大,超大)。比如,用户设置了极小字体,那么如果使用 sp 作为单位的话,应用的字体会随着用户的设置统一缩小。

如果不希望程序受到用户调整字体的影响,可直接用 dp 来设置文字的大小。

dp 是如何计算出来的?

需要理解像素密度 densityDpi。比如 240*320 分辨率的手机,物理尺寸为 1.5英寸 * 2英寸,则横向每英寸的像素点 = 240 / 1.5 = 160dpi,纵向每英寸的像素点 = 320 / 2 = 160dpi,一般机型的这两个值都相等,所以这个手机的像素密度 densityDpi = 160dpi。

然后,在 160dpi 的机型中,1px = 1dp,所以其他 dpi 的机子可以通过下面的换算公式

px = dp * densityDpi / 160 = dp * densitydp = px / density

density = densityDpi / 160 的可以作为一个系数,在 160dpi,density = 1.0;在 240 dpi,density = 1.5;在 320 dpi,density = 2.0

对应到开发 res 资源有

  • ldpi 120dpi,0.75
  • mdpi 160dpi,1.0
  • hdpi 240dpi,1.5
  • xhdpi 320dpi,2.0

所以 1dp,到这几个目录中,换算得到的像素大小为

  • ldpi 1 * 0.75 = 0.75px
  • mdpi 1 * 1.0 = 1px
  • hdpi 1 * 1.5 = 1.5px
  • xhdpi 1 * 2.0 = 2.0px

使用

Android 有个 DisplayMetrics 类来计算这些值

我一般使用 Application 的 Context 来计算这些值,包括一些需要 Context,但仅仅去读写一些资源而于具体什么类型的 Context 无关的操作。这样的好处可以避免某些引用到 Context 的场景出现内存泄漏

public class BaseApplication extends Application {    private static Application mBaseApplication = null;    public BaseApplication() {    }    @Override    public void onCreate() {        super.onCreate();        mBaseApplication = this;    }    public static Context getContext() {        return mBaseApplication;    }}DisplayMetrics metrics = BaseApplication.getContext().getResources().getDisplayMetrics();

具体的换算有下面的工具方法可以实现

/** * dp转px * * @param dpValue dp值 * @return px值 */public static int dp2px(float dpValue) {    final float scale = BaseApplication.getContext().getResources().getDisplayMetrics().density;    return (int) (dpValue * scale + 0.5f);}/** * px转dp * * @param pxValue px值 * @return dp值 */public static int px2dp(float pxValue) {    final float scale = BaseApplication.getContext().getResources().getDisplayMetrics().density;    return (int) (pxValue / scale + 0.5f);}/** * sp转px * * @param spValue sp值 * @return px值 */public static int sp2px(float spValue) {    final float fontScale = BaseApplication.getContext().getResources().getDisplayMetrics().scaledDensity;    return (int) (spValue * fontScale + 0.5f);}/** * px转sp * * @param pxValue px值 * @return sp值 */public static int px2sp(float pxValue) {    final float fontScale = BaseApplication.getContext().getResources().getDisplayMetrics().scaledDensity;    return (int) (pxValue / fontScale + 0.5f);}/** * 像素的各种单位换算 * <p>该方法存在于TypedValue</p> * * @param unit    单位 * @param value   像素值 * @return 转换结果 */public static float applyDimension(int unit, float value) {    DisplayMetrics metrics = BaseApplication.getContext().getResources().getDisplayMetrics();    switch (unit) {        case TypedValue.COMPLEX_UNIT_PX:            return value;        case TypedValue.COMPLEX_UNIT_DIP:            return value * metrics.density;        case TypedValue.COMPLEX_UNIT_SP:            return value * metrics.scaledDensity;        case TypedValue.COMPLEX_UNIT_PT:            return value * metrics.xdpi * (1.0f / 72);        case TypedValue.COMPLEX_UNIT_IN:            return value * metrics.xdpi;        case TypedValue.COMPLEX_UNIT_MM:            return value * metrics.xdpi * (1.0f / 25.4f);    }    return 0;}

特殊情况

dp 和 sp 的使用可以满足大部分场景的机型适配,但还是会有极端的情况发生,这时候就需要在代码上采用动态适配的方式来解决适配问题

比如某个控件的大小依赖于其他控件的显示,要保证在极端情况下,其他控件完整显示,则可以提前 measure,然后动态改大小,比如

int measureSpec = MeasureSpec.makeMeasureSpec(-1, MeasureSpec.UNSPECIFIED);view1.measure(measureSpec, measureSpec);view2.measure(measureSpec, measureSpec);textView.setMaxWidth(mTotalWidth - view1.getMeasureWidth() - view2.getMeasureWidth());

平时的开发还会遇到各种各样的机型适配问题,单是 dp 还不够。所以对于 View 的 measure,layout,draw 等流程都需要有一定的了解

0 0
原创粉丝点击