DynaWorks Tutorial

How to write applications with the DynaWorks framework


DynaPage: Table Of Content

DynaPage - creating multi-screen applications easily

DynaPage is the application and GUI oriented part of the framework. You can use the these classes to create an application Most applications have different screens or views where application-specific data is displayed and manipulated by the user. For example you can have a view with a listbox of all data records in your custom database; if the user selects a single entry you may want to display a new 'view' where the user can manipulate the data in that record and can switch back to the 'view' with the overall selection list.

The layout and functionality of each 'view' is application-specific; the DynaWorks framework only supports you in creating such views easily. Just to get the words right: A screen or view is called a Page in the DynaWorks framework.

A Page will contain UserWidget controls, that are the user interface elements that display the data and interact with the user. All standard controls like Button, CheckBox, ComboxBox, RadioButton, ListBox, TreeView and many more are part of DynaWorks. It is very easy to write your own widgets as described in a seperate chapter.

If you use the MVC (Model-View-Controler) paradigm as a base for your software development, you already have separated the data (Model) from the display process (View) and your GUI logic (Controler) and you will probably find no difficulties in using DynaWorks because the framework supports the MVC approach in some primitve way; otherwise you might take a closer look on the examples supplied with the framework.

The following sections will explain how to write an application using the DynaWorks framework.

Back to top of page ...

"Keep it as simple as possible - but no simpler..."

these words accredited to Albert Einstein, are the leading idea behind DynaWorks framework - maybe because I am a physicist myself.

In the beginning I "only" wanted a simple, easy-to-use but still flexible framework for Java-based PalmOS application development for myself. But the developing the framework itself was so exciting and so much fun, that I thought it might be of interest to more than just me - so I released it as Open Source.

A lot of people have downloaded DynaWorks - much more than I ever expected. And there where a lot of wishes too: and wherever they made sense to me, I have incorporated them. But the goal is still to keep it as simple as possible. If you want to learn more about the internal technology of DynaWorks in the Technologysection.

In the DynaWorks part of the framework every application consists of one or more so-called pages. A page can hold any number of user interface controls like buttons, checkboxes and the like. By utilizing more pages in an application you can present different views of the app to the user.

When I thought of developing a GUI framework I decided not to use the standard AWT approach which I find oversized for Palm apps. If you have a screen of just 160x160 pixel, there is for example no real need for a layout manager. So my goal was to keep the framework as small as possible but nevertheless to allow fullfledged application development.

Back to top of page ...

A very simple demo

Let's write a small demo application that will help you to catch the basic ideas behind the scenes. It will consist of only one page that contains a 'exit' button and a text.

First we write the class that will be executed by the JVM; let's call the file MyApp.java:
Please note that the following examples are written to work with the J9 implementation from IBM. If you are using a different environment, please change the source code accordingly. You can find more information on how to set the environment in the next section.

(You can find all the example source files in the examples directory of your Dynaworks installation)

import brf.j2me.dynaworks.*;
import brf.j2me.dynaworks.env.*;
import brf.j2me.dynaworks.env.j9.*;

public class MyApp {

   public static void main (String[] args) {

      // define the J2ME implementation
      Environment.setImpl (new J9Impl ());

      // create an application object
      Application theApp = new Application ("MyApp");

      // add a page
	  theApp.addPage (new MyPage ("MyPage"));

      // run the page
	  theApp.run ("MyPage");
   }
}

The static main() method instanciates a new named Application object. The name of the application must be unique; at least if different DynaWorks applications run inside the same KVM.

Back to top of page ...

Adding a page to the application

The next step is to add a Page object to the application. A Page object encapsulates an user interface screen representation, e.g. a full-screen fixed-size dialog that can hold user interface controls. Every application needs at least one page to be functional!

A page is identified by a name that also needs to be unique for the application. You can have the same name for pages in different applications without problems.

You start the application by calling the application's run() method with the page you want to start with. All user interaction and rendering is handled by a Page object; let's assume we implemented our page class in a file called MyPage.java:

import brf.j2me.dynaworks.*;
import brf.j2me.dynaworks.ui.*;

public class MyPage extends Page {

   private Button exitButton;
	
   public MyPage (String name) {
      super (name);
      add (exitButton = new Button (" Exit > ", 123, 145));
   }

   public boolean handleEvent (ActionEvent e) {
      if (e.getSource() == exitButton) {
         exit (null);
         return true;
      }
      return false;
   }

   public void paint () {
      super.paint ();
      canvas.drawString ("MyPage",10,100);
   }
}

In this easy case there are three methods that need to be implemented/overridden:

Overriding the constructor that instanciates the user interface elements
You can add all of the available user interface element types to a page; DynaWorks even allows you to write your own user interface widgets! All you have to do is to call the add() of the Page with the user interface object as an argument. Because you will need the object reference to the control later in the event processing, you better store the object in a private attribute.

