|
ImageBundleDesign
ImageBundle Generator, Image Clipping, and Image Request Batching
Automatic Compile-Time Image BundlingBruce Johnson, Rajeev Dayal IntroductionMaking AJAX apps very fast requires minimizing the number of HTTP round-trips. Images, particularly icon-style images, are a major cause of multiple small requests. What's worse, such image requests typically happen at startup, which creates a sluggish startup experience for the end user, working against our first design axiom. That's no good, but fortunately GWT has the machinery to create a highly-optimized solution to this recurring problem. MotivationWhy optimize treatment of images? Use Case: ToolbarToolbars containing image (or "icon") buttons are commonly needed, and they often have these characteristics:
At startup, each toolbar icon typically causes an HTTP freshness check (i.e. "If-Modified-Since"), which has several disadvantages:
Goals
Non-Goals
SolutionWith a few tweaks to the existing Image class, the addition of a deferred binding generator and a ImageBundle tag interface, we can achieve all of the goals above in a way that actually simplifies life for GWT developers. ImageBundleGenerator and ImageBundleThe two key types are ImageBundleGenerator and ImageBundle. Defining an Image BundleThis is a similar design to the Constants interface. ImageBundle is an empty tag interface that can be extended to create custom image bundles. Derived interfaces contain zero or more methods, each of which must have
For example, to create an image bundle for a word processor toolbar, you could define an image bundle as follows: interface WordProcessorImageBundle extends ImageBundle {
/**
* Would match either file 'newFileIcon.gif' or 'newFileIcon.png' in the same package as this type. Note that other file extensions may also be recognized.
*/
AbstractImagePrototype newFileIcon();
/**
* Would bundle the file 'open-file-icon.gif' residing in the same package as this type.
* @gwt.resource open-file-icon.gif
*/
AbstractImagePrototype openFileIcon();
/**
* Would bundle the file 'savefile.gif' residing in the package 'org.example.icons'.
* @gwt.resource org/example/icons/savefile.gif
*/
AbstractImagePrototype saveFileIcon();
}Using an Image BundleCreate the image bundle object using GWT.create() as would for any other object subject to deferred binding. For example, WordProcessImageBundle wpib =
(WordProcessImageBundle)GWT.create(WordProcessImageBundle.class);
Toolbar tb = new Toolbar();
tb.addImageButton(wpib.newFileIcon());
tb.addImageButton(wpib.openFileIcon());
tb.addImageButton(wpib.saveFileIcon()); Configuring Your Module for Image BundlesTo use image bundling, your module needs to explicitly inherit ImageBundle. Behavior/Restrictions/CaveatsFor the ImageBundle-compatible type T specified in GWT.create(T.class), the following must be true:
Clipping Constructor for ImageTo make the new image bundling functionality work well with the existing Image class, a new "clipped" mode is introduced, allowing an image to refer to a sub-image within a larger image. There are a variety of formulations of this idea, but the one that should have the least impact is to
A clipped image might appear like this in the DOM: <img src="clear.cache.gif" style="background-image:md5.cache.ext; background-position:-Xpx -Ypx; width:Wpx; height:Hpx"> The other more obvious implementation technique requires you to encase the <img> in a <div>, then set "overflow:hidden", width and height on the <div>. However, this adds weight and makes the DOM structure for Image less predictable to client code. Some browsers (not naming any names, but...Internet Explorer) may require extra weirdness to support transparency. Caveats
Image Request BatchingTBD -- IE6 has a bug when the same image URL is requested multiple times from code that causes it to issue all the requests instead of realizing a single result image can be shared. We could solve this through code in Image or in the DOM. ImplicationsDesign Goals AchievedHow does this approach address the goals?
Interaction With LocalizationImage bundles are (in the current implementation) orthogonal to localization and do not use localization concepts directly. It is possible to localize image bundles using a locale-specific factory. Suppose that we have the following ImageBundle: public interface MyImageBundle extends ImageBundle {
/**
* The default icon if no locale-specific image is specified.
* @gwt.resource help_icon.gif
*/
AbstractImagePrototype helpIcon();
/**
* The default icon if no locale-specific image is specified.
* @gwt.resource compose_new_message_icon.gif
*/
AbstractImagePrototype composeNewMessageIcon();
}We can define French and English variations of each ImageBundle image by extending MyImageBundle for each locale: public interface MyImageBundle_en extends MyImageBundle {
// Note that we are not re-declaring helpIcon(), so this bundle
// uses the inherited metadata.
/**
* The English version of this icon.
* @gwt.resource compose_new_message_icon_en.gif
*/
AbstractImagePrototype composeNewMessageIcon();
}
public interface MyImageBundle_fr extends MyImageBundle {
/**
* The French version of this icon.
* @gwt.resource help_icon_fr.gif
*/
AbstractImagePrototype helpIcon();
/**
* The French version of this icon.
* @gwt.resource compose_new_message_icon_fr.gif
*/
AbstractImagePrototype composeNewMessageIcon();
}By extending Localizable, we can create a locale-sensitive factory to select the appropriate MyImageBundle-derived interface based on the user's locale. public interface MyImageBundleFactory extends Localizable {
MyImageBundle createImageBundle();
}
public class MyImageBundleFactory_en implements MyImageBundleFactory {
MyImageBundle createImageBundle() {
return (MyImageBundle) GWT.create(MyImageBundle_en.class);
}
}
public class MyImageBundleFactory_fr implements MyImageBundleFactory {
MyImageBundle createImageBundle() {
return (MyImageBundle) GWT.create(MyImageBundle_fr.class);
}
}The application code looks something like the following: // Create a locale-specific MyImageBundleFactory. MyImageBundleFactory myImageBundleFactory = (MyImageBundleFactory) GWT.create(MyImageBundleFactory.class); // This will return a locale-specific MyImageBundle, since we are using a locale-specific // factory to create it. MyImageBundle myImageBundle = myImageBundleFactory.createImageBundle(); // Get the image prototype for the icon we are interested in. AbstractImagePrototype helpIconProto = myImageBundle.helpIcon(); // Create an Image object from the prototype. somePanel.add(helpIcon.createImage()); |
Sign in to add a comment
Begin quote: public class MyImageBundleFactory?_en extends MyImageBundleFactory? {
} : End QuoteThis cannot be correct. A class cannot extend an interface. The class should implement the interface.
@b.q.mulder: thanks for catching that. updated.
It's not working for me as described above. Look here: http://code.google.com/p/google-web-toolkit/issues/detail?id=1666.
is this approach (<img src="clear.cache.gif" style="background-image:md5.cache.ext; background-position:-Xpx -Ypx; width:Wpx; height:Hpx">) used in version 1.4.60? because in IE DOM I can see filter: progid:DXImageTransform.Microsoft.AlphaImageLoader?(src='md5.cache.ext',sizingMethod='crop')
Why is it necessary to introduce separate factory classes for localization? I've not tried it yet but wouldn't it be sufficient to let the application's base ImageBundle? interfaces extend Localizable instead and then let GWT.create() choose the correct localized variant?
ImageBundle? for css, is there are any way to use ImageBundle? for style?
Yes, see ImmutableResourceBundle? in gwt-incubator: http://code.google.com/p/google-web-toolkit-incubator/wiki/ImmutableResourceBundle
GWT is really Great!! I'll soon develop WebSoftwareApp? on it! thanx to gooogle
Question : I have created a AbstractImagePrototype? image with a png image, how do i use it as a Background ???? i wish thre was a setBackground function
following code does not work: //myImages is an interface implementing ImageBundle?
Image menuShadow=myImages.menuShadowTile().createImage(); DOM.setStyleAttribute(simplePanel.getElement(), "background-image","url('"+menuShadow.getUrl()+"')");