【问题标题】:Converting pixels to dp将像素转换为 dp
【发布时间】:2011-06-04 01:56:36
【问题描述】:

我已经为分辨率为480x800 的 Pantech 设备创建了高度和宽度(以像素为单位)的应用程序。

我需要为 G1 设备转换高度和宽度。
我认为将其转换为 dp 将解决问题并为两种设备提供相同的解决方案。

有什么简单的方法可以将像素转换为 dp?
有什么建议吗?

【问题讨论】:

标签: android pixel resolution dpi


【解决方案1】:

开发者文档提供了如何convert dp units to pixel units 的答案:

在某些情况下,您需要以 dp 表示尺寸,然后 将它们转换为像素。 dp 单位到屏幕像素的转换是 简单:

px = dp * (dpi / 160)

提供了两个代码示例,一个 Java 示例和一个 Kotlin 示例。这是 Java 示例:

// The gesture threshold expressed in dp
private static final float GESTURE_THRESHOLD_DP = 16.0f;

// Get the screen's density scale
final float scale = getResources().getDisplayMetrics().density;
// Convert the dps to pixels, based on density scale
mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);

// Use mGestureThreshold as a distance in pixels...

Kotlin 示例:

// The gesture threshold expressed in dp
private const val GESTURE_THRESHOLD_DP = 16.0f
...
private var mGestureThreshold: Int = 0
...
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Get the screen's density scale
    val scale: Float = resources.displayMetrics.density
    // Convert the dps to pixels, based on density scale
    mGestureThreshold = (GESTURE_THRESHOLD_DP * scale + 0.5f).toInt()

    // Use mGestureThreshold as a distance in pixels...
}

我从 Java 示例创建的 Java 方法运行良好:

/**
 * Convert dp units to pixel units
 * https://developer.android.com/training/multiscreen/screendensities#dips-pels
 *
 * @param dip input size in density independent pixels
 *            https://developer.android.com/training/multiscreen/screendensities#TaskUseDP
 *
 * @return pixels
 */
private int dpToPixels(final float dip) {
    // Get the screen's density scale
    final float scale = this.getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    final int pix = Math.round(dip * scale + 0.5f);
    
    if (BuildConfig.DEBUG) {
        Log.i(DrawingView.TAG, MessageFormat.format(
                "Converted: {0} dip to {1} pixels",
                dip, pix));
    }
    
    return pix;
}

此问题的其他几个答案提供了与此答案类似的代码,但其他答案中没有足够的支持文档让我知道其他答案之一是否正确。接受的答案也与此答案不同。我认为developer documentation 使 Java 解决方案变得清晰。

Compose 中也有一个解决方案。 Compose 中的 Density 为与设备无关的像素 (dp) 和像素之间的转换提供了一行 solution

val sizeInPx = with(LocalDensity.current) { 16.dp.toPx() }

