ChatGPT解决这个技术问题 Extra ChatGPT

Converting pixels to dp

I have created my application with the height and width given in pixels for a Pantech device whose resolution is 480x800.

I need to convert height and width for a G1 device. I thought converting it into dp will solve the problem and provide the same solution for both devices.

Is there any easy way to convert pixels to dp? Any suggestions?

If you're looking to do a one-off conversion (for instance for exporting sprites from Photoshop), here's a nifty converter.

H
Hani

Java code:

// 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 code:

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

Kotlin extension:

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

Note: The above is converting DIPs to Pixels. The original question asked how to convert pixels to Dips!
Here's a real answer to the OP: stackoverflow.com/questions/6656540/…
Its funny how the answer is more helpful when it doesn't really answer the question -_- I thought I wanted what the question asked then I realized I didn't! So great answer. I do have a question. How can I obtain the last paramter for applyDimension? Can I just do getResource().getDisplayMetrics(), or is there something else?
NOTE: relatively expensive operation. Try to cache the values for quicker acces
If have no access to Context object use Resources.getSystem().getDisplayMetrics()
S
Salam El-Banna
/**
 * 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);
}

Might be worth returning an int Math.round(px) as most methods expect an integer value
@MuhammadBabar This is because 160 dpi (mdpi) is the baseline desity from which other densities are calculated. hdpi for instance is considered to be 1.5x the density of mdpi which is really just another way of saying 240 dpi. See Zsolt Safrany's answer below for all densities.
@TomTasche: From the docs for Resource.getSystem() (emphasis mine): "Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc)."
Developer have a choice to ceil/floor the value. It is better to give control to developer.
I would recommand DisplayMetrics.DENSITY_DEFAULT instead of 160f developer.android.com/reference/android/util/…
J
Jared Burrows

Preferably put in a Util.java class

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;
}

This does not work on all devices! The result of this answer vs that of using TypedValue.applyDimension is not the same on a OnePlus 3T (probably because OnePlus have custom scaling built into the OS). Using TypedValue.applyDimension causes consistent behavior across devices.
Z
Zsolt Safrany
float density = context.getResources().getDisplayMetrics().density;
float px = someDpValue * density;
float dp = somePxValue / density;

density equals

.75 on ldpi (120 dpi)

1.0 on mdpi (160 dpi; baseline)

1.5 on hdpi (240 dpi)

2.0 on xhdpi (320 dpi)

3.0 on xxhdpi (480 dpi)

4.0 on xxxhdpi (640 dpi)

Use this online converter to play around with dpi values.

EDIT: It seems there is no 1:1 relationship between dpi bucket and density. It looks like the Nexus 5X being xxhdpi has a density value of 2.625 (instead of 3). See for yourself in the Device Metrics.


"It looks like the Nexus 5X being xxhdpi has a density value of 2.6 (instead of 3)" - Technically the Nexus 5X is 420dpi and the Nexus 6/6P is 560dpi, neither land directly in one of the standard buckets, just like the Nexus 7 with tvdpi (213dpi). So the site listing those as xxdpi and xxxhdpi is a farce. Your chart IS correct, and those devices will properly scale based one their "special" dpi buckets.
M
Maher Abuthraa

You can use this .. without Context

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);
}

As @Stan mentioned .. using this approach may cause issue if system changes density. So be aware of that!

Personally I am using Context to do that. It's just another approach I wanted to share you with


You might not want to use this. Documentation for getSystem() - "Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc)."
Seconding what @Stan is saying: this is dangerous and you shouldn't be using it, especially now when device form factors are becoming so complex.
This is not considering foldables and ChromeOS devices
T
Tim

If you can use the dimensions XML it's very simple!

In your res/values/dimens.xml:

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

Then in your Java:

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

Since px to dp depends on screen density, I don't know how the OP got 120 in the first place, unless he or she tested the px to dp method on all different screen sizes.
L
Linga

According to the Android Development Guide:

px = dp * (dpi / 160)

But often you'll want do perform this the other way around when you receive a design that's stated in pixels. So:

dp = px / (dpi / 160)

If you're on a 240dpi device this ratio is 1.5 (like stated before), so this means that a 60px icon equals 40dp in the application.


160 is constant, bed answer
@user25 Great answer actually. Haven't you read any docs on android screens? 160 is a common constant all over the place when talking about android screen densities. Quote from docs: "medium-density (mdpi) screens (~160dpi). (This is the baseline density)". Come on, man
A
Adam Stelmaszczyk

Without Context, elegant static methods:

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

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

Resources.getSystem() javadoc says "Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc)." This pretty much says you shouldn't be doing this even if it somehow works.
I just read @AustynMahoney's comment and realized this answer isn't as great as I originally thought, but SO won't let me undo my upvote! Argh!
b
beigirad

using kotlin-extension makes it better

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

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

UPDATE:

Because of displayMetrics is part of global shared Resources, we can use 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()
    

PS: According to @EpicPandaForce's comment:

You should not be using Resources.getSystem() for this, because it does not handle foldables and Chrome OS devices.


Why would you add it as an extension of Int, the responsibility doesn't have to do anything with the Int type.
@htafoya thanks for your feedback. I consider your mention in the last edition.
You should not be using Resources.getSystem() for this, because it does not handle foldables and Chrome OS devices.
K
Kapil Rajput

For DP to Pixel

Create a value in dimens.xml

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

Get that value in pixel as:

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

G
Gunhan

For anyone using Kotlin:

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

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

Usage:

64.toPx
32.toDp

From the docs for Resource.getSystem(): "Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc)."
@HendraWD The docs may be confusing, the dimension units it is mentioning is your application level dimens resources. displayMetrics is not an application level resource. It is a system resource and it returns the correct values. This code is working fine on all of my Prod apps. Never had an issue.
I like this solution, but I initially read the logic as backwards. I was wanting to type the dp value and say myVal.dp. If you think what I was wanting to do reads nicer, then you'll want to swap the divide and multiply logic. Also I think it is slightly better to use roundToInt() instead of toInt() here.
@AdamJohns Because all the View related functions use Pixels as arguments, I though 64.toPx would make more sense. I read it as 64 to -be converted- to pixels. But feel free to change the order and names as you wish. At the end the important part is if you multiply the value by density or not.
You should not be using Resources.getSystem() for this.
t
tabjsina

You can therefore use the following formulator to calculate the right amount of pixels from a dimension specified in 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);
}

This converts dp to pixels but you called your method convertToDp.
+0.5f is explain here --> developer.android.com/guide/practices/… . It's used to round up to the nearest integer.
the only right answer. you could add the source developer.android.com/guide/practices/…
web.archive.org/web/20140808234241/http://developer.android.com/… for a link that still has the content in question
Y
Yan

There is a default util in android SDK: http://developer.android.com/reference/android/util/TypedValue.html

float resultPix = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics())

You should be using this one. As a bonus it will also do SP.
resultPix should be of type int. int resultPix = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics())
K
Khemraj Sharma

Kotlin

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);
}

S
Saravanabalagi Ramachandran

This should give you the conversion dp to pixels:

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

This should give you the conversion pixels to dp:

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

You should not be using Resources.getSystem() for this.
L
Lorenzo Barbagli

Probably the best way if you have the dimension inside values/dimen is to get the dimension directly from getDimension() method, it will return you the dimension already converted into pixel value.

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

Just to better explain this,

getDimension(int resourceId) 

will return the dimension already converted to pixel AS A FLOAT.

getDimensionPixelSize(int resourceId)

will return the same but truncated to int, so AS AN INTEGER.

See Android reference


T
Trinea

like this:

public class ScreenUtils {

    public static float dpToPx(Context context, float dp) {
        if (context == null) {
            return -1;
        }
        return dp * context.getResources().getDisplayMetrics().density;
    }

    public static float pxToDp(Context context, float px) {
        if (context == null) {
            return -1;
        }
        return px / context.getResources().getDisplayMetrics().density;
    }
}

dependent on Context, return float value, static method

from: https://github.com/Trinea/android-common/blob/master/src/cn/trinea/android/common/util/ScreenUtils.java#L15


佚名

to convert Pixels to dp use the TypedValue .

As the documentation mentioned : Container for a dynamically typed data value .

and use the applyDimension method :

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

which Converts an unpacked complex data value holding a dimension to its final floating point value like the following :

Resources resource = getResources();
float dp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 69, resource.getDisplayMetrics());

Hope that Helps .


x
xlecoustillier
float scaleValue = getContext().getResources().getDisplayMetrics().density;
int pixels = (int) (dps * scaleValue + 0.5f);

Is this not just the same as what's covered in many of the other answers to this question?
P
Pavel

In case you developing a performance critical application, please consider the following optimized class:

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;
    }
}

It makes sence only if you do convertations really frequently. In my case I do.
What is 160f? Why do you use it, why is it 160?
160 => 160dpi and this is for converting measures because of formula
S
Saeed Masoumi

More elegant approach using kotlin's extension function

/**
 * 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()

Usage:

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

Love the use of extension here. I read the functions as "convert to function name" which I realize is backwards in this case. To clarify each function's intent, the names of the functions could be updated to read dpToPx and pxToDp, respectively.
From the docs for Resource.getSystem(): "Return a global shared Resources object that provides access to only system resources (no application resources), and is not configured for the current screen (can not use dimension units, does not change based on orientation, etc)."
You should not be using Resources.getSystem() for this.
J
Jared Rummler

This is how it works for me:

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

You can do the same for the width.


K
Kai Wang

A lot of great solutions above. However, the best solution I found is google's design:

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

https://i.stack.imgur.com/nuoMF.png


M
Manaus

To convert dp to pixel

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

To convert pixel to dp.

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

where resource is context.getResources().


The answer is wrong, because you are not converting pixels to dp - you are converting pixels to pixels!
The px2dp is wrong - will return the same value in pixels.
M
Max Base

Kotlin:

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;
}

M
Michael Lowman

You should use dp just as you would pixels. That's all they are; display independent pixels. Use the same numbers you would on a medium density screen, and the size will be magically correct on a high density screen.

However, it sounds like what you need is the fill_parent option in your layout design. Use fill_parent when you want your view or control to expand to all the remaining size in the parent container.


actually my problem is my application is coded for high density screen and now it needs to be converted to low density screen..
modify your pixels for a medium density screen (you can set up a medium density screen in the emulator) and replace the pixel with dp. However, more flexible applications can be made using fill_parent and multiple layouts.
Finally, i had no option but to change all the px to dp manually.. :(
At least next time you'll use dp first and won't have to change anything :) Although it should be possible to use layouts that don't require absolute positioning for most things.
Since it was my first app.. i made this mistake... i ll never do it again...:)
K
Kapil Rajput

PX and DP are different but similar.

DP is the resolution when you only factor the physical size of the screen. When you use DP it will scale your layout to other similar sized screens with different pixel densities.

Occasionally you actually want pixels though, and when you deal with dimensions in code you are always dealing with real pixels, unless you convert them.

So on a android device, normal sized hdpi screen, 800x480 is 533x320 in DP (I believe). To convert DP into pixels /1.5, to convert back *1.5. This is only for the one screen size and dpi, it would change depending on design. Our artists give me pixels though and I convert to DP with the above 1.5 equation.


A
Alexander
private fun toDP(context: Context,value: Int): Int {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
        value.toFloat(),context.resources.displayMetrics).toInt()
}

H
Hitesh Sahu

If you want Integer values then using Math.round() will round the float to the nearest integer.

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

J
JarsOfJam-Scheduler

The best answer comes from the Android framework itself: just use this equality...

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

(converts dp to px)