Google Code offered in: 中文 - English - Português - Pусский - Español - 日本語
As shown in the gallery, GWT includes a variety of pre-built Java widgets and panels that serve as cross-browser building blocks for your application. GWT also includes unique and powerful optimization facilities such as image bundles.
While it is possible to manipulate the browser's DOM directly using the DOM interface, it is far easier to use classes from the Widget hierarchy. You should rarely, if ever, need to access the DOM directly. Using widgets makes it much easier to quickly build interfaces that will work correctly on all browsers.
Widgets and panels work the same way on all browsers; by using them, you eliminate the need to write specialized code for each browser. But you are not limited to the set of widgets provided by the toolkit. There are a number of ways to create custom widgets yourself.
Panels, such as DockPanel, HorizontalPanel, and RootPanel, contain widgets and are used to define how they are laid out in the browser.
Visual styles are applied to widgets using Cascading Style Sheets (CSS). This section describes in detail how to use this feature.
The Button class, for example, publishes click events. The associated listener interface is ClickListener.
public void anonClickListenerExample() { Button b = new Button("Click Me"); b.addClickListener(new ClickListener() { public void onClick(Widget sender) { // handle the click event } }); }Using anonymous inner classes as in the above example can be inefficient for a large number of widgets, since it could result in the creation of many listener objects. Widgets supply their
this
pointer
as the sender
parameter when they invoke a listener
method, allowing a single listener to distinguish between multiple event
publishers. This makes better use of memory but requires slightly more
code, as shown in the following example:
public class ListenerExample extends Composite implements ClickListener { private FlowPanel fp = new FlowPanel(); private Button b1 = new Button("Button 1"); private Button b2 = new Button("Button 2"); public ListenerExample() { initWidget(fp); fp.add(b1); fp.add(b2); b1.addClickListener(this); b2.addClickListener(this); } public void onClick(Widget sender) { if (sender == b1) { // handle b1 being clicked } else if (sender == b2) { // handle b2 being clicked } } }Some event interfaces specify more than one event. If you are only interested in a subset of these events, subclass one of the event "adapters". Adapters are simply empty concrete implementations of a particular event interface, from which you can derive a listener class without having to implement every method.
public void adapterExample() { TextBox t = new TextBox(); t.addKeyboardListener(new KeyboardListenerAdapter() { public void onKeyPress(Widget sender, char keyCode, int modifiers) { // handle only this one event } }); }
gwt-Button
. In order to give all buttons a larger font,
you could put the following rule in your application's CSS file:
.gwt-Button { font-size: 150%; }
Some widgets have somewhat more complex styles associated with them. MenuBar, for example, has the following styles:
.gwt-MenuBar { the menu bar itself } .gwt-MenuBar .gwt-MenuItem { menu items } .gwt-MenuBar .gwt-MenuItem-selected { selected menu items }
In this example, there are two styles rules that apply to menu items. The
first applies to all menu items (both selected and unselected), while the
second (with the -selected suffix) applies only to selected menu items. A
selected menu item's style name will be set to
"gwt-MenuItem gwt-MenuItem-selected"
, specifying that
both style rules will be applied. The most common way of doing this is to
use
setStyleName
to set the base style name, then
addStyleName()
and
removeStyleName()
to add and remove the second style name.
Typically, stylesheets are placed in a package that is part of your module's public path. Then simply include a reference to the stylesheet in your host page, such as
<link rel="stylesheet" href="mystyles.css" type="text/css">
It is standard practice to document the relevant CSS style names for each widget class as part of its doc comment. For a simple example, see Button. For a more complex example, see MenuBar.
Sending out requests and freshness checks for many images will slow down your application. HTTP 1.1 requires browsers to limit the number of outgoing HTTP connections to two per domain/port. A multitude of image requests will tie up the browser's available connections, which blocks the application's RPC requests. RPC requests are the real work that the application needs to do.
To solve this problem, GWT introduces the concept of an image bundle. An image bundle is a composition of many images into a single image, along with an interface for accessing the individual images from within the composite. Users can define an image bundle that contains the images used by their application, and GWT will automatically create the composite image and provide an implementation of the interface for accessing each individual image. Instead of a round trip to the server for each image, only one round trip to the server for the composite image is needed.
Since the filename of the composite image is based on a hash of the file's contents, the filename will change only if the composite image is changed. This means that it is safe for clients to cache the composite image permanently, which avoids the unnecessary freshness checks for unchanged images. To make this work, the server configuration needs to specify that composite images never expire.
In addition to speeding up startup, image bundles prevent the 'bouncy' effect of image loading in browsers. While images are loading, browsers put a standard placeholder for each image in the UI. The placeholder is a standard size because the browser does not know what the size of an image is until it has been fully downloaded from the server. The result is a 'bouncy' effect, where images 'pop' into the UI once they are downloaded. With image bundles, the size of each individual image within the bundle is discovered when the bundle is created, so the size of the image can be explicitly set whenever images from a bundle are used in an application.
The derived interface can have zero or more methods, where each method
gwt.resource
metadata tag
which specifies the name of the image file in the module's classpath
Valid image file types are png
, gif
,
and jpg
. If the image name contains '/
'
characters, it is assumed to be the name of a resource on the
classpath, formatted as would be expected by
ClassLoader.getResource(String)
. Otherwise, the image
must be located in the same package as the user-defined image bundle.
If the gwt.resource
metadata tag is not specified, then
.png
,
.gif
, or .jpg
, and
In the event that there are multiple image files with different
extensions, the order of extension precedence is (1) png
,
(2) gif
, then (3) jpg
.
An image bundle for icons in a word processor application could be defined as follows:
public interface WordProcessorImageBundle extends ImageBundle { /** * Would match the file 'new_file_icon.png', 'new_file_icon.gif', or * 'new_file_icon.png' located in the same package as this type. */ public AbstractImagePrototype new_file_icon(); /** * Would match the file 'open_file_icon.gif' located in the same * package as this type. * * @gwt.resource open_file_icon.gif */ public AbstractImagePrototype openFileIcon(); /** * Would match the file 'savefile.gif' located in the package * 'com.mycompany.mygwtapp.icons', provided that this package is part * of the module's classpath. * * @gwt.resource com/mycompany/mygwtapp/icons/savefile.gif */ public AbstractImagePrototype saveFileIcon(); }
Methods in an image bundle return AbstractImagePrototype
objects (rather than Image
objects, as you might have
expected) because AbstractImagePrototype
objects provide
additional lightweight representations of an image. For example, the
AbstractImagePrototype.getHTML()
method provides an HTML fragment representing an image without having
to create an actual instance of the Image widget. In some
cases, it can be more efficient to manage images using these HTML
fragments.
Another use of AbstractImagePrototype
is to use
AbstractImagePrototype.applyTo(Image)
to transform an existing Image
into one that matches the
prototype without having to instantiate another Image
object. This can be useful if your application has an image that needs
to be swapped depending on some user-initiated action. Of course, if an
Image
is exactly what you need, the
AbstractImagePrototype.createImage()
method can be used to generate new Image
instances.
The following example shows how to use the image bundle that we just defined in your application:
public void useImageBundle() { WordProcessorImageBundle wpImageBundle = (WordProcessorImageBundle) GWT.create(WordProcessorImageBundle.class); HorizontalPanel tbPanel = new HorizontalPanel(); tbPanel.add(wpImageBundle.new_file_icon().createImage()); tbPanel.add(wpImageBundle.openFileIcon().createImage()); tbPanel.add(wpImageBundle.saveFileIcon().createImage()); }
The best way to explain this technique is with an example. Suppose that
we define the following ImageBundle
for use by a mail
application:
public interface MailImageBundle extends ImageBundle { /** * The default 'Compose New Message' icon if no locale-specific * image is specified. * * @gwt.resource compose_new_message_icon.gif */ public AbstractImagePrototype composeNewMessageIcon(); /** * The default 'Help' icon if no locale-specific image is specified. * Will match 'help_icon.png', 'help_icon.gif', or 'help_icon.jpg' in * the same package as this type. */ public AbstractImagePrototype help_icon(); }Suppose the application has to handle both English and French users. We define English and French variations of each image in
MailImageBundle
by creating locale-specific image
bundles that extend MailImageBundle
:
public interface MailImageBundle_en extends MailImageBundle { /** * The English version of the 'Compose New Message' icon. * Since we are not overriding the help_icon() method, this bundle * uses the inherited method from MailImageBundle. * * @gwt.resource compose_new_message_icon_en.gif */ public AbstractImagePrototype composeNewMessageIcon(); }
public interface MailImageBundle_fr extends MailImageBundle { /** * The French version of the 'Compose New Message' icon. * * @gwt.resource compose_new_message_icon_fr.gif */ public AbstractImagePrototype composeNewMessageIcon(); /** * The French version of the 'Help' icon. * * @gwt.resource help_icon_fr.gif */ public AbstractImagePrototype help_icon(); }The final step is to create a mechanism for choosing the correct image bundle based on the user's locale. By extending Localizable, we can create a locale-sensitive factory that will return new instances of
MailImageBundle
that match the factory's locale:
public interface MailImageBundleFactory extends Localizable { public MailImageBundle createImageBundle(); }
public class MailImageBundleFactory_en implements MailImageBundleFactory { public MailImageBundle createImageBundle() { return (MailImageBundle) GWT.create(MailImageBundle_en.class); } }
public class MailImageBundleFactory_fr implements MailImageBundleFactory { public MailImageBundle createImageBundle() { return (MailImageBundle) GWT.create(MailImageBundle_fr.class); } }The application code that utilizes a locale-sensitive image bundle would look something like this:
public void useLocalizedImageBundle() { // Create a locale-sensitive MailImageBundleFactory MailImageBundleFactory mailImageBundleFactory = (MailImageBundleFactory) GWT .create(MailImageBundleFactory.class); // This will return a locale-sensitive MailImageBundle, since we are using // a locale-sensitive factory to create it. MailImageBundle mailImageBundle = mailImageBundleFactory.createImageBundle(); // Get the image prototype for the icon that we are interested in. AbstractImagePrototype helpIconProto = mailImageBundle.help_icon(); // Create an Image object from the prototype and add it to a panel. HorizontalPanel panel = new HorizontalPanel(); panel.add(helpIconProto.createImage()); }