【问题标题】:Formula px to dp, dp to px android公式 px 到 dp,dp 到 px android
【发布时间】:2012-01-08 17:01:53
【问题描述】:

我正在尝试将可变数量的像素计算为与密度无关的像素,反之亦然。

这个公式(px to dp): dp = (int)(px / (displayMetrics.densityDpi / 160)); 不适用于小型设备,因为它被零除。

这是我的 dp 到 px 公式:

px = (int)(dp * (displayMetrics.densityDpi / 160));

有人可以指点一下吗?

【问题讨论】:

  • 将 dp 单位转换为像素单位 developer.android.com/guide/practices/…
  • @Bram:我认为你的公式很好。您将如何除以零? displayMetrics.densityDpi 将是 120、160、240 或 320,而不是 0。
  • 我同意@ct_rob。 displayMetrics.densityDpi / 160 最小值将为 0.75。您一定是在不正确的位置转换为 int。

标签: android formula density-independent-pixel


【解决方案1】:

注意:上面广泛使用的解决方案是基于displayMetrics.density。但是,文档解释说这个值是一个四舍五入的值,与屏幕“桶”一起使用。例如。在我的 Nexus 10 上,它返回 2,其中实际值为 298dpi(真实)/160dpi(默认)= 1.8625。

根据您的要求,您可能需要精确的转换,可以这样实现:

[编辑] 这并不意味着与 Android 的内部 dp 单元混合,因为这当然仍然基于屏幕存储桶。在您想要一个在不同设备上呈现相同实际尺寸的单元时使用它。

将dp转换为像素:

public int dpToPx(int dp) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));     
}

将像素转换为dp:

public int pxToDp(int px) {
    DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
    return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}

请注意,有 xdpi 和 ydpi 属性,您可能想要区分,但我无法想象这些值相差很大的正常显示。

【讨论】:

  • 这不会在许多设备(包括您的 Nexus 10)上计算正确的 dp/px 值!正如您所说, displayMetrics.density 被四舍五入到最近的屏幕桶,但 dp 单位也是如此!尝试绘制一个 160dp 宽的对象,然后在其下方绘制另一个 dpToPx(160) 像素宽的对象,您将看到两个对象的大小不同。还有一些手机(例如 Galaxy Mini 和 Galaxy S3 Mini)报告的 xdpi/ydpi 值完全错误,因此在这些手机上,您的方法将返回完全错误的结果。
  • @nibarius 是的,您不能将 Android 的盒装 dp 计算与上述混合。上述解决方案是基于精确设备物理的独立密度独立值。需要例如您想在不同设备上显示完全相同的实际长度的线。当然,如果某些设备没有正确设置 xdpi/ydpi 输入,它就不会在那里工作。
  • 不应使用 xdpi 和 ydpi,因为它们在许多设备上不准确,有时甚至很多。只有 DisplayMetrics.densityDpi 是可靠的,这是不幸的,因为它的设计不精确。有关详细信息,请参阅 Google 论坛主题:groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
  • 我找到了这个响应,实际上使用了许多类似的解决方案,但我刚刚浏览了文档,发现给定维度的 getDimensionPixelOffSet(在 dip/dp 中声明)返回像素偏移量,就像手工代码一样。我测试并完美地工作。希望对您有所帮助!
  • 嗯,这并没有给出正确的值。尝试:资源 r = getResources(); float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, r.getDisplayMetrics());如此处所述:stackoverflow.com/questions/4605527/converting-pixels-to-dp
【解决方案2】:

我使用以下公式解决了我的问题。愿其他人从中受益。

dp 到 px:

displayMetrics = context.getResources().getDisplayMetrics();
return (int)((dp * displayMetrics.density) + 0.5);

px 到 dp:

displayMetrics = context.getResources().getDisplayMetrics();
return (int) ((px/displayMetrics.density)+0.5);

