如何在彼此之间转换DP、PX和SP,尤其是DP和SP?

105
我了解 DP, SPPX 之间的区别。但是在搜索这个主题后,我没有找到完全满意的答案。也许这篇文章已经有了重复内容,但我仍想知道从DPPXDPSP,从 SPPX,从 PXSP,从 SPDP,从 DPSP 的转换公式是什么?我了解了一些代码,但它们并不完美。

你在这个链接上找到答案了吗?https://dev59.com/F2sy5IYBdhLWcg3wzBJ4 - surhidamatya
1
http://javapapers.com/android/difference-between-dp-dip-sp-px-in-mm-pt-in-android/ - surhidamatya
5
不,我认为目前还没有一个令人满意的答案。它们都存在不完美之处。你说过“这个问题已经被回答了无数次”,那么请你给我展示一下。 - SilentKnight
7个回答

233

DP 转 PX:

public static int dpToPx(float dp, Context context) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}

SP 转 PX:

public static int spToPx(float sp, Context context) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics());
}

DP 转 SP:

public static int dpToSp(float dp, Context context) {
    return (int) (dpToPx(dp, context) / context.getResources().getDisplayMetrics().scaledDensity);
}

1
@DerGolem 我希望我能找到一个实例来说明它们何时可以相同,何时又不同,但我感觉这是一个灰色地带。许多人(包括我)知道dp和sp以及它们之间的假设差异,但一些实际方面仍然不清楚。 - AndroidEx
1
@Android777。嗯,它们“大多数情况下”是相同的。作为一个经验法则,视图应该始终使用“dp”,字体应该使用“sp”。这样你就永远不会失败。说实话,我从未进一步调查过,因为我遵循这个“黄金法则”。 - Phantômaxx
1
@DerGolem 我完全同意,但是我们遇到了一个关于 dp 和 sp 之间转换的问题,我们不确定这是否是一个需要考虑的有效问题... - AndroidEx
1
@AndroidEx 你说得对。SP可以根据用户对文本大小的偏好而不同于DP。例如,在自定义绘图中,如果您希望图形的大小以某种方式对应于在画布上绘制的文本,则需要将SP转换为PX或DP。 - Mitselplik
2
如果 sp one 是错误的,请使用以下正确的代码:public static int convertDpToSp(float dp, Context context) { return (int) (convertDpToPixels(dp, context) / context.getResources().getDisplayMetrics().scaledDensity); } - user49557
显示剩余10条评论

103

被接受的答案缺少了一些有用的转换。

SP转PX

float sp = 20;
float px = sp * getResources().getDisplayMetrics().scaledDensity;
或者
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());

像素转换为比例值

float px = 70;
float sp = px / getResources().getDisplayMetrics().scaledDensity;

DP 转换为 PX

float dp = 20;
float px = dp * getResources().getDisplayMetrics().density;
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());

像素(PX)与密度无关像素(DP)

float px = 70;
float dp = px / getResources().getDisplayMetrics().density;

注释

  • 我上面选择的浮点数(2070)是任意值。如果你喜欢,你可以输入不同的数字。
  • px 指的是像素。设备每英寸屏幕空间有多少像素称为密度。
  • dp 代表密度无关像素。也就是说,无论使用什么设备,实际大小应该相同。例如,如果我将一个视图设置为 100 dp 宽,它在新的高密度手机和旧的低密度手机上的宽度都将是相同的。(另一方面,如果我将宽度设置为 100 px,则在低密度手机上显示会很大,在高密度手机上则会很小。)密度以每英寸点数(DPI)为单位进行测量。公式为 px = dp * density。因此,只需乘以或除以密度即可在 pxdp 之间转换。
  • sp 意味着比例独立像素。它仅用于字体,而不是视图。它类似于 dp,但还考虑了用户的偏好。考虑到用户偏好的密度称为缩放密度。例如,将 TextView 字体大小设置为 30 sp,则文本通常在所有设备上看起来物理大小相同。但是,您的祖母可能已经将她的首选字体大小设置为最大,在她的手机设置中,因此 30 sp 文本在她的手机上看起来比在你的手机上更大。公式为 px = sp * scaledDensity
  • DP 和 SP 的含义
  • DP 转 SP 的转换通常没有用处

scaledDensity现已过时。Android 14现在进行非线性字体缩放,因此您应该使用TypedValue.applyDimension - Remc4

10

将尺寸转换为整数或像素需要使用 "getDimensionPixelSize(R.dimen.your_dp_value)" 函数,例如...

在 dimens.xml 文件中设置一个值

<dimen name="padding_10">10dp</dimen>

现在,你可以像下面这样使用像素或整数的值:
int sizeInPixel = context.getResources().getDimensionPixelSize(R.dimen.padding_10);

在我看来,最干净的解决方案是不提到将文件拆分为values-*,如果需要的话,可以获得不同密度的不同值... - Shockwaver

6

对于 kotlin,我创建了一个扩展函数:

fun Number.spToPx(context: Context) = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_SP, this.toFloat(), context.resources.displayMetrics).toInt()

您可以像这样使用它:16.spToPx(context)16.5.spToPx(context)
(我将这些函数放在一个KotlinExtensions.kt文件中)

2

您可以编写一种不需要contextresources的方法:

public static int dpToPx(int dp) {
    return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}

public static int spToPx(int sp) {
    return (int) (sp * Resources.getSystem().getDisplayMetrics().scaledDensity);
}

类比地,其他量也可以进行转换。


1
根据文档,Resources.getSystem()未针对当前屏幕进行配置,因此无法使用尺寸单位。 - timgo

2
根据TypedValue#applyDimension源代码,并利用Kotlin扩展功能:
val Float.toSp get() = this * Resources.getSystem().displayMetrics.scaledDensity

其他扩展来自 链接

val Float.toPx get() = this * Resources.getSystem().displayMetrics.density

val Float.toDp get() = this / Resources.getSystem().displayMetrics.density

0

对我来说,只需反转applyDimension()即可,因此这个解决方案适用于所有单位。

public static int unApplyDimension(int unit,float value){
    return (int)( b.getTextSize()/TypedValue.applyDimension(
        b.getTextSizeUnit(),1,
        getApplicationContext().getResources().getDisplayMetrics()));
}

我知道我的格式很糟糕。也许不应该把它变成一行代码。不过没关系。

已经测试并且可以工作,但是在稍微不同的情况下,我必须对按钮的文本大小进行动画处理,其中初始文本大小单位可以是任何东西。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接