How to handle number fonts vertical placement on different devices?

I have seen some threads about this but I still don't fully understand. My next task is to do something about the graphics of my app. I try to get it working on anything from FR230/Fenix3 all the way to FR965. 

I'm using the Graphics.FONT_NUMBER_MEDIUM because the large text font simply isn't large enough. But this number font is different on different devices. Up until now, i have placed everything in a layout.xml (simply because the tutorial I started with used this). I got it somewhat goodlooking on my Fenix5Xplus, but when I looked at some other watches, it looked wrong. Below are some snippets from Fenix3, Fenix5XPlus and FR965.

Should I leave layouts.xml and strictly calculate every postion? But how? I know about TEXT_JUSTIFY_VCENTER but on some devices there are (different) spacing on the top and bottom. So dc.getFontHeight() may or may not include spacing. 

How do people do this? Special case for each device? All I want is for the three rows to fit within my two horizontal lines without interfering with each other...

  • Not gonna claim what I do is optimal but I don't use layouts and I do have special cases for various (families of) devices.

    Couple of annoying special cases I have seen:

    - As you mentioned, some devices like Vivoactive 3/4 have a ton of white space at the top/bottom of number fonts which can be annoying if you're trying to dynamically select a font size which fills the available space (especially if its an app which has configurable layouts/data). In this case I have hardcoded correction constants in my drawing code to account for the extra space

    - Fenix 3 actually has proportional number fonts (as opposed to fixed width), which can lead to text annoyingly "jumping around" if you display a constant number of digits / decimal places (e.g. something like "1.23") for a rapidly changing value (like time or pace) but you center text horizontally relative to a fixed area of the screen

    In your case, if you want to continue to use layouts, you could try to identify the various "families" of devices (for your purposes) and use custom resource paths in your monkey.jungle file so that each family has its own version of the layout.

  • Thank you for your response!

    I was considering skipping layouts, but at least I know how to handle this with layouts and monkey.jungle etc.

    If I were to try your way, handle families of devices directly in the code, how and where do you determine the family of the current device? is it like a case-statement in view.initialize() of something like that?

  • What I do is in onLayout(), I check the screen width, height and shape, and then the size of a specific font (FONT_NUMBER_MEDIUM for example)

  • But do you also check if the font already has spacing on this specific device? Or do you always add a litte margin?

  • I meant ascent and descent here, though (which I can't get to work. Which is strange, because they should have the same api level as getFontHeight but VSCode can't find symbol 'getFontAscent/Descent')...

  • I look in the sim.  I have a test app that shows fonts along with the getFontHeight() for each and lines to show the spacing.  Like this for the fenix5:

    and this for the venu:

  • So you look in sim if the font has spacing. If no spacing, you add some yourself in the code for that specific device?
    Shouldn't it be possible to look at descent/ascent and have someting like min(descent, 10) just to have some margin?

  • It's a matter of what works best for you.  I've been using what I describe for 8 years and it is easy for me to adapt when a new device comes out.

    Using custom fonts can also make things simpler, and for some of the system 6 devices, you also have scalable fonts

  • I use the personality feature which works a bit like css.  You then don’t need many layouts and instead of specifying x, y, font in each layout etc you point it to selectors do that in each personality file.  Then you associate each device to a particular personality file in monkey.jungle.  Then it’s all compiled into device specific layout parameters when you build, without the bloat of lots of layouts.

    I think this has been covered before but if you look inside the SDK folders you’ll find the device specific layout choices the sim uses to show what each device screen looks like with different datafield layouts.  It’s in JSON and thus can be easily parsed to generate your starter personality file layout selectors for every device and their every layout choice.

  • Thank you for your response!

    I was considering skipping layouts, but at least I know how to handle this with layouts and monkey.jungle etc.

    If I were to try your way, handle families of devices directly in the code, how and where do you determine the family of the current device? is it like a case-statement in view.initialize() of something like that?

    To save memory consumed by code (*), I use excludeAnnotations in monkey.jungle to "group" devices into families based on various shared properties, as well as overriding resource paths (if necessary). That way I can use annotations to only select the code that applies to a given device/family. I'll have different versions of functions and constant variables based on certain hardcoded properties in monkey.jungle. I may also have different resources (e.g. json data) based on these properties. (I actually wrote my own janky layout engine to save memory, and for devices that support it, I store the layout data in a json resource as opposed to placing it in app code.)

    It's very tedious, because for each device and each property I care about (e.g. screen resolution, "font family", CIQ level), I have to specify all the things it *isn't*. It does help that once you identify the "families" (based on trial and error, with respect to the things you care about), you can copy and paste your exclude annotations between devices in the same family. It also helps that if you use all your annotations in code, then you're pretty much guaranteed to get a duplicate symbol error if you mess up one of your excludeAnnotations definitions.

    e.g. FR945 isn't CIQ1/CIQ2, it doesn't have fonts like vivoactive 3 (and 4) which have extra whitespace at the top and bottom, it doesn't have a touchscreen, etc.

    (*) I worry about saving memory because most of my apps are data fields and I like to support all the old devices. When I write device apps or watch faces, this kind of thing is less of a concern for me.