Android: How to account for pixel density - android

I am really struggling with trying to handle the pixel density of screens. How do I make my application look the same on all devices? I thought the answer might be changing the units from dp to px:
public static int dpToPx(int dp) {
return (int)(dp / Resources.getSystem().getDisplayMetrics().density);
}
But this didn't seem to work either and I can't seem to find an answer on how to handle this.

If you are in an activity or fragment then, you can call the getResources() method. Then you can convert the dp to px by the below method:
Formula: px = dp * density
public static int dpToPx(int dp) {
return (int)(dp * getResources().getDisplayMetrics().density);
}

Related

pixels to dp convertor based on ppi

I am converting px to dp for given graphics. Going through docs I noticed that 1dp = 3px for 480ppi screen(xxhdpi).
I am testing it on Redmi Note3 which has approx 403 ppi.
I have been provided margins in pixels by my designer. Should I convert those into pixels to by using 1:3 ratio or it should be different
/**
* 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){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return px;
}
/**
* 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){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float dp = px / ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return dp;
}
for more information look into this SOF post Converting pixels to dp

Converting dp into pixels for multiple screen support

I am having a hard time to make this right.
Basically I am creating an ImageView and applying a LayoutParameter to it.
LayoutParams lp = new LayoutParams(width, height);
lp.gravity = Gravity.CENTER;
I know that width and height parameters receive pixel numbers, so I am passing them in DP and converting it to absolute pixels using:
public int convertToPixels(float dpSize){
final float density = getResources().getDisplayMetrics().density;
return ((int) (dpSize * density + 0.5f));
}
As far I know, this should make a drawable fill exactly the same area in different screens, right? Unfortunately, that is not happening at all.
Is there something wrong with these methods I am using?
These two emulators below have the same image and the same amount of DP.
Left emulator is 1.0 density and right one is 2.0. Why still does it look
so different ? Don't undertand..
Use :
public class Convert{
public static float convertDpToPixel(float dp){
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return Math.round(px);
}
}
Just use it in a static way:
float requiredPixel = Convert.convertDpToPixel(16.0);
For more info: https://developer.android.com/guide/practices/screens_support.html

How to convert DP, PX, SP among each other, especially DP and SP?

I have known the difference among DP, SP and PX. And after searching this topic, I found nothing satisfying me completely. Maybe this post is a duplicate, but I still want to know what is the formula of converting from DP to PX, and DP to SP, from SP to PX, from PX to SP, from SP to DP, from DP to SP? I have known some codes to do this, but they are imperfect.
DP to PX:
public static int dpToPx(float dp, Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
SP to PX:
public static int spToPx(float sp, Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics());
}
DP to SP:
public static int dpToSp(float dp, Context context) {
return (int) (dpToPx(dp, context) / context.getResources().getDisplayMetrics().scaledDensity);
}
The accepted answer is missing a few useful conversions.
SP to PX
float sp = 20;
float px = sp * getResources().getDisplayMetrics().scaledDensity;
or
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
PX to SP
float px = 70;
float sp = px / getResources().getDisplayMetrics().scaledDensity;
DP to PX
float dp = 20;
float px = dp * getResources().getDisplayMetrics().density;
or
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
PX to DP
float px = 70;
float dp = px / getResources().getDisplayMetrics().density;
Notes
The floats I chose above (20 and 70) were arbitrary values. You can plug in different numbers if you like.
px refers to pixels. The number of pixels that a device has per inch of screen space is called the density.
dp means density-independent pixels. That is, no matter what device is used, the actual size should be the same. For example, if I set a view to be 100 dp wide, it will have the same width on a new high density phone as it does on an old low density phone. (If I had set the width to 100 px, on the other hand, it would appear large on a low density phone and small on a high density phone.) Density is measured in dots per inch (DPI). The formula is px = dp * density. So you just multiply or divide by the density to convert between px and dp.
sp means scale-independant pixels. It is just used for fonts, not views. It is similar to dp except it also factors in the user preferences. This density with user preferences taken into account is known as scaled density. Setting a TextView font to a size of 30 sp, for example, will make the text generally appear to be the same physical size on all devices. However, your grandmother may have her preferred font size maxed all the way up in her phone settings, so 30 sp text will look bigger on her phone than it does on yours. The formula is px = sp * scaledDensity.
Meaning of DP and SP
DP to SP conversion is not generally useful
For converting Dimension to Integer or Pixel you need to use "getDimensionPixelSize(R.dimen.your_dp_value)" function, Like...
Make a value in dimens.xml
<dimen name="padding_10">10dp</dimen>
Now for That value in pixel or integer you can use as like below:
int sizeInPixel = context.getResources().getDimensionPixelSize(R.dimen.padding_10);
For kotlin I created an extension function:
fun Number.spToPx(context: Context) = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, this.toFloat(), context.resources.displayMetrics).toInt()
You can use it like 16.spToPx(context) or 16.5.spToPx(context)
(I place such functions in a KotlinExtensions.kt file)
You can write a method, that doesn't need context or resources:
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);
}
By analogy, other quantities can be converted.
According to TypedValue#applyDimension source code and take advantage of Kotlin extension:
val Float.toSp get() = this * Resources.getSystem().displayMetrics.scaledDensity
Other extensions from link
val Float.toPx get() = this * Resources.getSystem().displayMetrics.density
val Float.toDp get() = this / Resources.getSystem().displayMetrics.density

Passing from one unit to another in android?

Well, i been trying to figure out, is there a simple way to pass from dp or milimetres or even inches to px. (like: 6dp would be 20px)
It seems everything is usually done in px but i want to use these in orther to keep the proportions in different screens and have a rough sense of how big is going to be (guessing with pixels feels really...bothering)
Thanks in advance and sorry if the question is too vague.
There is no way to convert milimetres or even inches to px programmatically. If you want to support different screens, you can follow the document here.
To convert dp to px, you can also use the following method:
/**
* Convert dp to px.
* #param context
* #param dp the input dp.
* #return the output px.
*/
public static int dpToPx(Context context, int dp) {
float density = context.getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5);
}
Update:
Here is how to get a device's width and height pixels:
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int widthPixels = metrics.widthPixels;
int heightPixels = metrics.heightPixels;

