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.