【讨论】:

    【解决方案2】:

    Java 代码:

    // Converts 14 dip into its equivalent px
    float dip = 14f;
    Resources r = getResources();
    float px = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        dip,
        r.getDisplayMetrics()
    );
    

    Kotlin 代码:

     val dip = 14f
     val r: Resources = resources
     val px = TypedValue.applyDimension(
         TypedValue.COMPLEX_UNIT_DIP,
         dip,
         r.displayMetrics
     )
    

    Kotlin 扩展:

    val Number.toPx get() = TypedValue.applyDimension(
      TypedValue.COMPLEX_UNIT_DIP,
      this.toFloat(),
      Resources.getSystem().displayMetrics)
    

    【讨论】:

    • 注意:以上是将 DIP 转换为像素。最初的问题是如何将像素转换为 Dips!
    • 这是对 OP 的真实答案:stackoverflow.com/questions/6656540/…
    • 有趣的是,当它没有真正回答问题时,答案如何更有帮助 -_- 我以为我想要问题的内容,然后我意识到我没有!这么棒的答案。我确实有一个问题。如何获得applyDimension 的最后一个参数?我可以只做getResource().getDisplayMetrics(),还是有别的事情?
    • 注意:相对昂贵的操作。尝试缓存这些值以便更快地访问
    • 如果无法访问Context 对象,请使用Resources.getSystem().getDisplayMetrics()
    【解决方案3】:

    使用 kotlin-extension 会更好

    fun Int.toPx(context: Context): Int = (this * context.resources.displayMetrics.density).toInt()
    
    fun Int.toDp(context: Context): Int = (this / context.resources.displayMetrics.density).toInt()
    

    更新:

    因为displayMetricsglobal shared Resources的一部分,我们可以使用Resources.getSystem()

    val Float.toPx get() = this * Resources.getSystem().displayMetrics.density
    val Float.toDp get() = this / Resources.getSystem().displayMetrics.density
    
    val Int.toPx get() = (this * Resources.getSystem().displayMetrics.density).toInt()
    val Int.toDp get() = (this / Resources.getSystem().displayMetrics.density).toInt()
    

    【讨论】:

    • 为什么要添加它作为Int的扩展,责任与Int类型无关。
    • @htafoya 感谢您的反馈。我考虑你在上一版中提到的内容。
    【解决方案4】:
    ((MyviewHolder) holder).videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(final MediaPlayer mediaPlayer) {
            mediaPlayer.setLooping(true);
            ((MyviewHolder) holder).spinnerView.setVisibility(View.GONE);
            mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
               @Override
               public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
                   /*
                   * add media controller
                   */
                   MediaController controller = new MediaController(mContext);
                   float density = mContext.getResources().getDisplayMetrics().density;
                   float px = 55 * density;
                   // float dp = somePxValue / density;
                   controller.setPadding(0, 0, 0, (int) (px));
                   ((MyviewHolder) holder).videoView.setMediaController(controller);
                }
            });
        }
    });
    

    【讨论】:

      【解决方案5】:

      将 dp 转换为像素

      public static int dp2px(Resources resource, int dp) {
          return (int) TypedValue.applyDimension(
              TypedValue.COMPLEX_UNIT_DIP,
              dp,resource.getDisplayMetrics()
          );
      }
      

      将像素转换为dp。

      public static float px2dp(Resources resource, float px)  {
          return (float)TypedValue.applyDimension(
              TypedValue.COMPLEX_UNIT_PX,
              px,
              resource.getDisplayMetrics()
          );
      }
      

      其中资源是 context.getResources()。

      【讨论】:

      • 答案是错误的,因为您没有将像素转换为 dp - 您正在将像素转换为像素!
      • px2dp 错误 - 将返回相同的像素值。
      【解决方案6】:
      private fun toDP(context: Context,value: Int): Int {
          return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
              value.toFloat(),context.resources.displayMetrics).toInt()
      }
      

      【讨论】:

        【解决方案7】:

        最好的答案来自 Android 框架本身:只需使用这种相等性...

        public static int dpToPixels(final DisplayMetrics display_metrics, final float dps) {
            final float scale = display_metrics.density;
            return (int) (dps * scale + 0.5f);
        }
        

        (将 dp 转换为 px)

        【讨论】:

          【解决方案8】:

          你可以使用这个.. 没有上下文

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

          正如@Stan 提到的.. 如果系统改变密度,使用这种方法可能会导致问题。所以要注意这一点!

          就我个人而言,我正在使用 Context 来做到这一点。这只是我想与您分享的另一种方法

          【讨论】:

          • 您可能不想使用它。 getSystem() 的文档 - “返回一个全局共享资源对象,该对象仅提供对系统资源(无应用程序资源)的访问,并且未针对当前屏幕进行配置(不能使用维度单位,不根据方向更改等) 。”
          • 接着@Stan 的说法:这很危险,你不应该使用它,尤其是现在设备外形变得如此复杂。
          • 这不考虑可折叠设备和 ChromeOS 设备
          【解决方案9】:

          科特林

          fun convertDpToPixel(dp: Float, context: Context): Float {
              return dp * (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
          }
          
          fun convertPixelsToDp(px: Float, context: Context): Float {
              return px / (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
          }
          

          Java

          public static float convertDpToPixel(float dp, Context context) {
              return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
          }
          
          public static float convertPixelsToDp(float px, Context context) {
              return px / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
          }
          

          【讨论】:

            【解决方案10】:
            /**
             * This method converts dp unit to equivalent pixels, depending on device density. 
             * 
             * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
             * @param context Context to get resources and device specific display metrics
             * @return A float value to represent px equivalent to dp depending on device density
             */
            public static float convertDpToPixel(float dp, Context context){
                return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
            }
            
            /**
             * This method converts device specific pixels to density independent pixels.
             * 
             * @param px A value in px (pixels) unit. Which we need to convert into db
             * @param context Context to get resources and device specific display metrics
             * @return A float value to represent dp equivalent to px value
             */
            public static float convertPixelsToDp(float px, Context context){
                return px / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
            }
            

            【讨论】:

            • 可能值得返回一个 int Math.round(px),因为大多数方法都需要一个整数值
            • @MuhammadBabar 这是因为 160 dpi (mdpi) 是计算其他密度的基准密度。例如,hdpi 被认为是 mdpi 密度的 1.5 倍,这实际上只是 240 dpi 的另一种说法。有关所有密度,请参见下面 Zsolt Safrany 的答案。
            • @TomTasche:来自Resource.getSystem() 的文档(重点是我的):“返回一个全局共享资源对象,该对象仅提供对系统资源(无应用程序资源)的访问,并且未为当前配置屏幕(不能使用尺寸单位,不会因方向等而改变)。”
            • 开发者可以选择上限/下限。最好把控制权交给开发者。
            • 我会推荐 DisplayMetrics.DENSITY_DEFAULT 而不是 160f developer.android.com/reference/android/util/…
            【解决方案11】:

            科特林

               fun spToPx(ctx: Context, sp: Float): Float {
                return sp * ctx.resources.displayMetrics.scaledDensity
            }
            
            fun pxToDp(context: Context, px: Float): Float {
                return px / context.resources.displayMetrics.density
            }
            
            fun dpToPx(context: Context, dp: Float): Float {
                return dp * context.resources.displayMetrics.density
            }
            

            java

               public static float spToPx(Context ctx,float sp){
                return sp * ctx.getResources().getDisplayMetrics().scaledDensity;
            }
            
            public static float pxToDp(final Context context, final float px) {
                return px / context.getResources().getDisplayMetrics().density;
            }
            
            public static float dpToPx(final Context context, final float dp) {
                return dp * context.getResources().getDisplayMetrics().density;
            }
            

            【讨论】:

              【解决方案12】:

              对于 Xamarin.Android

              float DpToPixel(float dp)
              {
                  var resources = Context.Resources;
                  var metrics = resources.DisplayMetrics;
                  return dp * ((float)metrics.DensityDpi / (int)DisplayMetricsDensity.Default);
              }
              

              在制作自定义渲染器时,必须将其设为非静态

              【讨论】:

                【解决方案13】:

                这应该为您提供 dp 到像素的转换:

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

                这应该为您提供到 dp 的转换像素:

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

                【讨论】:

                  【解决方案14】:

                  如果您想要 整数 值,则使用 Math.round() 会将浮点数四舍五入为最接近的整数。

                  public static int pxFromDp(final float dp) {
                          return Math.round(dp * Resources.getSystem().getDisplayMetrics().density);
                      }
                  

                  【讨论】:

                    【解决方案15】:

                    要转换dp to px,此代码可能会有所帮助:

                    public static int dpToPx(Context context, int dp) {
                           final float scale = context.getResources().getDisplayMetrics().density;
                           return (int) (dp * scale + 0.5f);
                        }
                    

                    【讨论】:

                      【解决方案16】:

                      如果你可以使用维度 XML,那就很简单了!

                      在你的res/values/dimens.xml:

                      <?xml version="1.0" encoding="utf-8"?>
                      <resources>
                          <dimen name="thumbnail_height">120dp</dimen>
                          ...
                          ...
                      </resources>
                      

                      然后在你的 Java 中:

                      getResources().getDimensionPixelSize(R.dimen.thumbnail_height);
                      

                      【讨论】:

                      • 由于 px to dp 取决于屏幕密度,我不知道 OP 一开始是如何得到 120 的,除非他或她在所有不同的屏幕尺寸上测试了 px to dp 方法。跨度>
                      【解决方案17】:

                      对于使用 Kotlin 的任何人:

                      val Int.toPx: Int
                          get() = (this * Resources.getSystem().displayMetrics.density).toInt()
                      
                      val Int.toDp: Int
                          get() = (this / Resources.getSystem().displayMetrics.density).toInt()
                      

                      用法:

                      64.toPx
                      32.toDp
                      

                      【讨论】:

                      • 来自 Resource.getSystem() 的文档:“返回一个全局共享资源对象,该对象仅提供对系统资源(无应用程序资源)的访问,并且未针对当前屏幕进行配置(不能使用尺寸单位,不会因方向等而改变)。”
                      • @HendraWD 文档可能令人困惑,它提到的维度单位是您的应用程序级别的维度资源。 displayMetrics 不是应用程序级资源。它是一个系统资源,它返回正确的值。此代码在我所有的 Prod 应用程序上运行良好。从来没有问题。
                      • 我喜欢这个解决方案,但我最初将逻辑理解为倒退。我想输入 dp 值并说myVal.dp。如果您认为我想要做的事情读​​起来更好,那么您将需要交换除法和乘法逻辑。另外我认为在这里使用roundToInt()而不是toInt()会更好。
                      • @AdamJohns 因为所有与视图相关的函数都使用像素作为参数,所以我认为 64.toPx 会更有意义。我将其读取为 64 以转换为像素。但您可以随意更改顺序和名称。最后,重要的部分是是否将值乘以密度。
                      【解决方案18】:

                      使用 kotlin 的扩展函数更优雅的方法

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

                      用法:

                      println("16 dp in pixel: ${16.dpToPx}")
                      println("16 px in dp: ${16.pxToDp}")
                      

                      【讨论】:

                      • 喜欢在这里使用扩展。我将函数读作“转换为function name”,我意识到在这种情况下是倒退的。为了阐明每个函数的意图,函数的名称可以更新为分别读取 dpToPx 和 pxToDp。
                      • 来自 Resource.getSystem() 的文档:“返回一个全局共享资源对象,该对象仅提供对系统资源(无应用程序资源)的访问,并且未针对当前屏幕进行配置(不能使用尺寸单位,不会因方向等而改变)。”
                      【解决方案19】:

                      DP to Pixel

                      dimens.xml中创建一个值

                      <dimen name="textSize">20dp</dimen>
                      

                      pixel 中获取该值:

                      int sizeInPixel = context.getResources().getDimensionPixelSize(R.dimen.textSize);
                      

                      【讨论】:

                        【解决方案20】:

                        PXDP 不同但相似。

                        DP 是仅考虑屏幕物理尺寸时的分辨率。当您使用DP 时,它会将您的布局缩放到具有不同pixel 密度的其他类似大小的屏幕。

                        虽然有时您实际上需要pixels,但当您处理代码中的维度时,您总是在处理真正的pixels,除非您转换它们。

                        所以在安卓设备上,正常大小的hdpi 屏幕,800x480 is 533x320 中的DP(我相信)。要将DP 转换为pixels /1.5,再转换回*1.5。这仅适用于一种屏幕尺寸和dpi,它会根据设计而改变。我们的艺术家给了我pixels,然后我用上面的1.5 等式转换为DP

                        【讨论】:

                          【解决方案21】:
                          float density = context.getResources().getDisplayMetrics().density;
                          float px = someDpValue * density;
                          float dp = somePxValue / density;
                          

                          density 等于

                          • .75 on ldpi (120 dpi)
                          • 1.0 on mdpi160 dpi;基线)
                          • 1.5 on hdpi (240 dpi)
                          • 2.0 on xhdpi (320 dpi)
                          • 3.0 on xxhdpi (480 dpi)
                          • 4.0 on xxxhdpi (640 dpi)

                          使用此online converter 来调整 dpi 值。

                          编辑: dpi bucket 和 density 之间似乎没有 1:1 的关系。看起来Nexus 5Xxxhdpidensity 值为2.625(而不是3)。在Device Metrics 中亲自查看。

                          【讨论】:

                          • “看起来 Nexus 5X 是 xxhdpi,其密度值为 2.6(而不是 3)”- 从技术上讲,Nexus 5X 是 420dpi,而 Nexus 6/6P 是 560dpi,两者都没有直接落入其中标准存储桶,就像带有 tvdpi (213dpi) 的 Nexus 7。因此,将这些列为 xxdpi 和 xxxhdpi 的网站是一场闹剧。您的图表是正确的,这些设备将根据它们的“特殊”dpi 存储桶正确缩放。
                          【解决方案22】:

                          如果您开发性能关键应用程序,请考虑以下优化类:

                          public final class DimensionUtils {
                          
                              private static boolean isInitialised = false;
                              private static float pixelsPerOneDp;
                          
                              // Suppress default constructor for noninstantiability.
                              private DimensionUtils() {
                                  throw new AssertionError();
                              }
                          
                              private static void initialise(View view) {
                                  pixelsPerOneDp = view.getResources().getDisplayMetrics().densityDpi / 160f;
                                  isInitialised = true;
                              }
                          
                              public static float pxToDp(View view, float px) {
                                  if (!isInitialised) {
                                      initialise(view);
                                  }
                          
                                  return px / pixelsPerOneDp;
                              }
                          
                              public static float dpToPx(View view, float dp) {
                                  if (!isInitialised) {
                                      initialise(view);
                                  }
                          
                                  return dp * pixelsPerOneDp;
                              }
                          }
                          

                          【讨论】:

                          • 只有在您非常频繁地进行转换时才有意义。就我而言,我这样做了。
                          • 什么是 160f?为什么用它,为什么是160?
                          • 160 => 160dpi,这是因为公式转换度量
                          【解决方案23】:

                          这对我有用(C#):

                          int pixels = (int)((dp) * Resources.System.DisplayMetrics.Density + 0.5f);
                          

                          【讨论】:

                          • 不是这个问题的答案,但它适用于 C#。谢谢
                          【解决方案24】:

                          上面有很多很棒的解决方案。不过,我找到的最佳解决方案是 google 的设计:

                          https://design.google.com/devices/

                          【讨论】:

                            【解决方案25】:

                            这对我来说是这样的:

                            DisplayMetrics displaymetrics = new DisplayMetrics();
                            getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
                            int  h = displaymetrics.heightPixels;
                            float  d = displaymetrics.density;
                            int heightInPixels=(int) (h/d);
                            

                            你可以对宽度做同样的事情。

                            【讨论】:

                              【解决方案26】:
                              float scaleValue = getContext().getResources().getDisplayMetrics().density;
                              int pixels = (int) (dps * scaleValue + 0.5f);
                              

                              【讨论】:

                              • 这与该问题的许多其他答案所涵盖的内容不一样吗?
                              【解决方案27】:

                              最好放在 Util.java 类中

                              public static float dpFromPx(final Context context, final float px) {
                                  return px / context.getResources().getDisplayMetrics().density;
                              }
                              
                              public static float pxFromDp(final Context context, final float dp) {
                                  return dp * context.getResources().getDisplayMetrics().density;
                              }
                              

                              【讨论】:

                              • 这不适用于所有设备!这个答案的结果与使用 TypedValue.applyDimension 的结果在 OnePlus 3T 上不同(可能是因为 OnePlus 在操作系统中内置了自定义缩放)。使用 TypedValue.applyDimension 会导致跨设备的行为一致。
                              【解决方案28】:

                              如果你在 values/dimen 中有维度,最好的方法可能是直接从 getDimension() 方法获取维度,它会返回已经转换为像素值的维度。

                              context.getResources().getDimension(R.dimen.my_dimension)
                              

                              只是为了更好地解释这一点,

                              getDimension(int resourceId) 
                              

                              将已转换为像素的尺寸作为 FLOAT 返回。

                              getDimensionPixelSize(int resourceId)
                              

                              将返回相同但被截断为 int,所以 AS AN 整数。

                              Android reference

                              【讨论】:

                                【解决方案29】:

                                因此,您可以使用以下公式从 dp 中指定的维度计算正确的像素数量

                                public int convertToPx(int dp) {
                                    // Get the screen's density scale
                                    final float scale = getResources().getDisplayMetrics().density;
                                    // Convert the dps to pixels, based on density scale
                                    return (int) (dp * scale + 0.5f);
                                }
                                

                                【讨论】:

                                【解决方案30】:

                                根据 Android 开发指南:

                                px = dp * (dpi / 160)
                                

                                但当您收到以像素为单位的设计时,您通常会希望以相反的方式执行此操作。所以:

                                dp = px / (dpi / 160)
                                

                                如果您使用的是 240dpi 设备,则该比率为 1.5(如前所述),因此这意味着 60px 图标在应用程序中等于 40dp。

                                【讨论】:

                                猜你喜欢
                                • 2019-02-18
                                • 1970-01-01
                                • 2021-05-01
                                • 2015-12-22
                                • 1970-01-01
                                • 2017-07-19
                                • 1970-01-01
                                • 2012-11-16
                                相关资源
                                最近更新 更多