【讨论】:

  • @Vame 添加 0.5 用于向上舍入到最接近的整数值。添加 0.5 然后将计算结果转换为 int 导致它截断尾数并离开特征为适当舍入的整数值。
  • 这并不总是正确的。当我有两个布局,一个在另一个里面,然后我将每个视图的角(一个使用 dp,其他转换为 dp)圆角不匹配!
  • 我对这种技术没有任何问题。我将它用于不同的布局,它总是按预期工作。当然我几年前就用过这个,我不确定它是否仍然有效。也许您可以尝试 PanaVTEC 回答中的其他技术。也可能是圆角不仅仅是 dp / px 计算。
  • 这和Android开发者网站上采用的方案是一样的,所以我猜是对的:)。 http://developer.android.com/guide/practices/screens_support.html#dips-pels
  • +1 谢谢。像魅力一样工作!感谢您与我们分享此解决方案。
【解决方案3】:

高效的方式

DP 到像素:

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

像素到 DP:

private int pxToDp(int px)
{
    return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}

希望这会对你有所帮助。

【讨论】:

  • 更好,因为不需要使用 Context :) 谢谢。
  • 最佳答案!不幸的是,“标记为正确”的答案是错误的。特别是因为它使用displayMetrics.xdpi,这在任何设备上都是不同的。可悲的是,这个错误的答案也获得了最多的支持。
  • 不错。应标记为最佳答案。科特林:private fun dpToPx(dp: Int) = (dp * Resources.getSystem().displayMetrics.density).toInt()
【解决方案4】:

px 到 dp:

int valueInpx = ...;
int valueInDp= (int) TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, valueInpx , getResources()
                .getDisplayMetrics());

【讨论】:

  • 这是从 DP 到 PX,但有这个更正:typedValue.applyDimension( TypedValue.COMPLEX_UNIT_PX, valueInDp , getResources() .getDisplayMetrics()); 是从 PX 到 DP,这也是最好的答案
  • @PaNaVTEC,您引用的 dp 到 px 的解决方案不正确。 applyDimension 只产生像素。请参阅android.googlesource.com/platform/frameworks/base/+/refs/heads/…,其中没有为COMPLEX_UNIT_PX 执行转换。
  • 这个答案是完全错误的。它以像素为单位返回最终值,而不是dp。如果查看源代码,对于TypedValue.COMPLEX_UNIT_DIP 的情况,返回value * metrics.density,您实际上需要value * metrics.density。所以你得到的`valueInDp`不在dp中,因为valueInPxpx中。
  • ^typo,我的意思是 "...你真正需要的地方 value / metrics.density"
【解决方案5】:

只需调用getResources().getDimensionPixelSize(R.dimen.your_dimension) 即可将dp 单位转换为像素

【讨论】:

  • 根据文档,这与 getDimension() 相同,只是返回值转换为整数像素以用作大小。大小转换涉及对基值进行四舍五入,并确保非零基值的大小至少为一个像素。
【解决方案6】:

使用此功能

private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}

