Overriding Layouts

Former Member
Former Member
Greetings Fellow Connectors,

Can anyone give me the dumbed-down version of how to override (select one or another based on situation) layouts in my main program files? Here is an (admittedly very basic) example of what I am talking about:

  • I have a simple watch face with labels for varying information defined in my resources/layouts/layout.xml file. These labels are all defined with color="Gfx.COLOR_WHITE" since my default background color is black.
  • The user now has the option to switch the background color to white. This wouldn't make sense if the labels were to remain white, so...
  • I defined a second layout file as such: resources/layouts/black_label_layout.xml. It is identical to layout.xml with the exception that in here all the label elements are defined as having color="Gfx.COLOR_BLACK"
  • The idea here is to gain a 'negative effect' such that when the background goes light, the labels automatically go dark, and vice-versa.
  • I have fiddled around with this achieving various degrees of success (or not) but cannot seem to load the alternate rez in the right spot.


The SDK docs are rather ambiguous on this matter so I am reaching out to the community here to get a straight answer. Am I even on the right track here or is there a decidedly more efficient solution to this?

Thanks All!
  • You already have a layout for each configuration. All you need to do is change the active layout (usually by doing something to cause setLayout() with the appropriate layout to be called for your view).

    Since we're dealing with a watch face, I'm assuming that you're implementing the onSettingsChanged() callback. You can use that to trigger a call to onLayout() where you can set your layout based on the current settings.

    using Toybox.Application as App;
    using Toybox.System as Sys;
    using Toybox.WatchUi as Ui;
    using Toybox.Lang as Lang;

    class MyView extends Ui.WatchFace
    {
    hidden var mDirty;

    function initialize() {
    WatchFace.initialize();
    mDirty = false;
    }

    function onLayout(dc) {
    Sys.println("View.onLayout");

    var property = App.getApp().getProperty("Property");

    // avoid crash caused by Garmin Connect Mobile
    if (property == null) {
    property = 0;
    }

    // avoid crash caused by Garmin Connect Mobile
    if (property instanceof Lang.String) {
    property = property.toNumber();
    }

    if (property == 0) {
    setLayout(Rez.Layouts.Layout1(dc));
    }
    else {
    setLayout(Rez.Layouts.Layout2(dc));
    }

    // get references to the drawables here if you need to

    mDirty = false;
    }

    function onUpdate(dc) {
    Sys.println("View.onUpdate");

    if (mDirty) {
    onLayout(dc);
    }

    WatchFace.onUpdate(dc);
    }

    function onSettingsChanged() {
    Sys.println("View.onSettingsChanged");

    mDirty = true;

    Ui.requestUpdate();
    }
    }


    class MyApp extends App.AppBase
    {
    hidden var mView;

    function initialize() {
    AppBase.initialize();
    }

    function getInitialView() {
    mView = new MyView();
    return [ mView ];
    }

    function onSettingsChanged() {
    mView.onSettingsChanged();
    }
    }


    If you wanted to use a global variable, you could simplify this quite a bit. I really hate globals, so I won't do that.

    Travis
  • All that said, you could just update the color of the text and background instead of having multiple layouts. This allows you to support many different colors without bloating your application with a bunch of layouts.

    It does require that you either use a custom background drawable or you manually draw the layout. I'm assuming the former...

    class MyView extends Ui.WatchFace
    {
    hidden var mDirty;

    function initialize() {
    WatchFace.initialize();
    }

    function onLayout(dc) {
    Sys.println("View.onLayout");

    setLayout(Rez.Layouts.Layout1(dc));

    var foreColor = App.getApp().getProperty("ForeColor");
    var backColor = App.getApp().getProperty("BackColor");

    // sanity check foreColor, backColor.

    findDrawableById("Background").setColor(backColor);
    findDrawableById("TextField1").setColor(foreColor);
    findDrawableById("TextField2").setColor(foreColor);
    findDrawableById("TextField3").setColor(foreColor);

    mDirty = false;
    }

    function onUpdate(dc) {
    Sys.println("View.onUpdate");

    if (mDirty) {
    onLayout(dc);
    }

    WatchFace.onUpdate(dc);
    }

    function onSettingsChanged() {
    Sys.println("View.onSettingsChanged");

    mDirty = true;

    Ui.requestUpdate();
    }
    }
  • Former Member
    Former Member over 9 years ago
    Thank you very much Travis. I went with the onSettingsChanged() callback method. It seems not to be the best way to go, according to your statements, but it gets me the desired effect and I already had the infrastructure in place to support it so I'm going with it.

    Jeff