我们有一个Web应用程序,每个主要客户端都需要不同的主题。原始开发人员通过查看javascript中的URL并添加样式表来覆盖默认主题来完成此操作。
此问题的一个问题是网站默认查看几秒钟,然后突然切换到正确的主题。另一个原因是它似乎浪费了大量的带宽/时间。
我目前的想法是使用我们的默认外观创建一个“默认”ClientBundle扩展该界面并使用@ImageResouce等各种注释覆盖每个条目(根据需要)和客户端的图像并指向不同的位置。
有没有人有这方面的经验?我认为的一个问题是无法使用uibinder样式标记,因为它们静态地指向特定的资源包。
有什么想法吗?
答案 0 :(得分:17)
覆盖捆绑
是的,你可以。
我已经使用ClientBundles完成覆盖,并且工作正常。你必须要做的一件事就是继承属性的类型。例如:
BigBundle {
Nestedundle otherBundle();
ImageResource otherImage();
Styles css();
}
然后你必须以这种方式继承:
OtherBigBundle extends BigBundle {
OtherNestedBundle otherBundle(); // if you want to change it
ImageResource otherImage(); // of you want to change it
OtherStyles css(); // of you want to change it
}
和OtherNestedBundle extends NestedBundle
和OtherStyles extends Styles
至少使用css:如果声明属性不使用子接口,则它们将为相同的CSS类名生成样式,并且所有样式都将混合使用。因此,使用子接口声明重写样式:)
灵活的UIBinders
如果使用UiField(provided=true)
注释,可以从包外部进行设置。这样你首先设置bundle然后调用uibindler。它将使用资源字段,假设它已经创建。
延期绑定
您可以使用GWT.runAsync加载正确的捆绑包。
一些例子
ui.xml
<ui:with field='res' type='your.package.TheBundle'/>
相应的班级
@UiField(provided=true) TheBundle bundle;
private void createTheThing() {
this.bundle = factory.createBundle();
MyUiBindler binder = GWT.create(MyUiBindler.class);
this.panel = binder.createAndBindUi(this);
...
}
一些捆绑接口
interface TheBundle extends ClientBundle {
@ImageResource("default.png")
ImageResource image1();
@Source("default.css")
TheCss css();
}
interface Theme1Bundle extends TheBundle {
@ImageResource("one.png")
ImageResource image1(); // type: imageresource is ok
@Source("one.css")
OneCss css(); // type: OneCss => use other compiled css class-names
interface OneCss extends TheCss { // inner-interface, just for fun
// don't need to declare each String method
}
}
如果你没有覆盖某些东西,那就没关系
捆绑工厂的选项
1)完全
if (...) {
return GWT.create(TheBundle.class);
} else if (...) {
return GWT.create(Theme1Bundle.class);
}
2)runAsync(只需加载所需的部分......但是在执行初始部分之后)
if (...) {
GWT.runAsync(new RunAsyncCallback() {
public void onSuccess() {
return GWT.create(TheBundle.class);
}
// please program the onFailure method
});
} else if (...) {
GWT.runAsync(new RunAsyncCallback() {
public void onSuccess() {
return GWT.create(Theme1Bundle.class);
}
// please program the onFailure method
});
}
3)使用延迟绑定和生成器在编译时自动生成工厂,基于带注释的包,如@ThemeBundle("one")
这个例子来自现实世界。我使用DynamicEntryPointWidgetFactory(简称DEPWidgetFactory)来基于标识符字符串创建窗口小部件。每个小部件都是一个应用程序屏幕,每个主菜单都包含它必须创建的小部件名称。
在您的情况下,id将是要创建的主题。
重要提示:如果您使用runAsync,则无法在创建UI之前创建资源包,就像之前的示例代码一样。你必须要求主题,当它准备就绪时(在回调中)将它传递给你的widget构造函数,你的widget可以将它分配给它的字段。
工厂界面:
public interface DynamicEntryPointWidgetFactory
{
public void buildWidget(String widgetName, AsyncCallback<Widget> callback);
}
要生成的小部件的注释:
@Target(ElementType.TYPE)
public @interface EntryPointWidget
{
/**
* The name wich will be used to identify this widget.
*/
String value();
}
模块配置:
它说:将使用此类生成Factory的实现(另一个选项是使用replace-with,但在我们的示例中,我们没有为每个区域设置或浏览器预定义选项,但更具动态性)
<generate-with class="com.dia.nexdia.services.gwt.rebind.entrypoint.DynamicEntryPointFactoryGenerator">
<when-type-assignable class="com.dia.nexdia.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactory" />
</generate-with>
发电机:
public class DynamicEntryPointFactoryGenerator extends Generator {
@Override
public String generate(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
PrintWriter pw = context.tryCreate(logger,
"x.services.gwt.client.entrypoint",
"DynamicEntryPointWidgetFactoryImpl");
if (pw != null) {
// write package, imports, whatever
pw.append("package x.services.gwt.client.entrypoint;");
pw.append("import x.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactory;");
pw.append("import com.google.gwt.core.client.GWT;");
pw.append("import com.google.gwt.core.client.RunAsyncCallback;");
pw.append("import com.google.gwt.user.client.rpc.AsyncCallback;");
pw.append("import com.google.gwt.user.client.ui.Widget;");
// the class
pw.append("public class DynamicEntryPointWidgetFactoryImpl implements DynamicEntryPointWidgetFactory {");
// buildWidget method
pw.append(" public void buildWidget(String widgetName, final AsyncCallback<Widget> callback) {");
// iterates over all the classes to find those with EntryPointWidget annotation
TypeOracle oracle = context.getTypeOracle();
JPackage[] packages = oracle.getPackages();
for (JPackage pack : packages)
{
JClassType[] classes = pack.getTypes();
for (JClassType classtype : classes)
{
EntryPointWidget annotation = classtype.getAnnotation(EntryPointWidget.class);
if (annotation != null)
{
String fullName = classtype.getQualifiedSourceName();
logger.log(TreeLogger.INFO, "Entry-point widget found: " + fullName);
pw.append("if (\"" + annotation.value() + "\".equals(widgetName)) {");
pw.append(" GWT.runAsync(" + fullName + ".class, new RunAsyncCallback() {");
pw.append(" public void onFailure(Throwable t) {");
pw.append(" callback.onFailure(t);");
pw.append(" }");
pw.append(" public void onSuccess() {");
pw.append(" callback.onSuccess(new " + fullName + "());");
pw.append(" }");
pw.append(" });");
pw.append(" return;");
pw.append("}");
}
}
}
pw.append("callback.onFailure(new IllegalArgumentException(\"Widget '\" + widgetName + \"' not recognized.\"));");
pw.append(" }");
pw.append("}");
context.commit(logger, pw);
}
// return the name of the generated class
return "x.services.gwt.client.entrypoint.DynamicEntryPointWidgetFactoryImpl";
}