Expected Result
I have a some UI widgets whose dimensions are design for iPhone 4 Retina device. The unit is in pixels, e.g. a button with 30 pixels wide by 30 pixels high. I wanna copy the design style into Android devices, say the previous 30 by 30 button, takes 30/640 = 4.6875% of the screen width in iPhone 4 Retina and 30/960 = 9.375% of the screen height, then I expect it also takes 4.6875% of the Android device screen width, 9.375% of the screen height.
Problem
Don't know the size scale factor of iPhone 4 Retina device which is used in the following code.
Code
/**
* Change dip value to pixel value using density of current device
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
Log.d("ch", "density of current device : " + scale);
return (int) (dpValue * scale + 0.5f);
}
/**
* Change pixel value to dip value using density of current device
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
Log.d("congliu", "density of current device : " + scale);
return (int) (pxValue / scale + 0.5f);
}
Reference
iPhone 4 Retina
ppi : 326
resolution : 640 by 960 pixels
size scale factor : Unknown
Samsung Galaxy S
ppi : 233
resolution : 480 by 800 pixels
size scale factor : 1.5
Samsung Galaxy Note
ppi : 285
resolution : 800 by 1280 pixels
size scale factor : 2.0
There are several perspectives you should consider.
But i just following a simple way:
px -> dp
My image resources are located in xhdpi folder, using images for retina iphone.
Suppose you have a pixel value in ios, for example, 10,
if you want to get pixel value for your android device:
DisplayMetrics dm = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(dm);
SCREEN_DENSITY = dm.density;
SCREEN_WIDTH_DP = (int) (SCREEN_WIDTH_PIXELS / dm.density);
public static int getScaledWidthPixelsByDP(int desingDP) {
double v = desingDP / 320f * SCREEN_WIDTH_PIXELS;
return (int) v;
}
Assuming that iPhone 4 Retina version's size scale factor is 2.0 (xhdpi in Android), I did a very simple workaround by just give those pixels value a half of it with dp unit. So 30px by 30px button turns to be 15dp by 15dp in xml file. Looks good and works on Google Nexus, Samsung Galaxy S, S2, S4, Note, Note2. The dp2px and px2dp methods are not used.
Related
I have read the officel documentation but still dosn't understand it!
I'm playing and experimenting with the smallest-width layouts in Android and it dosn't give any sense to me.
I have created these layouts: sw-300dp/350dp/360dp/400dp/450dp/500dp/
But my Galaxy S7 use the sw-360dp layout. But 360dp is far from any of the numbers provedid by getDisplayMetrics(); or the numbers of the screen I got from GSMArena. So why do Galaxy S7 use 360dp and not 500dp since the xdpi is 580dp and closer to 500dp?
Test Phone: Samsung Galaxy S7
Screen Size: 5.1"
Resolution: 1440x 2560 (577 ppi)
From det Logcat I retrived the following screen values of the phone:
getDisplayMetrics().densityDpi: 640
getDisplayMetrics().xdpi: 580
getDisplayMetrics().ydpi: 575:
getDisplayMetrics().widthPixels: 1440
The density of the screen on the S7 is 4.0
That means that there are 4 pixels per dp, therefore the screen is 1440 / 4 = 360dp wide.
I use the following methods frequently:
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);
}
I'm testing on a Samsung Galaxy Note 3 which according to the docs is 1080 x 1920 pixels (~386 ppi pixel density). I want to figure out what the screen width in dp is so I can properly apply and check the new size qualifiers.
I've measured the screen width (in portrait) with a good 'ole ruler = 71mm = 2.8 inches
1080 / 2.8 = 386 ppi. Great, this matches the stated density.
I'm trying to figure out how many dp units the screen is wide.
dp = px / (dpi / 160) from here
= 1080 / (386 / 160) = 1080 /
2.4125 = 447.7 dp
So if I do this:
<TextView
android:id="#+id/density_test"
android:layout_width="447dp"
android:layout_height="wrap_content"
android:background="#ffff0000"
android:text="DENSITY TEST 447dp" />
then on my Galaxy Note 3 this view should be almost exactly the width of the screen. But it isn't, it's way wider.
So I set the above view to layout_width="300dp", measured how wide it was with my ruler and extrapolated the actual screen width in dp, based on this test:
300dp = 59mm
Total screen width is 71mm
so extrapolating screen width in dp = (71 / 59) * 300 = 361dp
361dp != 447.7dp
What is going on here? I feel like I must be doing something dumb.
Edit: I tried putting the layout file inside /res/layout-xhdpi but it had no effect. I thought maybe it was scaling it by xhdpi / mdpi since I had the xml in the default /res/layout dir.
The actual pixel size is calculated based on dp, but not as accurate as the formula dp = px / (dpi / 160) does. Indeed, it first determines the screen density (ldpp, mdph, hdpi, xhdpi, xxhdpi, xxxhdpi) and using that density to get one ratio from {0.75, 1.0, 1.5, 2.0, 3.0, (4.0)}.
In you case, the system deems 386dpi as xxhdpi, so it calculates the value by multiplying 3.0, and 360dp would exactly fill the width of your screen (1080p)
I have got a Samsung Galaxy S4 Active
When I execute on Android, the following code:
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int width=dm.widthPixels;
int height=dm.heightPixels;
int dens=dm.densityDpi;
double wi=(double)width/(double)dens;
double hi=(double)height/(double)dens;
double x = Math.pow(wi,2);
double y = Math.pow(hi,2);
double screenInches = Math.sqrt(x+y);
I obtain
width = 1080
heigh = 1920
dens = 480
So, is the used formula is correct, screenInches is 4.589. But the specification said that the screenInches size is 5.
I have also tried with my Samsung Galaxy SII that gives me:
width = 480
heigh = 800
dens = 240
that corresponds to a screenInches of 3.887 instead of 4.3 (as said in the specs)
Why there is this difference between the declared size in inches and the size I obtain?
EDIT: It seems that densityDpi returns one of these values: (120, 160, 213, 240, 320, 480 or 640 dpi).
I don't think that you can get the real screen density from DisplayMetrics.densityDpi. It can return only one of particular constant value.
DisplayMetrics documentation says that
The screen density expressed as dots-per-inch. May be either DENSITY_LOW, DENSITY_MEDIUM, or DENSITY_HIGH.
But Android Compatibility Definition Document (CDD). provides a more comprehensive explanation
Screen Density
The Android UI framework defines a set of standard
logical densities to help application developers target application
resources.
Device implementations MUST report one of the following
logical Android framework densities through the
android.util.DisplayMetrics APIs, and MUST execute applications at
this standard density.
> 120 dpi, known as 'ldpi'
> 160 dpi, known as 'mdpi'
> 213 dpi, known as 'tvdpi'
> 240 dpi, known as 'hdpi'
> 320 dpi, known as 'xhdpi'
> 400 dpi, known as '400dpi'
> 480 dpi, known as 'xxhdpi'
> 640 dpi, known as 'xxxhdpi'
Device implementations SHOULD define the
standard Android framework density that is numerically closest to the
physical density of the screen, unless that logical density pushes the
reported screen size below the minimum supported. If the standard
Android framework density that is numerically closest to the physical
density results in a screen size that is smaller than the smallest
supported compatible screen size (320 dp width), device
implementations SHOULD report the next lowest standard Android
framework density.
You can get the exact physical pixels per inch of the screen in the X and Y dimension from DespalyMetrics.xdpi and DespalyMetrics.ydpi
densityDpi returns the screen density expressed as dots-per-inch, but this value is approximated.
densityDpi is calculated from density which is a logical density of 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.
As said in the documentation:
This value does not exactly follow the real screen size (as given by xdpi and ydpi) , but rather is used to scale the size of the overall UI in steps based on gross changes in the display dpi.
So in order to obtain the correct DPI I can use xdpi and ydpi.
Applying these to my Samsung Galaxy S4 Active, I obtain:
xdpi = 442.451
ydpi = 439.351
My phone spec said ~441 ppi pixel density, so I think these value are correct.
So, getting the average value 440.901 DPI the result is:
screenInches = 4.9963
I have coordinates corresponding to screen resolution 600x400. Now I want to get the relative position of this coordinate for the screen resolution 1280x800. After getting the coordinates, I have to create a link on that coordinate..For example
Suppose I have a coordinate (5,5) for a 600*400 device resolution, so this coordinate will be at the left-bottom of the screen.Now i want to know what will be the relative coordinate of this on a 1280*800 screen resolution device so that it looks at the same position i.e bottom left of screen.
Thanks in advance.
Well sticking to what you asked ,you can get your new pixels as per follow
suppose the coordinates are (6,4) on 600*400 screen size, now calculate the % of x,y as per screen resolution ,as follow
(6 * 100 )/600 = 1%
and
(4* 100)/400 = 1%
now calculate the coordinates as per the new screen size as follow ,
(1 * 1280) /100 = 12.8
and
(1* 800) /100 = 8
so the coordinates in the new screen size are : (12.8, 8) which were previously (6,4) .
But there are better ways to go through in requirements like these , if you could be more specific with what you are actually doing.
Here is formula for converting dp to px, and px to dp based on screen density, so either way you can convert the coordinates appropriated to relative screen density.
public int doToPixel(int dp) {
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
public int pixelToDP(int px) {
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int dp = Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return dp;
}
I have two tablet devices:
1) Asus Memo Pad 172V tablet
Specs given online as: 600 * 1024 pixels, 7.0 inches (~170 ppi pixel density) LINK
Specs through code: 1024 * 552 pixels, 7.0 inches (160 ppi pixel density)
2) MID 7510 tablet
Specs given online as: 800 * 480 pixels, 7.0 inches (no density mentioned anywhere) LINK
Specs through code: 1024 * 552 pixels, 7.0 inches (160 ppi pixel density)
My problem is:
- Both the tablets have the same density and resolutions (by code), so how can I distinguish between them in order to set the bitmap height (width is coming correct for both) as in case of MID7510, the bitmap height is extended slight down.
- Is there any other factors that are responsible for causing different bitmap sizes for both
tablets ?
- Why are specs coming different by code and are given different online ?
Code to get density and resolution:
DisplayMetrics dm = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
int screenHeight = dm.heightPixels;
// Display device dpi (density) value in pixels
int screenDPIy = (int)dm.ydpi;
I'll try to answer to each question but I'll start by the more important one:
- Why are specs coming different by code and are given different online?
Because you find online what material is used for the device, its usually the correct values. You find by code specifications that are set up by the constructor when they are building their android version for their devices. Meaning : it can be false! especially for devices from unknown constructor or Chinese low cost such as 'weisung'.
Given that, answers for the other questions:
- Is there any other factors that are responsible for causing different bitmap sizes for both
tablets?
I dont think so, they have got different screen size and density but system think they are the same... it explain the difference on the final bitmap size.
- How can I distinguish between them?
Definitively not from the screen specifications. If you really need to manage 'weisung' devices you can always check for manufacturer in the device configuration by code. (Assuming they have correctly set up their identification in the manufacturer field XD)
Check the constant value : android.os.Build.MANUFACTURER
I'm really interested to know more in this topic too so any more information is welcome.
The chart was on drawable-hdpi folder.
I was doing simply this:
img.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
The height was not proper in MID 7510 doing so.
Setting height like this worked for both the tablets:
static int imgHeight = 0;
Drawable d = (BitmapDrawable) getResources().getDrawable(R.drawable.chart);
imgHeight = d.getIntrinsicHeight();
final ImageView img = new ImageView(getActivity());
img.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (imgHeight * 1.5)));
This is because 1 hdpi= 1.5 mdpi. So total height should be 1.5 times of image height.