You can use the following predefined controls:

  • Button
  • CheckBox
  • ComboBox
  • RadioButton
  • ListBox
  • ScrollTextBox
  • Slider
  • TextBox
  • TextField
  • Tree

Don't forget to call the super(name) method at the beginning of the constructor!

A handleEvent() method to react to user interaction
The underlying DynaWorks framework 'converts' user interface interactions with controls on a page into ActionEvents that are passed into the handleEvent() method of that page.

To find out what type of event occured and which user control initiated the event you have two methods available:

Object source = (ActionEvent) e.getSource();
int code = (ActionEvent) e.getCode();
The source object refers directly to the control that you created in the constructor of the page so you can find out which user interface object initiated the event.

The code value helps you to find out which type of event occured. Not all events that occur on the kjava level are reflected to the handleEvent() method; most of the events are passed directly to the controls and are processed there.

The following table tells you what event types can show up for what class of user control (your custom widgets of course can send all event types):

Code Button Check
Box
Combo
Box
Radio
Button
Slider Text
Box
Scroll
Text
Box
Text
Field
Tree List
Box
VALUE_CHANGED5 -x--- --xx-
INVOKED6 x-x-- -----
FOCUS_CHANGED7 ----- --x--

Although this table may look rather sparse it supports all basic interactions with controls. That some 'usefull' events are not triggered (like a VALUE_CHANGE event from a Slider) is a kjava problem because there is no access to the internal state (value) of a Slider.

So you will probably end up like me: Writing your own widgets that send all events neccessary. In the following section you will find an example of a slider widget that triggers all these events.

A paint() method to render the page on the screen.
Note that user interface elements that are added to a page are painted thru the base class. So don't forget to call super.paint() at the beginning of your own paint() method.

You can use this method to draw additional graphic on the page; you don't have to care for your user controls!

Back to top of page ...

Page-based event handling

The are probably some more methods you want to override in your page class; especially the keyDown() event handler is interesting, if you want to process the PAGEUP, PAGEDOWN or any of the KEY_HARD<x>> or <???>ICON key events in your page class (or its controls!).

A sample implementation looks like this:

public ActionEvent sysKeyDown (int keyCode) {
   // we want to handle PAGEUP and PAGEDOWN
   if (keyCode == Spotlet.PAGEUP) {
      // process "page up" event
      return event;
   } else if (keyCode == Spotlet.PAGEDOWN) {
      // process "page up" event
      return event;
   }
   // super messaging.
   return super.sysKeyDown (keyCode);
}

Your keyDown() handler receives an integer argument representing the key, the user has entered or pressed; it can have on of the following values:

keyCode Meaning
PAGEUP11"PageUp" hard key pressed
PAGEDOWN12"PageDown" hard key pressed
KEY_HARD1516"Calendar" hard key pressed
KEY_HARD2517"Address" hard key pressed
KEY_HARD3518"ToDo" hard key pressed
KEY_HARD4519"Memo" hard key pressed
KEY_POWER520"Power" key pressed
CALCICON267Silkpad icon "Calculator" (top right)
MENUICON261Silkpad icon "Menu" (bottom left)
 264Silkpad icon "Home" (top left)
 266Silkpad icon "Find" (bottom right)
 257(PalmOS generated event; meaning unknown)
ASCII characterASCII valueof char entered

Back to top of page ...

Creating an application with more pages

The last thing you must know is how to work with more than one page in your application. So we will extend our little demo app from chaper 2 and include two more pages.

First we extend the source code in MyApp.java:

:
Application theApp = new Application ("MyApp");
theApp.addPage (new MyPage ("MyPage"));
theApp.addPage (new ViewPage ("ViewPage"));
theApp.addPage (new SearchPage ("SearchPage"));
:

As you can see we have added two more page objects to the application; a ViewPage and a SearchPage (don't care about the names - it's just an example).

The demo should show the following behaviour: If you start the application you enter an 'intro' page (MyPage) that has a text and three buttons: an exit button (which quits the app), a 'view' button and a 'search' button.

If you click the view button, you go to the page ViewPage which has an 'exit' button (that quits the application if clicked) and a 'search' button.

The search button in either case displays the "SearchPage" page with a 'back' button. If you click that button you return to the calling page!

How is that achieved? Let's have a (simplified) look at the page classes:

public class MyPage extends Page {

   private Button exitButton;
   private Button viewButton;
   private Button searchButton;
	
   public MyPage (String name) {
      super (name);
      add (viewButton = new Button (" View page ", 10, 10));
      add (searchButton = new Button (" Search page ", 10, 40));
      add (exitButton = new Button (" Exit > ", 123, 145));
   }

   public boolean handleEvent (ActionEvent e) {
      if (e.getSource() == exitButton) {
         exit (null);
         return true;
      }
      else if (e.getSource() == viewButton) {
         exit ("ViewPage");
         return true;
      }
      else if (e.getSource() == searchButton) {
         exit ("@SearchPage");
         return true;
      }
      return false;
   }

   public void paint () {
      super.paint ();
      canvas.drawString ("MyPage",10,100);
   }
}

