23 Sept 2015

Which unit of measurement does the Paint.setTextSize(float) use in Android?

Question:
I want to draw text with an specific height(in pixels) on a view using Canvas. Can you simply use Paint.setTextSize(float) with the number of pixels or is this using dp or sp?

Answer:


It uses pixels, but you can convert it to dp using this code:
double getDPFromPixels(double pixels) {
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    switch(metrics.densityDpi){
     case DisplayMetrics.DENSITY_LOW:
                pixels = pixels * 0.75;
                break;
     case DisplayMetrics.DENSITY_MEDIUM:
                 //pixels = pixels * 1;
                 break;
     case DisplayMetrics.DENSITY_HIGH:
                 pixels = pixels * 1.5;
                 break;
    }
    return pixels;
}

These methods include DisplayMetrics that can be added into future Android SDK.
Pixels to dip:
public static float getDipFromPixels(float px) {
        return TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_PX,
                px,
                Resources.getSystem().getDisplayMetrics()
        );
}
Dip to pixels:
public static float getPixelsFromDip(float dip) {
        return TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP, 
                dip,
                Resources.getSystem().getDisplayMetrics()
        );
}









how to get the user prefered units ? (metric (meters) or imperial (miles)) in Android?

I would like to know what are the user prefered units between miles and meters. It seems that the "Locale" class doesn't allow that. 
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Although there isn't a way to get the preferred units using Locale, you can assume by default that if the Locale indicates the United States or Myanmar (locale.getISO3Country().equalsIgnoreCase("usa") || locale.getISO3Country().equalsIgnoreCase("mmr")) the preferred units are imperial, otherwise the preferred units are metric.



I don't think there's necessarily a correct answer to the Metric/Imperial question for all locales.
For example, in the UK we generally use miles for large distances like a car journey, feet and inches for people's height but may use meters to describe the dimensions of a room.
We almost always use litres for measuring liquid volume except for beer and sometimes milk which can still be sold in pints.
Kilograms are usually preferred to pounds and ounces but most recipe books will list both since some people still have scales which use the latter.
I think you will have to do some research to find out which countries use which scale for what you're interested in measuring (and of course, give the user the option to select one or the other manually.)

Since some people outside the US may have set the language to English (US) it is eventually better to take the return value of getSimCountryIso() instead of getISO3Country()
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String countryCode = tm.getSimCountryIso();
If the countryCode is us the SIM card is an american and therefore imperial units might be the better choice - in any other case the metric system is applied

1 Sept 2015

Using locale settings to detect wheter to use imperial units

Just give the user the option to choose a preferred unit in a settings menu. If it is a traveling user you don't want the app to be geographically aware, IMO.
In the end I went for the following solution.
public class UnitLocale {
    public static UnitLocale Imperial = new UnitLocale();
    public static UnitLocale Metric = new UnitLocale();

    public static UnitLocale getDefault() {
            return getFrom(Locale.getDefault());
    }
    public static UnitLocale getFrom(Locale locale) {
        String countryCode = locale.getCountry();
        if ("US".equals(countryCode)) return Imperial; // USA
        if ("LR".equals(countryCode)) return Imperial; // liberia
        if ("MM".equals(countryCode)) return Imperial; // burma
        return Metric;
    }
}
Use it like this for example.
if (UnitLocale.getDefault() == UnitLocale.Imperial) convertToimperial();
If convert methods are also need they can preferably be added to subclasses of UnitLocale. I only needed to detect wheter to use imperial units and send it to the server.
Using ints over java objects have extremely slim performance gains and makes the code harder to read. Comparing two references in java is comparable in speed to comparing two ints. Also using objects allow us to add methods to the UnitLocale class or subclasses such as, convertToMetric, etc.

25 Aug 2015

How do convert pt to sp in Android?

The problem is that it depends on the density. Really, they're not good measurements to try to compare.
  • Points: There are 12 points in a pica, 6 picas per inch, so 72 (more accurately 72.27) points per inch.
  • Device-Independent Pixels (DP): These will be equal to the pixel size for MDPI displays, 1.5x the pixel size for HDPI displays, and 2x the pixel size for XHDPI displays. (e.g. 12dp = 12px MDPI, 18px HDPI, 24px XHDPI).
  • Scaled Pixels: These will be equivalent to the DP value, but scaled according to the user preference to be smaller or larger.
If you're designing at 72 dpi (Photoshop default DPI setting), an 8pt font would be equivalent to 8px, which would be 8px at MDPI, 12px at HDPI, and 16px at XHDPI, or more simply 8dp for all densities. I don't know how much scaling gets applied to different SP settings, so you'd have to hunt that out, but basically I would just give them the mockup and let them size it appropriately from there -- surely they can get it pretty close visually from that.

In contrast to dp (and sp with minus the user text sizing factor), pt has pretty much the exact same physical measurement. Dp is more or less a rough approximation, since it uses the same factor for a wide dpi range.
You cannot convert pt to sp, like said by Flavio. You can however convert pt to dp roughly (+/- 30% in extreme cases, but at least expect +/-10%).
Using the dpi buckets (ldp=120,mdpi=160,hdpi=240,xhdpi=320) the most generic converstion factor would be x0.45 (= 72/120*0.75 = 72/160*1 = 72/240*1,5 = 72/320*2) if using the exact dpi bucket values.
For more precise factors:
  • mdpi (scaling factor: 1.0) 10.1" WXGA Screen (149dpi) is 0.483076923 (= 72/149 * 1.0)
  • hdpi (scaling factor: 1.5) Nexus 7 (216dpi) is 0.5 (= 72/216 * 1.5)
  • xhdpi (scaling factor: 2.0) Nexus 10 (300dpi) is 0.48 (= 72/300 * 2)
