Part I of the GWT Tutorial explains the GWT UI design aspects and covers client side coding. It explains a menu driven multi tabbed front end and in doing so, it covers the use of essential GWT Panels, widgets,custom widgets, and a simple but effective way of handling GWT Events, and using EventBus architecture introduced in GWT ™2.x, and the benefits of @UIFactory to create the widgets. GWT Tutorial extensively uses UIBinder concept introduced in Google Web Toolkit 2.x for declarative definition the UI structure of the application.
FinsShell
In this section, we explorer two import concepts - GWT UiBinder and
Custom Widgets and use them to compose FinsShell, which holds other
layout components of our application The most effective way to create
new widgets is to extend the Composite class as explained in Creating
Custom
Widgets
section of GWT Developer’s Guide. FinsShell
is a custom widget which
extends Composite
and is the container for other parts of the layout.
FinsShell is added to RootPanel in onLoadModule()
method of Entry
Point instead of adding individual parts directly to the EntryPoint.
Out of various Panels provided by GWT, DockLayoutPanel
is most
suitable as we can dock Logo to the north, Status to the south, Menu bar
to the west and contents to the center. Dimensions of menu and content
panel becomes fixed in size if they placed directly in DockLayoutPanel.
To make them adjustable, menu is added to the west and contents to the
center of a SplitLayoutPanel
which is added to the center of the
DockLayoutPanel
. With this design, we have fixed top and bottom
sections and flexible middle section.
FinsShell and various content pages which goes into content area are
placed in a separate package and for this create a new packagein.fins.client.content
. Add FinsShell.java
and corresponding UIBinder file FinsShell.ui.xml
with following
contents to the newly created package in.fins.client.content
.
in.fins.client.content/FinsShell.ui.xml
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:DockLayoutPanel unit='PCT'>
<g:north size='5'>
<g:LayoutPanel>
<g:layer left="90%" width="10%" top="25%" height="75%">
<g:Label ui:field="logoLabel" text="Fins"
styleName="fins-Logo-Label" />
</g:layer>
</g:LayoutPanel>
</g:north>
<g:south size='3'>
<g:LayoutPanel>
<g:layer>
<g:Label>Status Placeholder</g:Label>
</g:layer>
</g:LayoutPanel>
</g:south>
<g:center>
<g:SplitLayoutPanel ui:field="split">
<g:west size="150">
<g:Label>Menu Placeholder</g:Label>
</g:west>
<g:center>
<g:Label>Content Placeholder</g:Label>
</g:center>
</g:SplitLayoutPanel>
</g:center>
</g:DockLayoutPanel>
</ui:UiBinder>
in.fins.client.content/FinsShell.java
package in.fins.client.content;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ResizeComposite;
import com.google.gwt.user.client.ui.Widget;
public class FinsShell extends ResizeComposite {
interface FinsShellBinder extends UiBinder<Widget, FinsShell> {
}
private static UiBinder<Widget, FinsShell> binder = GWT
.create(FinsShellBinder.class);
@UiField
Label logoLabel;
public FinsShell() {
initWidget(binder.createAndBindUi(this));
}
}
We have placed Logo and Status label in a LayoutPanel
as we want them
at certain positions within their docks. LayoutPanel allows its children
to be positioned using arbitrary constraints, almost like AbsolutePanel.
As far as possible, it is good to avoid fixed or
absolute constraints to position the widgets as such constraints are
specified in percentage which ensures that widgets are adjusted when
browser is resized.
SplitLayoutPanel
is added to the center of dock. HTML
widgets are
added to west and center of this split panel as placeholders, for
MenuBar and ContentPanel, which are replaced by actual widgets later.
We are using fins-Logo-Label
style to decorate the logo text. Create a
new folder in in.fins package and name it as public. It has to be a
folder as it is not possible to create a package named public. Add
Fins.css
to in/fins/public
directory with following contents.
in.fins.public/Fins.css
.fins-Logo-Label {
color: cornflowerblue;
font-size: 18px;
font-weight: bold;
}
To add css to module use stylesheet element in fins.gwt.xml which includes css through Automatic Resource Inclusion mechanism.
in.fins/fins.gwt.xml
<stylesheet src="Fins.css"/>
Add FinsShell to EntryPoint with the following modifications to Fins.java
in.fins.client.content/Fins.java
import in.fins.client.content.FinsShell;
import com.google.gwt.user.client.ui.RootLayoutPanel;
....
@Override
public void onModuleLoad() {
RootLayoutPanel rp = RootLayoutPanel.get();
FinsShell finsShell = new FinsShell();
rp.add(finsShell);
}
GWT offers two types of panel, Basic Panels and Layout Panels. Layout
Panels were introduced in GWT 2.0 for fast and predictable
application-level layout and ideally they preferred over Basic Panels.
In entry point class Fins
we have used RootLayoutPanel
instead of
RootPanel
, FinsShell
extends ResizeComposite
instead of
Composite
and Layout Panels like DockLayoutPanel
, LayoutPanel
are
used instead of basic panels like DockPanel
etc. All of these belongs
to Layout Panels group. But it is not possible to build complex UI
entirely with Layout Panels, we have to mix and match Basic Panels and
Layout panels. For now, you may ignore these subtle differences. To know
more about Panels refer Developer’s Guide - Layout Using
Panels.
In FinsShell constructor we have called initWidget
method of
Composite
. This method sets the widget to be wrapped by the Composite.
Let us explain this with following code snippet.
public class WrapperWidget extends Composite{
public WrapperWidget(){
initWidget(new Label("I will getting wrapped");
}
}
In the above example, the Composite WrapperWidget wraps the Label. With this, we can place WrapperWidget in any panel or even wrap it by other composites. Let us extend the example bit further.
public class WrapperWidget extends Composite{
public WrapperWidget(){
VerticalPanel vPanel = new VerticalPanel();
TextField field = new TextField();
Button button = new Button("Finish");
vPanel.add(field);
vPanel.add(button);
initWidget(vPanel);
}
}
TextField and Button are added to VerticalPanel
and initWidget call
wraps VerticalPanel
. Widgets may be added to VerticalPanel either
before or after calling initWidget, but initWidget
has to be called
before calling any other method of the Composite or using it with other
composite or panels.
Coming back to our code, can you guess which widget is getting wrapped
by FinsShell
. The right answer is DockLayoutPanel
and to get that we
have to understand the logic behind UIBinder.
UiBinder involves two files, template file and its owner class. Template file is a xml file with extension ui.xml where UI objects are laid out, and an Owner class owns a template file. UiBinder generates objects declared in the template file and binds them to fields in owner class. In the owner class, following code initializes the UiBinder.
interface FinsShellBinder extends UiBinder<Widget, FinsShell> {}
UiBinder<Widget, FinsShell> binder = GWT.create(FinsShellBinder.class);
UiBinder instances are factories that generate a UI structure and binds it to an owning Java class. The UiBinder<U, O> interface declares two parameter types:
U is the type of root element declared in the ui.xml file, returned by the createAndBindUi call
O is the owner type whose @UiFields are to be filled in.
Figure 3.2. Ui create and wrap
When Binder.createAndBindUi
is called, FinsShellBinder parses
FinsShell.ui.xml and creates an UI structure as shown in dotted area in
the figure. Binder injects the widgets to @UiFields defined in owner
class FinsShell. Then createAndBindUi
returns the root widget which is
DockLayoutPanel in this case.
With this, Fins is ready to display the broad contours of the application.