客户端捆绑包
已部署的 GWT 应用程序中的资源可以大致分为三种类别:永远不缓存的资源(.nocache.js
)、永远缓存的资源(.cache.html
)以及其他所有资源(myapp.css
)。ClientBundle 接口将其他所有资源类别中的条目移到永远缓存的资源类别中。
- 概览
- DataResource
- TextResource 和 ExternalTextResource
- ImageResource
- GwtCreateResource
- CssResource
- CssResourceCookbook
概览
目标
- 不再需要担心您的应用程序是否获得了程序资源的正确内容。
- 减少由中间代理服务器引起的非确定性。
- 为程序资源启用更积极的缓存头。
- 通过在编译期间执行一致性检查,消除物理文件名与 Java 代码中的常量之间的不匹配。
- 在浏览器和大小合适的情况下,使用“data:” URL、JSON 捆绑包或其他将资源嵌入到已编译 JS 中的方法来消除不必要的往返。
- 为添加新的资源类型提供可扩展的设计。
- 确保多个
ClientBundle
资源函数引用相同内容不会造成任何损失。
非目标
- 提供文件系统抽象
示例
要使用 ClientBundle,请在您的 gwt.xml
文件中添加一个 inherits
标签
<inherits name="com.google.gwt.resources.Resources" />
如果您编写了此接口
public interface MyResources extends ClientBundle {
public static final MyResources INSTANCE = GWT.create(MyResources.class);
@Source("my.css")
public CssResource css();
@Source("config.xml")
public TextResource initialConfiguration();
@Source("manual.pdf")
public DataResource ownersManual();
}
然后您可以这样说
MyResources.INSTANCE.css().ensureInjected();
// Display the manual file in an iframe
new Frame(MyResources.INSTANCE.ownersManual().getSafeUri().asString());
I18N
ClientBundle
与 GWT 的 I18N 模块兼容。
假设您定义了一个资源
@Source("default.txt")
public TextResource defaultText();
对于 locale
延迟绑定属性的每个可能值,ClientBundle
生成器将以类似于 Java 的 ResourceBundle
的方式查找指定文件名的变体。
假设 locale
设置为 fr_FR
。生成器将按以下顺序查找文件
- default_fr_FR.txt
- default_fr.txt
- default.txt
这将对所有资源类型都能很好地工作,这将允许您提供其他资源的本地化版本,例如 ownersManual_en.pdf
与 ownersManual_fr.pdf
。
可插拔资源生成
每个 ResourcePrototype
的子类型都必须定义一个 @ResourceGeneratorType
注解,其值为扩展 ResourceGenerator
的具体 Java 类。ResourceGenerator
的实例负责累积(或捆绑)传入的资源数据,以及少量的代码生成以组装 ClientBundle
类的具体实现。ResourceGenerator
子类的实现者可以预期在 ClientBundle
接口中,对于给定类型的资源,只创建一个 ResourceGenerator
。
ResourceGenerator
上的方法按以下顺序调用
init
为ResourceGenerator
提供一个ResourceContext
prepare
为ResourceGenerator
预期要处理的每个JMethod
调用createFields
允许ResourceGenerator
在类级别添加代码createAssignment
为每个JMethod
调用。生成的代码应适合用作赋值表达式的右侧。finish
在所有赋值都应写入后调用。
ResourceGenerators
预计将使用 ResourceGeneratorUtil
类。
杠杆和旋钮
ClientBundle.enableInlining
是一个延迟绑定属性,可用于禁用在会支持将资源数据内联到已编译 JS 中的浏览器中使用“data:” URL。*ClientBundle.enableRenaming
是一个配置属性,它将禁用使用强命名缓存文件。
资源类型
这些资源类型是 ClientBundle
中定义的方法的有效返回类型
- ImageResource
- CssResource
- DataResource
- ExternalTextResource
- GwtCreateResource
- TextResource
- ClientBundle 的子类(用于嵌套捆绑包)
DataResource
DataResource 是最简单的资源类型,提供了一个 URL,通过该 URL 可以运行时检索文件的原始内容。提供的主要优化是根据文件的原始内容自动重命名文件,以便浏览器能够对生成的 URL 进行强缓存。非常小的文件可能会被转换为“data:” URL(在支持它们的浏览器中)。
interface Resources extends ClientBundle {
Resources INSTANCE = GWT.create(Resources.class);
@Source("mycursor.cur")
DataResource customCursor();
}
// Elsewhere
someDiv.getStyle().setProperty("cursor", "url(" + Resources.INSTANCE.customCursor().getUrl() + ")");
不适合内联到已编译的 JavaScript 中作为“data:” URL 的资源将被发射到编译输出中,并使用基于文件内容的强名称。例如,foo.pdf
将被赋予类似于 ABC1234.cache.pdf
的名称。Web 服务器应配置为使用公开可缓存的头和远期 Expires
头来提供与 *.cache.*
glob 匹配的所有文件。
TextResource 和 ExternalTextResource
相关的资源类型 TextResource 和 ExternalTextResource 提供对静态文本内容的访问。这两者之间的主要区别是前者将文本实习到已编译的 JavaScript 中,而后者将相关的文本资源捆绑到一个单独的文件中,该文件是异步访问的。
interface Resources extends ClientBundle {
Resources INSTANCE = GWT.create(Resources.class);
@Source("a.txt")
TextResource synchronous();
@Source("b.txt")
ExternalTextResource asynchronous();
}
// Using a TextResource
myTextArea.setInnerText(Resources.INSTANCE.synchronous().getText());
// Using an ExternalTextResource
Resources.INSTANCE.asynchronous().getText(new ResourceCallback<TextResource>() {
public void onError(ResourceException e) { ... }
public void onSuccess(TextResource r) {
myTextArea.setInnerText(r.getText());
}
});
ImageResource
本节介绍 ImageResource 如何调整图像数据的编译时处理,并在运行时提供对图像数据的有效访问。
目标
- 以最有效的方式提供运行时对图像数据的访问* 不要将
ImageResource
API 绑定到特定的小部件框架
非目标
- 提供通用的图像处理框架。
概览
- 定义一个包含一个或多个 ImageResource 访问器的 ClientBundle。对于每个访问器方法,请添加一个
@Source
注解,并使用要添加到程序中的新图像的路径。ClientBundle 生成器将您接口中定义的所有图像组合到一个优化后的图像中。
interface Resources extends ClientBundle {
@Source("logo.png")
ImageResource logo();
@Source("arrow.png")
@ImageOptions(flipRtl = true)
ImageResource pointer();
}
- 通过调用
GWT.create()
来实例化ClientBundle
。 - 实例化一个或多个 Image 小部件,或与 CssResource @sprite 指令一起使用。
例如,代码
Resources resources = GWT.create(Resources.class);
Image img = new Image(resources.logo());
导致 GWT 加载为 Resources 生成的合成图像,然后创建一个 Image,该 Image 是仅针对 logo 图像的正确子区域。
ImageOptions
@ImageOptions
注解可以应用于 ImageResource
访问器方法,以调整图像数据的编译时处理
flipRtl
是一个布尔值,当LocaleInfo.isRTL()
返回true
时,它会导致图像在其 Y 轴上镜像。repeatStyle
是一个枚举值,它与@sprite
指令结合使用,以指示图像打算被平铺。
支持的格式
ImageBundleBuilder
使用 Java ImageIO 框架读取图像数据。这包括对所有常见 Web 图像格式的支持。
ImageResource
支持动画 GIF 文件。虽然图像数据不会被合并到图像条中,但该资源仍然通过更大的 ClientBundle
框架以浏览器优化的方式提供服务。
从 ImageBundle 转换
将现有代码转换为使用 ImageResource
只需要进行最小的更改。
ImageBundle
变为ClientBundle
AbstractImagePrototype
变为ImageResource
AbstractImagePrototype.createImage()
变为new Image(imageResource)
- 可以通过调用
AbstractImagePrototype.create(imageResource)
来提供 API 包装器,以继续使用AbstractImagePrototype
上的其他方法。
GwtCreateResource
GwtCreateResource 是 ClientBundle
与任何其他(资源)类型(可默认实例化)之间的桥接类型。GwtCreateResource
的实例充当其他类型的工厂。
interface Resources extends ClientBundle {
Resources INSTANCE = GWT.create(Resources.class);
@ClassType(SomeClass.class)
GwtCreateResource<ReturnType> factory();
}
// Elsewhere
ReturnType obj = Resources.INSTANCE.factory().create();
虽然以上等同于
ReturnType obj = GWT.<ReturnType> create(SomeClass.class);
但它允许使用类不知道传递到 GWT.create()
的特定类字面量。只要该类型可默认实例化,就不需要为 SomeClass
指定特定的延迟绑定规则。
CssResource
本节介绍 CssResource 以及 CSS 的编译时处理。
另请参阅 CssResourceCookbook 和 StyleInjector。
目标
- 主要
- 与非 GWT 感知 CSS 解析器兼容(即任何扩展都应为有效的 CSS 语法)
- 这并不意味着,如果你只是在浏览器中显示样式表,该样式表就一定有意义
- 语法验证
- 压缩
- 利用 GWT 编译器
- 为不同的浏览器自动提供不同的 CSS
- 内容的静态评估
- 次要
- 基本 CSS 模块化
- 通过依赖注入 API 风格
- 只有在需要时,小部件才能注入其自身的 CSS
- 双向(Janus 风格?)
- CSS 图像条
- “改进 CSS”
- 常量
- 简单表达式
- 基本 CSS 模块化
- 第三级
- 运行时操作(StyleElement.setEnabled() 处理许多情况)
- 编译时类名检查(Java/CSS)
- 混淆
非目标
- 服务器端操作
- CssResource 中的所有功能都必须仅用编译时和运行时代码实现。任何功能都不能依赖于服务器端代码的运行时支持。
概述
- 编写 CSS 文件,无论是否使用 GWT 特定的扩展
- 如果使用了 GWT 特定的扩展,则定义 CssResource 的自定义子类型
- 在 ClientBundle 中声明一个返回 CssResource 或子类型的方法
- 使用
GWT.create()
生成捆绑包类型时,将创建评估为样式表内容的 Java 表达式- 除了 Java 表达式是字符串字面量的最简单情况外,通常情况下,CSS 文件无法生成到模块输出中
- 在运行时,调用
CssResource.ensureInjected()
以注入样式表的内容- 此方法可以安全地调用多次,因为后续调用将成为无操作
- 建议的模式是在各种小部件类型的静态初始化器中调用
ensureInjected()
功能
常量
@def small 1px;
@def black #000;
border: small solid black;
- 解析规则使得难以使用定界符来进行替换
- 重新定义内置尺寸允许用户编写纯 CSS 来草拟样式,然后对其进行微调。
- 建议用户使用大写名称,类似于静态 final 成员。
- 任何合法的属性值或表达式都可以在
@def
中使用 - 定义单个数值的
@def
规则可以通过类似于混淆类名的方式进行访问,方法是在 CssResource 类型上定义一个返回原始数值的访问器方法。
interface MyResources extends CssResource {
int small();
}
调用
small()
将返回1
。@def` 规则也可以作为字符串访问。你可以使用以下方法检索上面的两个定义:
interface MyResources extends CssResource {
String small();
String black();
}
- 调用
small()
返回“1px” - 调用
black()
返回“#000” - 生成器不允许你声明与类同名的
@def
规则,除非你使用@ClassName
注解来注解用于检索类的方法。
@def myIdent 10px;
.myIdent {
...
}
interface MyResources extends CssResource {
String myIdent();
@ClassName("myIdent")
String myIdentClass();
}
- 调用
myIdent()
返回 @def 值“10px” - 调用
myIdentClass()
返回 .myIdent 的混淆类名
运行时替换
@eval userBackground com.module.UserPreferences.getUserBackground();
div {
background: userBackground;
}
在注入样式表时,为评估静态方法提供运行时支持。如果我们允许对样式元素进行编程操作,则将来可以添加触发/动态更新。
如果用户定义的函数可以被编译器静态评估,那么特定 CssResource 的实现应该缩减为一个字符串字面量。
- 这允许轻松支持非结构化皮肤更改。
值函数
.myDiv {
offset-left: value('imageResource.getWidth', 'px');
}
value()
函数接受一系列用点分隔的标识符和可选的后缀。标识符被解释为零参数方法调用,使用传递到 GWT.create() 的接口作为根命名空间。通过只允许零参数方法,生成器不需要尝试执行类型检查。唯一需要的验证是确保方法序列存在。链中可能存在任意多个标识符。value()
函数可以与@def
结合使用
@def SPRITE_WIDTH value('imageResource.getWidth', 'px')
.selector {
width: SPRITE_WIDTH;
}
字面量函数
一些用户代理使用不符合 CSS 语法的属性值。literal()
函数的存在是为了允许使用这些非标准属性值。
div-with-literal {
top: literal("expression(document.compatMode==\"CSS1Compat\" ? documentElement.scrollTop : document.body.scrollTop \\ 2)");
}
请注意,有必要对反斜杠 (\
) 和双引号 ("
) 字符进行转义。
条件 CSS
/* Runtime evaluation in a static context */
@if (com.module.Foo.staticBooleanFunction()) {
... css rules ...
}
/* Compile-time evaluation */
@if <deferred-binding-property> <space-separated list of values> {
... css rules ...
}
@if user.agent safari gecko1_8 { ... }
@if locale en { ... }
/* Negation is supported */
@if !user.agent ie6 opera {
...
}
/* Chaining is also supported */
@if (true) {
} @elif (false) {
} @else {
}
- 这允许通过允许 CSS 中的结构性更改来实现更高级的皮肤/主题/浏览器怪癖处理。
- @if 块的内容可以是 CSS 样式表中任何顶层规则。
- @if 块可以任意嵌套。
- @if 块中使用 @def 或 @eval 的含义是什么?对于基于属性的 @if 语句,很容易实现这一点;对于基于表达式的 @if 语句,则必须生成非常复杂的运行时代码才能处理。可以使用块级作用域;但这似乎是一个可疑的用例。
- 如果第一种形式中的函数可以在编译器在某个排列中静态评估,那么就不会产生运行时成本。第二种形式永远不会产生运行时成本,因为它是在编译期间评估的。
图像精灵
@sprite .mySpriteClass {gwt-image: "imageAccessor"; other: property;} => generates =>
.mySpriteClass {
background-image: url(gen.png);
clip: ...;
width: 27px;
height: 42px;
other: property;
}
interface MyCssResource extends CssResource {
String mySpriteClass();
}
class MyResources extends ClientBundle {
@Source("my.css")
MyCssResource css();
@Source("some.png")
ImageResource imageAccessor();
@Source("some.png")
@ImageOptions(repeatStyle=RepeatStyle.Horizontal)
ImageResource repeatingImage();
}
- @sprite 对声明 CssResource 的 FooBundle 很敏感;在 @sprite 声明中命名的同级 ImageResource 方法将用于组合背景精灵。
- @sprite 条目将扩展为静态 CSS 规则,可能包含 data: url。
- 扩展对
ImageResource
访问器函数上定义的任何 RepeatStyle 值都很敏感。适当的repeat-x
或repeat-y
属性将添加到 @sprite 选择器。 - 可以为 @sprite 指定任何 CSS 选择器。
- 无法以这种格式支持 IE6,因为必须对 DOM 进行结构性更改才能实现“窗口”效果。一旦能够区分 ie6 和 ie7 的 user.agent,我们就可以重新考虑对 ie6 的支持。在当前实现中,ie6 代码将无法正确呈现,虽然这是一个纯粹的视觉问题。
对数据资源的引用
/* @url <constant name> <DataResource method name> */
@url myCursorUrl fancyCursorResource;
.myClass {
cursor: myCursorUrl, pointer;
}
interface MyResources extends ClientBundle {
@Source("myCursor.cur")
DataResource fancyCursorResource();
@Source("my.css")
CssResource css();
}
- 标识符将根据
DataResource.getUrl()
的返回值扩展为url('some_url')
。
RTL 支持
- CssResource 支持在编译时将 CSS 代码自动转换为从右到左的变体。
- 使用 RTL 变体由
com.google.gwt.i18n.client.LocaleInfo.getCurrentLocale().isRTL()
确定 - 应用的转换
left
和right
属性被翻转。- 任何具有
left
或right
值的属性都会被翻转:clear float text-align page-break-before page-break-after
background
/background-position
属性被翻转。用百分比表示的附件被镜像:40% 变成 60%margin padding border-color border-style
和border-width
四值属性被翻转:1px 2px 3px 4px
变成1px 4px 3px 2px
- 任何
xyz-right
或xzy-right-abc
属性都被翻转为xzy-left
或xzy-left-abc
body
选择器上的direction
属性将从ltr
翻转为rtl
;在任何其他选择器上,direction
属性保持不变- 当光标属性具有
resize
值时,它将被翻转:ne-resize
变成nw-resize
- 可以通过将 CSS 部分包含在
@noflip
块中来将其从自动翻转中排除
@noflip {
.selector {
left: 10;
}
}
- 使用基于像素的偏移量的
background
属性值,例如background-position: 4px 10px;
,将不会被自动转换。- 四值 CSS3
background-position
属性将被 RTL 支持自动翻转
- 四值 CSS3
background-position: left 4px top 10px;
* For CSS2 browsers, it will be necessary to use an `@sprite` rule:
@sprite .bgImage {
gwt-image: 'background-image';
position: absolute;
left: 4px;
top: 10px;
}
ImageResources
可以通过使用@ImageOptions
注解在 RTL 上下文中自动翻转
@Source("icon128.png")
@ImageOptions(flipRtl = true)
ImageResource logo();
选择器混淆
java:
class Resources {
MyCSSResource myCSSResource();
}
class MyCSSResource extends CSSResource {
Sprite mySpriteClass();
String someOtherClass();
String hookClass();
}
myWidget.addStyleName(resource.mySpriteClass());
css:
@sprite mySpriteClass mySpriteImage;
.someOtherClass {
/* ... */
}
.hookClass{} /* Empty and stripped, but left for future expansion */
- 该函数仅返回 CSS 类名,但会验证 CSS 类是否存在于样式表中。
- 通过接口访问类名可确保使用
CssResource
的代码中不会出现任何拼写错误。 - 为了混淆,我们将使用源 css 文件的 Adler32 校验和(以 36 进制表示)作为前缀(7 个字符)。开发者可以使用
CssResource.obfuscationPrefix
延迟绑定属性来覆盖它。 <set-configuration-property name="CssResource.obfuscationPrefix" value="empty" />
可用于最短的选择器名称,但这仅在 GWT 模块完全控制页面时才推荐。@external
at-rule 可用于有选择地禁用对命名选择器的混淆;有关更多详细信息,请参阅 外部和遗留范围。
优化
基本压缩
对 CSS 输入进行基本压缩会生成保留输入原始结构所需的最小字节数。通常,这意味着注释、不必要的空格和空规则将被删除。
.div {
/* This is the default background color */
background: blue;
}
.empty {}
将被转换为
.div{background:blue;}
选择器合并
具有相同选择器的规则可以合并在一起。
.div {prop: value;}
.div {foo: bar;}
变成
.div {prop:value;foo:bar;}
但是,必须保留 CSS 中属性的原始语义顺序。为了确保所有选择器合并都是正确的,我们施加了限制,即 **如果两个规则定义了共同属性,则任何规则都不能被提升到另一个规则之上**。我们认为 border
和 border-top
是等效的属性,但 padding-left
和 padding-right
不是等效的属性。
因此
.a {background: green;}
.b {border: thin solid blue;}
.a {border-top: thin solid red;}
无法合并,因为 CSS 类同时匹配 .a
和 .b
的元素将根据 CSS 规则的确切顺序进行不同的呈现。
在使用@if
语句时,最好使用操作延迟绑定属性的表单,因为 CSS 编译器可以在合并优化之前静态地评估这些规则。请考虑以下内容
.a {
background: red;
}
@if user.agent safari {
.a {
\-webkit-border-radius: 5px;
}
} @else {
.a {
background: url('picture_of_border.png');
}
}
在 Safari 排列中,规则变为.a{background:red;\-webkit-border-radius:5px;}
,而在其他排列中,background
属性被合并。
属性合并
具有相同属性的规则可以合并在一起。
.a {background: blue;}
.b {background: blue;}
可以转换成
.a,.b{background:blue;}
规则的提升遵循先前确立的规则,即不将规则提升到具有共同属性的其他规则之上。
杠杆和旋钮
- 配置属性
CssResource.style
可以设置为pretty
,这将禁用类名混淆,并将 CSS 内容格式化为漂亮打印。将其与ClientBundle.enableInlining
值为false
结合使用,以生成适合客户端编辑的 CSS 表达式。 - 配置属性
CssResource.mergeEnabled
可以设置为false
以禁用重新排序规则的修改。这应该被视为一种临时措施,直到合并逻辑得到充分验证。 - 为了允许客户端调整有效(即排列特定)样式规则,可以将
CssResource
.getText()的值存储到一个文本区域中。连接一些 UI 操作以将文本区域的内容传递到StyleInjector.setContents()
中,以覆盖原始的注入样式表。
选择器混淆细节
严格范围
在正常情况下,任何与字符串访问器函数不匹配的类选择器都是错误。可以通过在 CSS 访问器方法中添加@NotStrict
注释来禁用此行为。启用@NotStrict
行为仅推荐用于从外部 CSS 文件迁移到CssResource
的应用程序。
interface MyCssResource extends CssResource {
String foo();
}
interface Resources {
@Source("my.css")
@CssResource.NotStrict
MyCssResource css();
}
/* This is ok */
.foo {}
/* This would normally generate a compile error in strict mode */
.other {}
范围
混淆类名的范围由资源包中CssResource
访问器方法的返回类型定义。每个不同的返回类型将为字符串访问器方法返回一个完全独立的值集合。
interface A extends CssResource {
String foo();
}
interface B extends A {
String foo();
}
interface C extends A {
String foo();
}
interface D extends C {
// Intentionally not defining foo()
}
interface Resources {
A a();
A a2();
B b();
C c();
D d();
D d2();
a().foo() != b().foo() != c().foo() != d().foo()将为真。但a().foo() == a2().foo()且d().foo() == d2().foo()。
共享范围
在“有状态”CSS 类(如focused
或enabled
)的情况下,允许某些字符串访问器函数返回相同的值很方便,无论从访问器方法返回的CssResource
类型如何。
@Shared
interface FocusCss extends CssResource {
String focused();
String unfocused();
}
interface A extends FocusCss {
String widget();
}
interface B extends FocusCss {
String widget();
}
interface C extends B {
// Intentionally empty
}
interface Resources {
A a();
B b();
C c();
FocusCss f();
}
在此示例中,a().focused() == b().focused() == c().focused == f().focused()。但a().widget() != b().widget != c.widget(),如前例所示。
简而言之,如果不同的 CSS 类型需要共享混淆的类名,则附加它们的CssResource
子类型必须共享一个共同的超类型,该超类型定义了这些名称的访问器,并具有@Shared
注释。
导入范围
在涉及定义具有相同签名的多个方法的接口的多重继承时,Java 类型系统可能有点模棱两可,尽管存在许多情况需要引用多个不相关的CssResource
类型。考虑包含复选框的树的情况。
@ImportedWithPrefix("tree")
interface TreeCss extends CssResource {
String widget();
}
@ImportedWithPrefix("checkbox")
interface CbCss extends CssResource {
String widget();
}
interface MyCss extends CssResource {
String other();
}
interface Resources extends ClientBundle {
@Import({TreeCss.class, CbCss.class})
MyCss css();
}
/* Now we can write a descendant selector using the prefixes defined on the CssResource types */
.tree-widget .checkbox-widget {
color: red;
}
.other {
something: else;
}
组合“TreeCbCss”接口将是不够的,因为 TreeCss 接口和 CbCss 接口的使用者将从 widget 方法中接收相同的值。此外,如果没有使用某种类选择器前缀,在关联 CSS 文件中仅使用.widget
也将是不够的。前缀是在CssResource
类型上定义的(而不是在CssResource
访问器方法上定义的),为了在导入给定范围的所有 CSS 文件中保持一致性。导入多个具有相同前缀或简单名称的类是一个编译时错误。
共享范围的情况可以用导入范围完全处理,但是这种形式有点冗长,并且不相关范围之间的关系不如有状态选择器使用常见。
示例:StackPanel 在 StackPanel 内部
这是一个目前在 GWT 中无法正确设置样式的用例。
// Assume this interface is provided by the UI library
interface StackPanelCss extends CssResource {
String widget();
// and many more class names
}
// App code defines the following interfaces:
@ImportedWithPrefix("inner")
interface StackPanelInner extends StackPanelCss {
// Empty interface
}
interface StackPanelOuter extends StackPanelCss {
// Empty interface
}
interface Resources {
@Source("stackPanel.css")
StackPanelInner inner();
@Import(StackPanelInner.class)
@Source("stackPanel.css", "outer.css")
StackPanelOuter outer();
}
文件stackPanel.css
定义了任何给定 StackPanel 的基本结构
.widget .title {}
.widget .content {}
/* Other stuff to make a StackPanel work */
outer()
方法可以继续使用基本stackPanel.css
文件,因为StackPanelCss
中定义的访问器方法被映射到默认(无前缀)命名空间。内部 StackPanel 的样式成员也可以使用,但在inner
前缀中。以下是outer.css
可能包含的内容
.widget {color: red;}
.inner-widget {
color: blue;
font-size: smaller;
}
在许多情况下,新开发的 CSS 需要与外部或遗留 CSS 结合使用。@external
at-rule 可以用来抑制选择器混淆,同时仍然允许以编程方式访问选择器名称。
interface MyCssResource extends CssResource {
String obfuscated();
String legacySelectorA();
}
interface Resource extends ClientBundle {
@Source("my.css")
MyCssResource css();
}
@external legacySelectorA, legacySelectorB;
.obfuscated .legacySelectorA { .... }
.obfuscated .legacySelectorB { .... }
在上面的示例中,.obfuscated
类选择器将被混淆,obfuscated()
方法将返回替换的名称。这两个遗留选择器都不会被混淆,legacySelectorA()
方法将返回未混淆的值。此外,由于legacySelectorB
是在@external
声明中显式定义的,因此不可访问的类名不会触发错误。
自动生成CssResource
接口
GWT 分发版中包含一个实用程序,它将分析与CssResource
兼容的 CSS 文件,并为访问文件中使用的类名创建一个相应的 Java 接口。
java -cp gwt-dev.jar:gwt-user.jar com.google.gwt.resources.css.InterfaceGenerator \
-standalone -typeName some.package.MyCssResource -css input.css
生成的接口将被输出到System.out
。-standalone
选项将向输出添加必要的package
和import
语句,以便它可以作为构建过程的一部分使用。
CssResourceCookbook
本节包含示例,展示如何使用CssResource。
浏览器特定 css
.foo {
background: green;
}
@if user.agent ie6 {
/* Rendering fix */
.foo {
position: relative;
}
} @elif user.agent safari {
.foo {
\-webkit-border-radius: 4px;
}
} @else {
.foo {
font-size: x-large;
}
}
混淆的 CSS 类名
CssResource
将在运行时使用方法名作为 CSS 类名进行混淆。
interface MyCss extends CssResource {
String className();
}
interface MyResources extends ClientBundle {
@Source("my.css")
MyCss css();
}
当 CSS 编译时,所有带有.className
选择器的实例都将被替换为混淆的符号。要使用混淆的名称
MyResources resources = GWT.create(MyResources.class);
Label l = new Label("Some text");
l.addStyleName(resources.css().className());
如果您的 css 文件中包含不是合法 Java 标识符的类名,您可以在访问器方法上使用@ClassName
注释
interface MyCss extends CssResource {
@ClassName("some-other-name")
String someOtherName();
}
背景图像/精灵
CssResource
重用ImageResource
捆绑技术并将它们应用于 CSS 背景图像。这通常被称为“精灵”,CssResource
中使用了一个特殊的@sprite
规则。
interface MyResources extends ClientBundle {
@Source("image.png")
ImageResource image();
@Source("my.css");
CssResource css();
}
在my.css
中,精灵使用@sprite
关键字定义,后跟任意 CSS 选择器,规则块必须包含gwt-image
属性。gwt-image
属性应该命名ImageResource
访问器函数。
@sprite .myImage {
gwt-image: 'image';
}
与给定选择器匹配的元素将显示命名图像,并将其高度和宽度自动设置为图像的高度和宽度。
平铺图像
如果ImageResource
用@ImageOptions
注释修饰,则源图像可以沿 X 轴或 Y 轴平铺。这允许您使用 1 像素宽(或高)的图像来定义边框,同时仍然利用ImageResource
提供的图像捆绑优化。
interface MyResources extends ClientBundle {
@ImageOptions(repeatStyle = RepeatStyle.Horizontal)
@Source("image.png")
ImageResource image();
}
与@sprite
的选择器匹配的元素将仅设置其高度或宽度,具体取决于图像要重复的方向。
9 盒子
为了使 9 盒子的内容区域具有正确的尺寸,必须考虑边框图像的高度和宽度。与其在 CSS 文件中硬编码图像宽度,不如使用value()
CSS 函数从关联的ImageResource
中插入高度或宽度。
public interface Resources extends ClientBundle {
Resources INSTANCE = GWT.create(Resources.class);
@Source("bt.png")
@ImageOptions(repeatStyle = RepeatStyle.Horizontal)
ImageResource bottomBorder();
@Source("btl.png")
ImageResource bottomLeftBorder();
@Source("btr.png")
ImageResource bottomRightBorder();
@Source("StyleInjectorDemo.css")
CssResource css();
@Source("lr.png")
@ImageOptions(repeatStyle = RepeatStyle.Vertical)
ImageResource leftBorder();
@Source("rl.png")
@ImageOptions(repeatStyle = RepeatStyle.Vertical)
ImageResource rightBorder();
@Source("tb.png")
@ImageOptions(repeatStyle = RepeatStyle.Horizontal)
ImageResource topBorder();
@Source("tbl.png")
ImageResource topLeftBorder();
@Source("tbr.png")
ImageResource topRightBorder();
}
.contentArea {
padding: value('topBorder.getHeight', 'px') value('rightBorder.getWidth', 'px')
value('bottomBorder.getHeight', 'px') value('leftBorder.getWidth', 'px');
}
@sprite .contentAreaTopLeftBorder {
gwt-image: 'topLeftBorder';
position: absolute;
top:0;
left: 0;
}
@sprite .contentAreaTopBorder {
gwt-image: 'topBorder';
position: absolute;
top: 0;
left: value('topLeftBorder.getWidth', 'px');
right: value('topRightBorder.getWidth', 'px');
}
@sprite .contentAreaTopRightBorder {
gwt-image: 'topRightBorder';
position: absolute;
top:0;
right: 0;
}
@sprite .contentAreaBottomLeftBorder {
gwt-image: 'bottomLeftBorder';
position: absolute;
bottom: 0;
left: 0;
}
@sprite .contentAreaBottomBorder {
gwt-image: 'bottomBorder';
position: absolute;
bottom: 0;
left: value('bottomLeftBorder.getWidth', 'px');
right: value('bottomRightBorder.getWidth', 'px');
}
@sprite .contentAreaBottomRightBorder {
gwt-image: 'bottomRightBorder';
position: absolute;
bottom: 0;
right: 0;
}
@sprite .contentAreaLeftBorder {
gwt-image: 'leftBorder';
position: absolute;
top: 0;
left: 0;
height: 100%;
}
@sprite .contentAreaRightBorder {
gwt-image: 'rightBorder';
position: absolute;
top: 0;
right: 0;
height: 100%;
}
<div class="contentArea">
<div class="contentAreaTopLeftBorder"></div>
<div class="contentAreaTopBorder"></div>
<div class="contentAreaTopRightBorder"></div>
<div class="contentAreaBottomLeftBorder"></div>
<div class="contentAreaBottomBorder"></div>
<div class="contentAreaBottomRightBorder"></div>
<div class="contentAreaLeftBorder"></div>
<div class="contentAreaRightBorder"></div>
</div>