So 12 pt would be on...
  • 10.1" WXGA: 24.84dp
  • Nexus 7: 24 dp
  • Nexus 10: 25dp
  • dpi bucket value: 26.666dp
You could use the same values for sp, but keep in mind, that these could be in/decreased by the users settings.

31 Jul 2015

Does setWidth(int pixels) use dip or px?

Does setWidth(int pixels) use device independent pixel or physical pixel as unit? For example, does setWidth(100) set the a view's width to 100 dips or 100 pxs?

It uses pixels, but I'm sure you're wondering how to use dips instead. The answer is in TypedValue.applyDimension(). Here's an example of how to convert dips to px in code:
// Converts 16 dip into its equivalent px
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics());

You can also call nbDips * getResources().getDisplayMetrics().density







26 Jun 2015

How to get the screen density programmatically in android?

How to find the screen dpi of the current device?

You can get info on the display from the DisplayMetrics struct:
DisplayMetrics metrics = getResources().getDisplayMetrics();
getResources().getDisplayMetrics().density;
Though Android doesn't use a direct pixel mapping, it uses a handful of quantized Density Independent Pixel values then scales to the actual screen size. So the metrics.densityDpi property will be one of the DENSITY_??? constants (120, 160, 213, 240, 320, 480 or 640 dpi).
If you need the actual lcd pixel density (perhaps for an OpenGL app) you can get it from the metrics.xdpi and metrics.ydpi properties for horizontal and vertical density respectively.
If you are targeting API Levels earlier than 4. The metrics.density property is a floating point scaling factor from the reference density (160dpi). The same value now provided by metrics.densityDpican be calculated
int densityDpi = (int)(metrics.density * 160f);
Here,

0.75 - ldpi
1.0 - mdpi
1.5 - hdpi
2.0 - xhdpi
3.0 - xxhdpi
4.0 - xxxhdpi


Or,
Use follwing code,

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
switch(metrics.densityDpi){
     case DisplayMetrics.DENSITY_LOW:
                break;
     case DisplayMetrics.DENSITY_MEDIUM:
                 break;
     case DisplayMetrics.DENSITY_HIGH:
                 break;
}
This will work in API lavel 4 or higher.

More details about px, dip, dp and sp units in Android

1) px = dp * ( dpi / 160 )
2) px is one pixel.
3) sp is scale-independent pixels.
4) sp for font sizes
5) dip is Density-independent pixels.
6) dip for everything else, where dip == dp
7) 100*100 px image for mdpi
8) 150*150 px image for hdpi
9) 200*200 px image for xhdpi
10) 1080x1920    save it in “drawable-xxhdpi” folder
11) 480x800      save it in “drawable-hdpi” folder
12) 320x480      save it in “drawable-mdpi” folder
13) 1280x720     save it in “drawable-xhdpi” folder
14) If running on mdpi device, 150x150 px image will take up 150*150 dp of screen space.
15) If running on hdpi device, 150x150 px image will take up 100*100 dp of screen space.
16) If running on xhdpi device, 150x150 px image will take up 75*75 dp of screen space.

Supporting Multiple Screens, Sizes and Densities

Use dp (== dip) for everything but for fonts use sp
Pretty much everything about this and how to achieve the best support for multiple screens with different sizes and density is very well documented here:
If you are any serious about developing an Android app for more than one type of device, you should have read the above at least once. In addition to that it is always a good thing to know the actual number of active devices that have a particular screen configuration.

25 Jun 2015

Android Dimension Description 2

Screen size: Actual physical size, measured as the screen's diagonal. For simplicity, Android groups all actual screen sizes into four generalized sizes: small, normal, large, and extra large.
Screen density: The quantity of pixels within a physical area of the screen; usually referred to as dpi (dots per inch). For example, a "low" density screen has fewer pixels within a given physical area, compared to a "normal" or "high" density screen. For simplicity, Android groups all actual screen densities into four generalized densities: low, medium, high, and extra high.
Orientation: The orientation of the screen from the user's point of view. This is either landscape or portrait, meaning that the screen's aspect ratio is either wide or tall, respectively. Be aware that not only do different devices operate in different orientations by default, but the orientation can change at runtime when the user rotates the device.
Resolution: The total number of physical pixels on a screen. When adding support for multiple screens, applications do not work directly with resolution; applications should be concerned only with screen size and density, as specified by the generalized size and density groups.
Density-independent pixel (dp): A virtual pixel unit that you should use when defining UI layout, to express layout dimensions or position in a density-independent way. The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is the baseline density assumed by the system for a "medium" density screen. At runtime, the system transparently handles any scaling of the dp units, as necessary, based on the actual density of the screen in use. The conversion of dp units to screen pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units when defining your application's UI, to ensure proper display of your UI on screens with different densities.

Android Dimension Description 1

px
Pixels - corresponds to actual pixels on the screen.
in
Inches - based on the physical size of the screen.
1 Inch = 2.54 centimeters
mm
Millimeters - based on the physical size of the screen.
pt
Points - 1/72 of an inch based on the physical size of the screen.
dp
Density-independent Pixels - an abstract unit that is based on the physical density of the screen. These units are relative to a 160 dpi screen, so one dp is one pixel on a 160 dpi screen. The ratio of dp-to-pixel will change with the screen density, but not necessarily in direct proportion. Note: The compiler accepts both "dip" and "dp", though "dp" is more consistent with "sp".
sp
Scale-independent Pixels - this is like the dp unit, but it is also scaled by the user's font size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted for both the screen density and user's preference.