public class ViewPage extends Page {

   private Button exitButton;
   private Button searchButton;
	
   public ViewPage (String name) {
      super (name);
      add (exitButton = new Button (" Exit > ", 123, 145));
      add (searchButton = new Button (" Search ", 10, 10));
   }

   public boolean handleEvent (ActionEvent e) {
      if (e.getSource() == exitButton) {
	     exit (null);
	     return true;
	  }
	  else if (e.getSource() == searchButton) {
	     exit ("@SearchPage");
	     return true;
	  }
	  return false;
   }

   public void paint () {
      super.paint ();
      canvas.drawString ("ViewPage",10,100);
   }
}

public class SearchPage extends Page {

   private Button backButton;

   public SearchPage (String name) {
      super (name);
      add (backButton = new Button (" Back ", 123, 145));
   }

   public boolean handleEvent (ActionEvent e) {
      if (e.getSource() == backButton) {
         exit (null);
         return true;
      }
      return false;
   }

   public void paint () {
      super.paint ();
      canvas.drawString ("SearchPage",10,100);
   }
}

Every page terminates by passing a String object back to the application object. The following rules apply to the String object:

  1. If the String is empty (or a "null" reference) than there is no request to load another page. The Application exits in case the call stack (see b) is empty; otherwise the call stack is poped and the retrieved page is displayed.
  2. If the String starts with a '@' character, the name following that character are treated as the name of a page. This page is then loaded and the name of the "calling" page is pushed onto a call stack. This allows nested pages.
  3. If the String is a simple name, the corresponding page is loaded and activated (displayed).

Back to top of page ...

What happens in a multi-page application?

The little example we have just discussed is a very simple one. In fact, it does nothing than flipping some pages and having buttons to be pressed.

Real-world applications process data - and so almost all pages in a multiple page application need to share the application data. The object store for these application-wide, cross-page data objects is explained in a later chapter.

Whenever a page is about to be activated, its perform() method is called. This is the place to initialize attributes, read from the application data objects and to setup user controls when neccessary.

A standard perform() method can look like this:

/////////////////////////////////////////////////////////////////
/**
 * page entry point.
 */
public String perform (boolean clear) {

   // get the application data from the dictionary.
   param = (MyAppData) getVariable ("Data");
   if (param != null) {
      // setup controls
      name.setText (param.getName());
      // do what you want...
      :
   }
  
   // perform super messaging.
   return super.perform (clear);
}

N.B.: It is very important to call the super.perform() method at the end of your initialization!

The page design even allows to start threads at the perform() method. The thread can even continue to run when you flip to another page! But keep two things in mind: A Palm is not a full-fledged desktop computer and can handle threads at much less speed, so keep the number of threads small and and thread execution time short. And make sure that all threads terminate (at latest) on application exit.

Back to top of page ...

Using the object store

The DynaPage framework includes the concept of data stores for data shared accross DynaPage applications or across pages within a single application.

The base class for all this is a DataDictionary that maps String keys to value objects. Any Java object can be used as a value.

All DynaPage applications have a global hashtable that maps String names against DataDictionary entries. All DynaPage applications share a global DataDictionary called System and have a private DataDictionary that has the same name as the application.

Back to top of page ...

Global data dictionary 'System'

The global DataDictionary System is a place where DynaPage application can share data between each other. The following code fragments show how to add and retreive entries in the System dictionary; remember that the code can only be included in methods of a class derived from Application.

  // add a variable to "System"
  Application.os.get("System").put("VarName", var);

  // get a variable from "System"
  Object var = Application.os.get("System").get("VarName");
 
The global dictionary remains available, until all DynaPage-based applications have been terminated and are removed from memory. The System dictionary is not persistent between application invocations; don't rely on something like a persistent data store in a data dictionary!

Back to top of page ...

Local data dictionary

The local data dictionary is stored globally under the application name (and can be accessed from other DynaPage applications, see example further below).

Any DynaPage application has simplified access methods for the local dictionary inside the classes Application and Page:

  // add a variable to the local dictionary.
  addVariable ("VarName", var);

  // get a variable from the local dictionary.
  Object var = getVariable (var);
So you can easily store data in the local dictionary during construction of the application object (like opening a database) and then later re-use this data for the specific processing on the application pages.

If you want to access the local dictionary of another application concurrently running on the Palm, all you have to know is the specific application name. You can and retreive entries in the dictionary with the following code lines; remember that the code can only be included in methods of a class derived from Application.

  // add a variable to "System"
  Application.os.get("AppName").put("VarName", var);

  // get a variable from "System"
  Object var = Application.os.get("AppName").get("VarName");
The local dictionary remains available, until all instances of the specific DynaWorks-based application have been terminated and are removed from memory. The local dictionary is not persistent between application invocations; don't rely on something like a persistent data store in a data dictionary!

Back to top of page ...


Copyright © 2000, Bernd R. Fix. All Rights Reserved.