【问题标题】:ClientBundle for multiple "themes"ClientBundle 用于多个“主题”
【发布时间】:2011-08-08 05:37:08
【问题描述】:

我们有一个 Web 应用程序,它需要为每个主要客户提供不同的主题。最初的开发人员通过查看 javascript 中的 URL 并添加样式表来覆盖默认主题来做到这一点。

这个问题的一个问题是该网站有几秒钟的默认外观,然后突然切换到正确的主题。另一个是它似乎浪费了很多带宽/时间。

我目前的想法是使用我们的默认外观创建一个“默认”ClientBundle 扩展该界面,并使用诸如 @ImageResouce 之类的各种注释并指向不同位置的客户端图像覆盖每个条目(根据需要) .

有人有这样做的经验吗?我预见到的一个问题是无法使用 uibinder 样式标签,因为它们静态指向特定的资源包。

有什么想法吗?

【问题讨论】:

    标签: gwt uibinder clientbundle


    【解决方案1】:

    覆盖的捆绑包

    是的,你可以。

    我已经使用 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 NestedBundleOtherStyles 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 之前创建资源包。您必须请求主题,并且当它准备好时(在回调中)将其传递给您的小部件构造函数,您的小部件可以将其分配给它的字段。

    工厂界面:

    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();
    }
    

    模块配置:

    它说:工厂的实现将使用这个类生成(另一个选项是使用替换,但在我们的例子中,我们没有为每个语言环境或浏览器预定义选项,而是更动态的) .

    <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";
        }
    

    【讨论】:

    • 选项 3 如何工作?你在 GWT xml 配置文件中注册了一个生成器?
    • 没错。您指定哪个是 BundleFactory 接口的“生成器类”。该生成器类必须实现一个输出代码的方法。该代码编写了“如果”。它可以使用一些 GWT 实用程序类来查找以某种方式标记的 Bundle 接口(如 @ThemeBundle 示例)。如果你愿意,明天我可以发布一些示例代码。
    • 使用传统的 css 交换不会改变加载的 css 文件立即重新加载整个 GUI?有没有办法达到同样的效果,或者我必须将每种样式“重新设置”到新的捆绑包中。
    • 我不确定你在问什么。但是,如果您建议使用一个或其他常用的 css 外部文件作为主题交换......是的,它会起作用。 Clientbundles 的优点是编译(它是:优化)以及在主题内包含图像和其他资源。但是如果你只需要修改一个外部的css文件就可以了。
    • 好点。 1)一种方法是使用相同的类名(在子资源接口中声明相同的 CssResource 类型)并“反注入”以前的 css。但我认为你不能。 2) 其他方法是简单地拥有一个可以调用的 setAllStyles(Bundle) 方法:) 或 3) 你可以刷新页面 ;-) 我认为这是所有解决方案中最简单的...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多