Using AnyJ's GUI Builder
Introduction
The GUI builder enables you to quickly create Swing GUIs by means of a generic layout scheme.
(Please read the respective section below as the layout scheme, though simple, is not self-explaining).
AnyJ's GUI builder supports the kind of tasks which profit from visual representation & manipulation:
- instantiating components and Beans,
- laying them out,
- setting properties,
- generating event handlers.
The most important facts for hasty readers:
- The Layout of a form is stored in a XML-format '.gml'. This file is not needed at runtime.
- It uses a special layoutmanager at runtime (ScalingLayout.java, source is part of the AnyJ distribution).
- The dynamic behaviour of the Layout is set by use of the 'Resize Wizard'. To avoid the need of a runtime-library some callbacks are made to the form's class by use of the reflection API.
('getImageForBuilder', 'internationalize', 'instanttiationHook').
- AnyJ generates pure Java sourcecode each time you save a Form.
- Each form consists of a subclass of JPanel (say 'MyForm.java', and a (completely generated) class to setup the GUI
('MyFormGUI.java').
Creating a sample workspace
We create a new workspace of type 'Swing Project'. This kind of workspace mounts two classes
(ScalingLayout.java, Applicationhelper.java) required by AnyJ's forms at runtime.
You can make any workspace a 'Swing'-type workspace by adding the runtime classes to the classpath.
Additionally we create and mount a directory to put our sources in and mark it to be parsed and compiled.
Now we select the directory (package) where to create the new form and choose 'File|New|Form' from the menu. We name the
new form 'ATestForm'. AnyJ opens a new, empty form. The .gml file contains a description of the form (xml). It is used
by AnyJ and not required at runtime.
The 'ATestForm.java' is your JPanel subclass containing the form later on. Eventhandlers are implemented here.
The 'ATestFormGUI.java' contains generated GUI-creation code. GUI-creation is separated from your main JPanel to avoid
cluttering your application code with tons of Component initialization etc. .
The 'images' subdirectory may contain images. The GUI-Builder looks into this folder to look up images used in the form.
! If you use images, these ressource must be reachable using the classpath. In this example 'C:\apps\anyj3.0\anyj\projects\swing_project\source'
has to be added to the classpath, in order to make the images accessable at runtime !
Another possibility is to copy the images to the output directory or change the implementation of the 'getImageForBuilder' method in your
form class.
Creating Components
AnyJ has a built-in support for Swing components. Some of the listed components refer to the same swing-class using different
preconfigurations. E.g. 'HtmlView', 'RichTextView' and 'PlainText' are JEditorPanes.
Since some components are rarely used without being put into a JScrollPane, common controls such as TreeView and ListBox
are automatically wrapped into a JScrollPane.
To create a component, choose one from the Components tab and create it by double-clicking or pressing the 'Create' button.
Note: If there is no property of a customizers type (e.g., a textfield cannot have an image),
the corresponding customizer will be disabled.
Setting properties
Properties can be set in several ways. The basic property types (such as string or integer) can be edited with the
'Properties' tab. More complex types (Font, Color, Images, Border .. ) can be set up using the 'Customizers' tab.
If there is no customizer for a property, you can set up the property programatically.
Accessing Components from within your program
For access to a component from within your program, specify a name for this component (see image below).
You have to save the edited form in order to prompt AnyJ to generate the corresponding variables into your source.
Do not remove the code markers (e.g., '//$$varinit'...//$$endvarinit').
They mark sections into which sourcecode is generated. Do not insert sourcecode between those markers.
Adding Event Handlers
Select the component in your form and double-click an event method (in the 'Events' section of the 'Events & Properties' tab)
of the GUI builder UI. AnyJ pops up the source and generates an action handler named '[id of component]_[methodname]'
(e.g., 'myButton_actionPerformed( ActionEvent e )'.
Note that you have to remove the event handler source code manually if you remove an event from a component.
If you decide to rename a component, it is necessary to rename the event handler methods.
Defining the Resizing Behaviour of a Form
We think the LayoutManagers concept is laudable but a rather long winded approach for visual GUI Building.
The best approach to make use of LayoutManagers is to manually write your GUI code.
AnyJ uses a simple yet powerful layout scheme, more suitable for use in GUI builders.
It works as follows:
- you enter the minimum size of your form,
- then resize the form to a larger size.
- AnyJ computes a transformation from the smaller layout to the bigger one.
All of AnyJs forms use this layout scheme, so you may wish to play around a bit to see what is possible.
GUI's created with AnyJ's layout scheme have several advantages:
- Pixel level position control, even in dynamically resizing forms
- Possibility to create stylish forms by overlaying components (without the requirement to give the form an absolute layout).
- Memory and performance savings, since it is not necessary to create subpanels to achieve specific component layouts.
Step by step:
- We want to layout the following form:
Open the 'Resize Wizard' using 'Gui Builder|Resize Wizard' from the menu.
- Layout your form to the minimum size and press 'Continue' to proceed.
As our example is already of minimum size, we need to do nothing but press the 'Continue' button.
- The second step of the resize wizard appears ('Recall' resets the previous maximum form size if present.):
We resize the headline because it should always cover the whole width of the form. (Note that the resizing behaviour of the headline is the same as the resizing behaviour of a menu component.)
We don't resize the upper textual field as it has the right size.
We enlarge the text area in the middle. At least we shift the controls below the text area to the bottom:
We press the 'Done' button of the resize wizard.
- Now we proudly watch our result using AnyJ's feature.
Note: First press 'Done' in the Wizard, only then press the 'Preview' button !
(I've just done it wrong myself....)
The resulting resizing behaviour looks like this:
Both width and height increased
Width increased only
Height increased only
What about Frames and Windows ?
AnyJ's GUI Builder always creates JPanels. You have to manually add the Panel to a Window, Frame or Dialog.
We think it is bad style to directly add controls directly to a Window (or Frame) since it is impossible to reuse your form
later on as a subcomponent. Additionally you may use a form either in a Dialog or Frame, you don't have to decide this at
time of form creation.
You may want to make use of the ApplicationHelper class provided by NetComputing GmbH to easily create Frames or Dialogs for
your forms.
Internationalization, Cross Platform Layout
Since the sizes of component differ slightly depending on the operating system and Look & Feel, it is
sometimes necessary to globally increase or decrease the size of
forms and controls. This is possible by setting the fontFakX
and fontFakY static variables in the class
de.netcomputing.runtime.ScalingLayout. E.g. setting
fontFakX to 1.1 will increase the minimum width of all
controls (e.g. labels, textfields) in your forms by 10%. This can
also be useful to fix internationalization issues.
If the checkbox shown above is enabled, AnyJ will create a hook
which enables you to replace each string of a form. It is necessary
to implement a method with the following signature in your forms
class:
public String internationalize( String toMap ) {
if ( toMap.equals( Cancel) )
return Abbrechen;
else
return toMap;
}
Note that the language mapping is demonstrated as an example in
the method above. You are free to make use of any
internationalization scheme (e.g. Ressource Bundles) in this method.
It is recommended to keep possible differences of String sizes
depending on the current language in mind when layouting a form.
Additionally it may be necessary to make use of the global size
factors depending on the current language as described above.
If the generate InstantiationHook checkbox is selected
you may implement the following method in your form in order to
change the Class which is actually instantiated at runtime or apply some default property
public Object instantiationHook ( Object toMap ) {
if ( toMap instanceof JButton )
return new MyButton();
else
return toMap;
}
Adding a Bean based on a .class file (recommended)
Adding a Java-Bean from a .class file is similar to adding Beans from a .jar file.
Press the 'Add' Button in the 'Components' tab of the GUI Builder and select the .class representing the Java Bean
Another way to add a .class file based Bean is to select a class in the file tree (or editor) and choose
'Gui Builder|Add as Bean to component palette' from the menu.
Adding a Bean from a .jar file
Select the 'Components' tab of the GUI builder, and press the 'Add' button.
AnyJ will open a dialog to select a .class or .jar file which should contain one or several beans.
AnyJ scans the jar for java beans, and if everything works well the java beans become available in the GUI
builder's 'Component' tab.
AnyJ will silently update your compile-classpath. If you choose to specify a different classpath for running (runtime-classpath), don't forget to update it.
Note: Some bean libraries consist of several jars which have to be imported in the correct version due to inter-jar dependencies. As an alternative, you may add these jars to the compile-classpath (Compile Options) before importing the jar.
Note: AnyJ silently adds the imported bean jars to your classpath settings.
A bean is instantiated just as normal components are: double-click on the selected component, or select it and press the
'Create' button.
Working with Container Components (Tabs/Groups)
Container components are components which are able to contain other components. AnyJ offers special support for
'TabbedPane' and 'Group' boxes. As long as a container shows black pivots when selected, it acts like a normal
non-container component (e.g., a button).
By couble clicking on it, a container component is 'activated'. This is indicated by the pivot elements turning red.
Your working area which covered the complete form before is now restricted to the content of the active container.
E.g., if you create or paste a button it will be added to the active container. This also works within nested containers,
as illustrated by the graph below:
You can move components from one container to another by Drag & Drop.
To add or remove 'Tabs' to a 'TabbedPane', use the Tabbed Pane Customizer.
Note: When you use the resize wizard to specify the resizing behaviour of your form, don't forget to layout all panes of a
TabbedPane.
To create SplitPane's, simply define two separate forms one for upper/left, one for lower/right and add them to a splitpane
programatically. The resize Algorithm used by the GUI Builder is for logical reasons not applicable to SplitPanes in-place.
Working with Images
If a selected component or bean has 'Image' or 'Icon' properties, the image customizer is enabled.
How are images found
Consider the following structure:
The MainFrame resides in the package 'propedit'.
Design Time:
The GUIBuilder's Image Customizer first looks in the images folder of the currently edited form.
If this is empty (there are no .gif's) it shows the content of [workspacedir]/images.
This check is done each time a component is selected in a form.
Runtime:
For each form two classes exist. The form class itself (usually a subclass of JPanel) and the generated GUI-creation class
('MyFormsNameGUI.java'). Each time a property of type 'Image' or 'Icon' is set, the GUI-creation class calls back the
form class to obtain the image. This is done if and only if the form class implements a method with the following signature:
public Image getImageForBuilder( String imageName, String packageAsPath ) {
// load via classpath from mypackage.subpack/images/imageName.gif
// resp. /images/imageName.gif (2cnd lookup)
return ApplicationHelper.Singleton().getImage(
getClass(), "images/"+imageName
);
}
By default we use our ApplicationHelper class to retrieve the image ('ApplicationHelper.Singleton().getImage(..)') which
implements the following image retrieval scheme:
a lookup is made via the classpath in the package of the Form (in this example in 'propedit/images/yourimage.gif').
If that fails, a lookup is made from the package root ('/images/yourimage.gif').
You have the choice to store ressources in the same package as the form's class (which is suited for reuse, e.g. Beans) or
store all ressources in the root package (well suited if the forms are unlikely to get reused and you won't deal with
image copying).
If you don't want to use the ApplicationHelper class or want to implement your own lookup scheme (e.g. Applet) simply
provide an alternative implementation of the 'getImageForBuilder' method.
Note: You can add the source root to your classpath, so the images are found at runtime and at design time. If you decide to
deploy your application, don't forget to add them to the .jar file.