WatchApp : add many DataField into a single view

I'm thinking about an app which should looks like native running/cycling apps. At least for the aspect of configurable data screens and data fields.

At this time I've made my first steps with the API/MonkeyC by developing a single view divided into 4 fields implemented from scratch.

This works fine but I've the feeling to reinvent the wheel for the rendering aspect as DataField and SimpleDataField exist...
Hence my question: how to have a view implementing 1 to n fields by using available classes ?

Thanks !
  • Former Member
    Former Member over 9 years ago
    You can't (or shouldn't) use DataField or SimpleDataField in an app.
    You can create a custom drawable and draw that on multiple places on the screen.

    I'd take the following approach:
    - Create a Model class that has attributes for holding the data you want in your data fields and one or more functions to get that data;
    - Create custom Drawables as Data fields, which you can place on the screen in a layout;
    - Have a global timer that fires every second and first updates the model and then do a Ui.requestUpdate();
    - In the draw function of the Data field call the model to retrieve data. You can either call one method with a parameter to distinguish which info to get (have a number or symbol as parameter for your data field) or have separate functions (have a method object as parameter for your data field)
  • Ok thanks for having confirmed that this is not possible without reimplementing all the rendering part.
    That's a bit painful but the SDK have to be used as it is :) anyway this might be something to improve..
    I'll carefully keep your design approach in mind, thanks again TeunMo ;)
  • Example source needed for absolute beginner with Connect IQ

    You can't (or shouldn't) use DataField or SimpleDataField in an app.
    You can create a custom drawable and draw that on multiple places on the screen.

    I'd take the following approach:
    - Create a Model class that has attributes for holding the data you want in your data fields and one or more functions to get that data;
    - Create custom Drawables as Data fields, which you can place on the screen in a layout;
    - Have a global timer that fires every second and first updates the model and then do a Ui.requestUpdate();
    - In the draw function of the Data field call the model to retrieve data. You can either call one method with a parameter to distinguish which info to get (have a number or symbol as parameter for your data field) or have separate functions (have a method object as parameter for your data field)


    Hi TeunMo,
    I understand what you are writing but it is difficult for a beginner to design a multiple field monitor app from scratch. Do you have a link to a real example source code ?
    Based on that, it would be easier to start my own first steps and adjust to my own needs.
    Thanks so much
  • Former Member
    Former Member over 9 years ago
    Just so happens I built this for myself. Hope it's useful. Part 1 of 2

    ./resources/layouts/layout.xml
    <?xml version="1.0" standalone="yes" ?><!DOCTYPE layout [
    <!ENTITY leftLblX "3">
    <!ENTITY rightLblX "108">
    <!ENTITY leftValX "50">
    <!ENTITY midValX "102">
    <!ENTITY rightValX "155">
    <!ENTITY topLblY "2">
    <!ENTITY bottomLblY "79">
    <!ENTITY topValY "21">
    <!ENTITY bottomValY "98">
    <!ENTITY lblFont "Gfx.FONT_SMALL">
    <!ENTITY valFont "Gfx.FONT_NUMBER_HOT">
    ]>
    <layouts>
    <layout id="DataFields1">
    <drawable class="DataFieldDivisions" />
    <label id="DataField1Lbl" x="&leftLblX;" y="&topLblY;" font="Gfx.FONT_MEDIUM" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField1Val" x="&midValX;" y="73" font="Gfx.FONT_NUMBER_THAI_HOT" justification="Gfx.TEXT_JUSTIFY_CENTER|Gfx.TEXT_JUSTIFY_VCENTER" color="Gfx.COLOR_BLACK" />
    </layout>
    <layout id="DataFields2">
    <drawable class="DataFieldDivisions" />
    <label id="DataField1Lbl" x="&leftLblX;" y="&topLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField1Val" x="&midValX;" y="&topValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    <label id="DataField2Lbl" x="&leftLblX;" y="&bottomLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField2Val" x="&midValX;" y="&bottomValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    </layout>
    <layout id="DataFields3">
    <drawable class="DataFieldDivisions" />
    <label id="DataField1Lbl" x="&leftLblX;" y="&topLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField1Val" x="&midValX;" y="&topValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    <label id="DataField2Lbl" x="&leftLblX;" y="&bottomLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField2Val" x="&leftValX;" y="&bottomValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    <label id="DataField3Lbl" x="&rightLblX;" y="&bottomLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField3Val" x="&rightValX;" y="&bottomValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    </layout>
    <layout id="DataFields4">
    <drawable class="DataFieldDivisions" />
    <label id="DataField1Lbl" x="&leftLblX;" y="&topLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField1Val" x="&leftValX;" y="&topValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    <label id="DataField2Lbl" x="&rightLblX;" y="&topLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField2Val" x="&rightValX;" y="&topValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    <label id="DataField3Lbl" x="&leftLblX;" y="&bottomLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField3Val" x="&leftValX;" y="&bottomValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    <label id="DataField4Lbl" x="&rightLblX;" y="&bottomLblY;" font="&lblFont;" justification="Gfx.TEXT_JUSTIFY_LEFT" color="Gfx.COLOR_BLACK" />
    <label id="DataField4Val" x="&rightValX;" y="&bottomValY;" font="&valFont;" justification="Gfx.TEXT_JUSTIFY_CENTER" color="Gfx.COLOR_BLACK" />
    </layout>
    </layouts>


    ./resources/resources.xml
    <resources> <properties>
    <property id="DataFieldQty" type="number">4</property>
    <property id="DataField1" type="number">2</property>
    <property id="DataField2" type="number">0</property>
    <property id="DataField3" type="number">1</property>
    <property id="DataField4" type="number">3</property>
    </properties>
    <settings>
    <setting propertyKey="@Properties.DataFieldQty" title="@Strings.DataFieldQtyTitle">
    <settingConfig type="list">
    <listEntry value="1">@Strings.One</listEntry>
    <listEntry value="2">@Strings.Two</listEntry>
    <listEntry value="3">@Strings.Three</listEntry>
    <listEntry value="4">@Strings.Four</listEntry>
    </settingConfig>
    </setting>
    <setting propertyKey="@Properties.DataField1" title="@Strings.DataField1Title">
    <settingConfig type="list">
    <listEntry value="0">@Strings.FieldSpeed</listEntry>
    <listEntry value="1">@Strings.FieldCadence</listEntry>
    <listEntry value="2">@Strings.FieldPower</listEntry>
    <listEntry value="3">@Strings.FieldHR</listEntry>
    <listEntry value="4">@Strings.FieldTemp</listEntry>
    </settingConfig>
    </setting>
    <setting propertyKey="@Properties.DataField2" title="@Strings.DataField2Title">
    <settingConfig type="list">
    <listEntry value="0">@Strings.FieldSpeed</listEntry>
    <listEntry value="1">@Strings.FieldCadence</listEntry>
    <listEntry value="2">@Strings.FieldPower</listEntry>
    <listEntry value="3">@Strings.FieldHR</listEntry>
    <listEntry value="4">@Strings.FieldTemp</listEntry>
    </settingConfig>
    </setting>
    <setting propertyKey="@Properties.DataField3" title="@Strings.DataField3Title">
    <settingConfig type="list">
    <listEntry value="0">@Strings.FieldSpeed</listEntry>
    <listEntry value="1">@Strings.FieldCadence</listEntry>
    <listEntry value="2">@Strings.FieldPower</listEntry>
    <listEntry value="3">@Strings.FieldHR</listEntry>
    <listEntry value="4">@Strings.FieldTemp</listEntry>
    </settingConfig>
    </setting>
    <setting propertyKey="@Properties.DataField4" title="@Strings.DataField4Title">
    <settingConfig type="list">
    <listEntry value="0">@Strings.FieldSpeed</listEntry>
    <listEntry value="1">@Strings.FieldCadence</listEntry>
    <listEntry value="2">@Strings.FieldPower</listEntry>
    <listEntry value="3">@Strings.FieldHR</listEntry>
    <listEntry value="4">@Strings.FieldTemp</listEntry>
    </settingConfig>
    </setting>
    </settings>
    <strings>
    <string id="AppName">DF APP</string>
    <string id="DataFieldQtyTitle">Data Field Count</string>
    <string id="One">1</string>
    <string id="Two">2</string>
    <string id="Three">3</string>
    <string id="Four">4</string>
    <string id="DataField1Title">Field 1</string>
    <string id="DataField2Title">Field 2</string>
    <string id="DataField3Title">Field 3</string>
    <string id="DataField4Title">Field 4</string>
    <string id="FieldSpeed">Speed</string>
    <string id="FieldCadence">Cadence</string>
    <string id="FieldPower">Power</string>
    <string id="FieldHR">Heart Rate</string>
    <string id="FieldTemp">Temperature</string>
    </strings>
    </resources>
  • Former Member
    Former Member over 9 years ago
    Just so happens I built this for myself. Hope it's useful. Part 2 of 2

    ./source/dfApp.mc
    using Toybox.Application as App;using Toybox.Graphics as Gfx;
    using Toybox.WatchUi as Ui;


    class dfApp extends App.AppBase {
    function initialize() {
    AppBase.initialize();
    }
    //! onStart() is called on application start up
    function onStart() {
    }
    //! onStop() is called when your application is exiting
    function onStop() {
    }
    //! Return the initial view of your application here
    function getInitialView() {
    return [ new DataFieldView() ];
    }
    //! New app settings have been received so trigger a UI update
    function onSettingsChanged() {
    Ui.requestUpdate();
    }
    }


    class DataFieldDivisions extends Ui.Drawable {
    function initialize() {
    Drawable.initialize( { :identifier => "DataFieldDivisions" } );
    }
    function draw( dc ){
    //! Set the theme colors then clear the screen
    dc.setColor( Gfx.COLOR_BLUE, Gfx.COLOR_WHITE );
    dc.clear();
    var fields = App.getApp().getProperty( "DataFieldQty" );
    //! Draw horizontal line
    if( fields > 1 ){
    dc.setPenWidth(6);
    dc.drawLine( 0, dc.getHeight()/2, dc.getWidth(), dc.getHeight()/2 );
    }
    //! Draw bottom vertical line
    if( fields > 2 ){
    dc.drawLine( dc.getWidth()/2, dc.getHeight()/2, dc.getWidth()/2, dc.getHeight() );
    }
    //! Draw top vertical line
    if( fields > 3 ){
    dc.drawLine( dc.getWidth()/2, 0, dc.getWidth()/2, dc.getHeight()/2 );
    }
    }
    }


    class DataFieldView extends Ui.View {
    hidden enum {
    SPEED,
    CADENCE,
    POWER,
    HR,
    TEMP
    }
    hidden var numFields;
    function initialize() {
    View.initialize();
    }
    //! Load your resources here
    function onLayout( dc ) {
    updateLayout( dc );
    }
    hidden function updateLayout( dc ) {
    //! Select a layout based on the amount of datafields
    numFields = App.getApp().getProperty( "DataFieldQty" );
    if( numFields == 1 ) {
    setLayout( Rez.Layouts.DataFields1( dc ) );
    }
    else if( numFields == 2 ) {
    setLayout( Rez.Layouts.DataFields2( dc ) );
    }
    else if( numFields == 3 ) {
    setLayout( Rez.Layouts.DataFields3( dc ) );
    }
    else {
    setLayout( Rez.Layouts.DataFields4( dc ) );
    }
    }
    //! Called when this View is brought to the foreground. Restore
    //! the state of this View and prepare it to be shown. This includes
    //! loading resources into memory.
    function onShow() {
    }
    //! Update the view
    function onUpdate( dc ) {
    //! Need to load a new layout if the field count has changed
    if( numFields != App.getApp().getProperty( "DataFieldQty" ) ){
    updateLayout( dc );
    }
    //! Assign the correct data based on field configuration
    for( var i = 0; i < numFields; i++ ){
    var fieldNum = "DataField" + (i + 1).toString();
    var type = App.getApp().getProperty( fieldNum );
    if( type == SPEED ){
    var label = View.findDrawableById( fieldNum + "Lbl" );
    label.setText( "Speed" );
    var text = View.findDrawableById( fieldNum + "Val" );
    text.setText( "000" );
    }
    else if( type == CADENCE ){
    var label = View.findDrawableById( fieldNum + "Lbl" );
    label.setText( "Cadence" );
    var text = View.findDrawableById( fieldNum + "Val" );
    text.setText( "000" );
    }
    else if( type == POWER ){
    var label = View.findDrawableById( fieldNum + "Lbl" );
    label.setText( "Power" );
    var text = View.findDrawableById( fieldNum + "Val" );
    text.setText( "000" );
    }
    else if( type == HR ){
    var label = View.findDrawableById( fieldNum + "Lbl" );
    label.setText( "Heart Rate" );
    var text = View.findDrawableById( fieldNum + "Val" );
    text.setText( "000" );
    }
    else if( type == TEMP ){
    var label = View.findDrawableById( fieldNum + "Lbl" );
    label.setText( "Temp" );
    var text = View.findDrawableById( fieldNum + "Val" );
    text.setText( "000" );
    }
    }
    //! Call the parent onUpdate function to redraw the layout
    View.onUpdate( dc );
    }
    //! Called when this View is removed from the screen. Save the
    //! state of this View here. This includes freeing resources from
    //! memory.
    function onHide() {
    }
    }


  • Thanks for that example !
    Will try it right now :-)