widget size in pixels

How to get widget size in pixels?
I am using Note 3 smartphone, I took screenshot and calculated manually, should be 1020px x 316px (+-5px), 4x1widget (resizable).
I tried the code bellow, but I get wrong results:
minW_dp=324dp, maxW_dp=439dp, minH_dp=69dp, maxH_dp=88dp.
minW=782px, maxW=1060px, minH=167px, maxH=213px.
How to get real size of widget in pixels?
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)
{
int minW_dp = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
int maxW_dp = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH);
int minH_dp = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
int maxH_dp = newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
int minW = dpToPx(minW_dp, context);
int maxW = dpToPx(maxW_dp, context);
int minH = dpToPx(minH_dp, context);
int maxH = dpToPx(maxH_dp, context);
bitmap = Bitmap.createBitmap(minW, maxH, Config.ARGB_8888);
}
public static int dpToPx(int dp, Context context)
{
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
Your dpToPx calculation is wrong. You need to change it from:
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
to
int px = Math.round(dp * displayMetrics.density);
If you read the Docs it gets obvious why your calculation couldn't work. (Note: DisplayMetrics.xdpi seems to be messed up on some devices as well, see this post)
Following what displayMetrics.xdpi represents:
The exact physical pixels per inch of the screen in the X dimension.
But you don't wanna get the pixels per inch of your screen. You wanna get the scale-factor, that's where displayMetrics.density comes into play:
The logical density of the display. This is a scaling factor for the
Density Independent Pixel unit, where one DIP is one pixel on an
approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen),
providing the baseline of the system's display. Thus on a 160dpi
screen this density value will be 1; on a 120 dpi screen it would be
.75; etc.
Note: The Note 3 is a xxhdpi device and thus has a scale-factor of 3.0 (480dpi).
Edit
An other way to calculate the dp->px:
(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, maxH_dp, context.getResources().getDisplayMetrics());

Categories

Resources