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?
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)
/**
* 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);
}
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)."
DisplayMetrics.DENSITY_DEFAULT
instead of 160f
developer.android.com/reference/android/util/…
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;
}
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.
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
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);
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.
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.
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.
Resources.getSystem()
for this, because it does not handle foldables and Chrome OS devices.
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);
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
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.
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);
}
convertToDp
.
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())
resultPix
should be of type int. int resultPix = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,getResources().getDisplayMetrics())
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);
}
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);
}
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.
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
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 .
float scaleValue = getContext().getResources().getDisplayMetrics().density;
int pixels = (int) (dps * scaleValue + 0.5f);
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;
}
}
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}")
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.
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.
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
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().
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;
}
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.
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.
private fun toDP(context: Context,value: Int): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
value.toFloat(),context.resources.displayMetrics).toInt()
}
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);
}
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)
Success story sharing
applyDimension
? Can I just dogetResource().getDisplayMetrics()
, or is there something else?Context
object useResources.getSystem().getDisplayMetrics()