【讨论】:

    【解决方案7】:
    px = dp * (dpi / 160)
    
    dp = px * (160 / dpi)
    

    【讨论】:

    • 虽然,现在我想起来了...... Brams 原始公式中怎么会有除零? displayMetrics.densityDpi 将是 120、160、240 或 320,而不是 0。
    • (displayMetrics.densityDpi / 160) - 这部分在小型设备上可以得到 0,它计算例如 120/160,两个值都是 int,结果为 0。最终为 (int) (px/0)。跨度>
    • 啊,你说得对。所以他需要做的就是在他的公式中使用一个long:160.0
    • 这个答案与上面的答案相似,因为 DisplayMetrics.DENSITY_DEFAULT 的值为 160。但是您能告诉我们这里的 DPI 是什么,我们如何计算它。
    【解决方案8】:

    使用这些 Kotlin 扩展:

    /**
     * Converts Pixel to DP.
     */
    val Int.pxToDp: Int
        get() = (this / Resources.getSystem().displayMetrics.density).toInt()
    
    /**
     * Converts DP to Pixel.
     */
    val Int.dpToPx: Int
        get() = (this * Resources.getSystem().displayMetrics.density).toInt()
    

    【讨论】:

      【解决方案9】:

      在大多数情况下,转换函数被频繁调用。我们可以通过添加 memoization 来优化它。因此,它不会在每次调用函数时都计算。

      让我们声明一个存储计算值的 HashMap。

      private static Map<Float, Float> pxCache = new HashMap<>();
      

      计算像素值的函数:

      public static float calculateDpToPixel(float dp, Context context) {
      
              Resources resources = context.getResources();
              DisplayMetrics metrics = resources.getDisplayMetrics();
              float px = dp * (metrics.densityDpi / 160f);
              return px;
      
          }
      

      一个从HashMap返回值并维护先前值记录的记忆函数。

      Memoization 可以在 Java 中以不同的方式实现。 对于 Java 7

      public static float convertDpToPixel(float dp, final Context context) {
      
              Float f = pxCache.get(dp);
              if (f == null) {
                  synchronized (pxCache) {
                      f = calculateDpToPixel(dp, context);
                      pxCache.put(dp, f);
                  }
      
              }
      
              return f;
          }
      

      Java 8 支持 Lambda 函数

      public static float convertDpToPixel(float dp, final Context context) {
      
              pxCache.computeIfAbsent(dp, y ->calculateDpToPixel(dp,context));
      }
      

      谢谢。

      【讨论】:

      • 给定解决方案的不错补充。
      • 如果转换计算至少不如地图查找那么快,更不用说同步计算了,我会感到惊讶。您是否有任何测试结果证明这种优化是有益的?在工作内存有限的 Android 上,您不应该毫无理由地用内存来换取计算工作量。
      【解决方案10】:

      【讨论】:

      【解决方案11】:

      以下功能对我来说在各种设备上都很有效。

      取自https://gist.github.com/laaptu/7867851

      public static float convertPixelsToDp(float px){
          DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
          float dp = px / (metrics.densityDpi / 160f);
          return Math.round(dp);
      }
      
      public static float convertDpToPixel(float dp){
          DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
          float px = dp * (metrics.densityDpi / 160f);
          return Math.round(px);
      }
      

      【讨论】:

        【解决方案12】:

        您可以使用[DisplayMatrics][1] 并确定屏幕密度。像这样的:

        int pixelsValue = 5; // margin in pixels
        float d = context.getResources().getDisplayMetrics().density;
        int margin = (int)(pixelsValue * d);
        

        我记得最好使用地板进行偏移和四舍五入的宽度。

        【讨论】:

          【解决方案13】:

          如果您正在寻找一种用于在不同屏幕密度下相互转换 DP、SP、英寸、毫米、点或像素的在线计算器,this is the most complete tool I know of

          【讨论】:

            【解决方案14】:

            // 用于获取 Decimal/Float

            public static float convertPixelsToDp(float px, Context context) {
            
                Resources resources = context.getResources();
                DisplayMetrics metrics = resources.getDisplayMetrics();
                float dp = px / (metrics.densityDpi / 160f);
                return Math.round(dp);
            }
            
            
            
            public static float convertDpToPixel(float dp, Context context) {
                DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
                float px = dp * (metrics.densityDpi / 160f);
                return Math.round(px);
            }
            
            
            // for  getting in terms of Integer
            
            private int convertPxToDp(int px, Context context) {
                Resources resources = context.getResources();
                return Math.round(px / (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
            }
            
            
            private int convertDpToPx(int dp, Context context) {
                Resources resources = context.getResources();
            
                return Math.round(dp * (resources.getDisplayMetrics().xdpi / DisplayMetrics.DENSITY_DEFAULT));
            
            }
            

            ________________________________________________________________________________

            public static float convertPixelsToDp(float px){
                DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
                float dp = px / (metrics.densityDpi / 160f);
                return Math.round(dp);
            }
            
            public static float convertDpToPixel(float dp){
                DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
                float px = dp * (metrics.densityDpi / 160f);
                return Math.round(px);
            }
            
            
            private int convertDpToPx(int dp){
                return Math.round(dp*(getResources().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));
            
            }
            
            private int convertPxToDp(int px){
                return Math.round(px/(Resources.getSystem().getDisplayMetrics().xdpi/DisplayMetrics.DENSITY_DEFAULT));
            }
            

            【讨论】:

              【解决方案15】:

              优雅的 kotlin 解决方案 :)

              val Int.dp get() = this / (Resources.getSystem().displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
              val Float.dp get() = this / (Resources.getSystem().displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
              
              val Int.px get() = this * Resources.getSystem().displayMetrics.density
              val Float.px get() = this * Resources.getSystem().displayMetrics.density
              

              用法:

              val dpValue = 2.dp
              val pxFromDpValue = 2.px
              

              重要:

              我不确定Resources.getSystem() 是否可以在方向更改时正常工作。

              如果想在例如片段或活动中工作,只需将其添加到基本片段或基本活动中并像这样使用它:

              abstract class BaseFragment : Fragment() {
              
                  val Int.dp get() = this / (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
                  val Float.dp get() = this / (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
              
                  val Int.px get() = this * resources.displayMetrics.density
                  val Float.px get() = this * resources.displayMetrics.density
              
                  .......
              }
              

              【讨论】:

                【解决方案16】:

                随意使用我写的这个方法:

                int dpToPx(int dp)
                {
                    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
                }
                

                【讨论】:

                  【解决方案17】:

                  上面接受的答案并不完全准确。根据查看Android源码得到的信息:

                  Resources.getDimension()getDimensionPixelOffset()/getDimensionPixelSize() 不同之处仅在于前者返回float,而后两者返回相同的值,四舍五入为int。对于所有这些,返回值都是原始像素。

                  这三个函数都是通过调用Resources.getValue()实现的,分别调用TypedValue.complexToDimension()TypedValue.complexToDimensionPixelOffset()TypedValue.complexToDimensionPixelSize()转换得到TypedValue

                  因此,如果要获取“原始”值以及 XML 源中指定的单位,请调用 Resources.getValue() 并使用 TypedValue 类的方法。

                  【讨论】:

                    【解决方案18】:

                    在其他答案的帮助下,我编写了这个函数。

                    public static int convertToPixels(Context context, int nDP)
                    {
                            final float conversionScale = context.getResources().getDisplayMetrics().density; 
                            return (int) ((nDP * conversionScale) + 0.5f) ;
                    }
                    

                    【讨论】:

                      【解决方案19】:

                      这是使用 kotlin 扩展的另一种方法:

                      val Int.dpToPx: Int
                          get() = Math.round(this * Resources.getSystem().displayMetrics.density)
                      
                      val Int.pxToDp: Int
                          get() = Math.round(this / Resources.getSystem().displayMetrics.density)
                      

                      然后在任何地方都可以这样使用

                      12.dpToPx
                      
                      244.pxToDp
                      

                      【讨论】:

                        【解决方案20】:
                        DisplayMetrics displayMetrics = contaxt.getResources()
                                    .getDisplayMetrics();
                        
                            int densityDpi = (int) (displayMetrics.density * 160f);
                            int ratio = (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
                            int px;
                            if (ratio == 0) {
                                px = dp;
                            } else {
                                px = Math.round(dp * ratio);
                        
                            }
                        

                        【讨论】:

                          【解决方案21】:

                          上面 ct_robs 答案的变化,如果您使用整数,这不仅可以避免除以 0,还可以在小型设备上产生可用的结果:

                          在涉及最高精度除法的整数计算中,在除法之前先乘以减少截断效应。

                          px = dp * dpi / 160
                          dp = px * 160 / dpi
                          
                          5 * 120 = 600 / 160 = 3
                          

                          而不是

                          5 * (120 / 160 = 0) = 0
                          

                          如果您想要四舍五入的结果,请执行此操作

                          px = (10 * dp * dpi / 160 + 5) / 10
                          dp = (10 * px * 160 / dpi + 5) / 10
                          
                          10 * 5 * 120 = 6000 / 160 = 37 + 5 = 42 / 10 = 4
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 2014-08-21
                            • 1970-01-01
                            • 2015-06-22
                            • 2013-05-18
                            • 2011-01-02
                            • 1970-01-01
                            • 2017-04-10
                            • 2020-01-09
                            相关资源
                            最近更新 更多