?构建 UI

在本章中,我们将使用 Material Design 规范为 TodoList 应用程序构建现代化的 UI。我们将使用 Vaadin gwt-polymer-elements 库,它是 Polymer Paper Elements 集合的包装器。

主屏幕

  1. 创建应用程序的 主屏幕

    我们将创建一个由 java 文件 (Main.java) 及其可视化描述符 (Main.ui.xml) 组成的 UiBinder 屏幕。

    您可以通过复制以下代码段或使用 Eclipse GWT 插件来生成这些文件。

    • Main.java
        package org.gwtproject.tutorial.client;
    
        import com.google.gwt.core.client.GWT;
        import com.google.gwt.uibinder.client.UiBinder;
        import com.google.gwt.user.client.ui.Composite;
        import com.google.gwt.user.client.ui.HTMLPanel;
    
        public class Main extends Composite {
          interface MainUiBinder extends UiBinder<HTMLPanel, Main> {
          }
    
          private static MainUiBinder ourUiBinder = GWT.create(MainUiBinder.class);
    
          public Main() {
            initWidget(ourUiBinder.createAndBindUi(this));
          }
        }
    
    • Main.ui.xml
        <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
                 xmlns:g='urn:import:com.google.gwt.user.client.ui'>
    
          <g:HTMLPanel>
    
          </g:HTMLPanel>
        </ui:UiBinder>
    
  2. 添加 菜单项

    现在,我们可以通过添加菜单项来更新 Main.ui.xml 文件。

    <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
                 xmlns:g='urn:import:com.google.gwt.user.client.ui'>
    
        <g:HTMLPanel>
            <paper-icon-item ui:field="menuClearAll">
                <iron-icon icon="delete" item-icon=""/>
                <div>Clear All</div>
            </paper-icon-item>
            <paper-icon-item ui:field="menuClearDone">
                <iron-icon icon="clear" item-icon=""/>
                <div>Clear Done</div>
            </paper-icon-item>
            <paper-icon-item ui:field="menuSettings">
                <iron-icon icon="settings" item-icon=""/>
                <div>Settings</div>
            </paper-icon-item>
            <paper-icon-item ui:field="menuAbout">
                <iron-icon icon="help" item-icon=""/>
                <div>About</div>
            </paper-icon-item>
        </g:HTMLPanel>
    </ui:UiBinder>
    

    注意:访问原始的 polymer 元素 文档以了解和演示每个组件。

  3. 更新 入口点以使用我们的新屏幕。

    package org.gwtproject.tutorial.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.vaadin.polymer.Polymer;
    import com.vaadin.polymer.elemental.Function;
    import com.vaadin.polymer.iron.element.IronIconElement;
    import com.vaadin.polymer.paper.element.PaperIconItemElement;
    
    import java.util.Arrays;
    
    public class TodoList implements EntryPoint {
    
      public void onModuleLoad() {
        // We have to load icon sets before run application
        Polymer.importHref(Arrays.asList(
               PaperIconItemElement.SRC,
               IronIconElement.SRC), new Function() {
          public Object call(Object arg) {
            // The app is executed when all imports succeed.
            startApplication();
            return null;
          }
        });
      }
    
      private void startApplication() {
        RootPanel.get().add(new Main());
      }
    }
    

    提示:为了方便使用 webcomponents,gwt-polymer-elements 保持了几个静态常量:Element.TAGElement.SRC,以避免分别记忆标签名称和导入路径。

  4. 运行应用程序。

    在浏览器中重新加载页面,您应该会看到四个菜单项。您可能会注意到图标丢失了。我们将在下一步中修复这个问题。

图标和效果

  1. 导入 图标集合

    Polymer 带有几个图标集合。在可以使用集合之前,必须导入它。在本例中,我们将使用 Iron 集。在下面的代码中,我们使用 Polymer.importHref 实用程序方法,并在集合加载完成后运行应用程序。

    package org.gwtproject.tutorial.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.vaadin.polymer.Polymer;
    import com.vaadin.polymer.elemental.Function;
    
    public class TodoList implements EntryPoint {
    
      public void onModuleLoad() {
        // We have to load icon sets before run application
        Polymer.importHref(Arrays.asList(
              "iron-icons/iron-icons.html",
               PaperIconItemElement.SRC,
               IronIconElement.SRC), new Function() {
          public Object call(Object arg) {
            // The app is executed when all imports succeed.
            startApplication();
            return null;
          }
        });
      }
    
      private void startApplication() {
        RootPanel.get().add(new Main());
      }
    }
    
  2. 重新加载应用程序

    您现在应该在浏览器中看到所有图标。

  3. 添加 Ripple 效果

    与 UI 元素交互时的反馈通常被认为是积极的。如果您愿意,可以详细了解 Material Design 关于 响应式交互 的理念。

    • Main.ui.xml 文件中的每个项目中添加 <paper-ripple/>
    • 我们需要向项目添加一些 CSS 样式属性,以便将涟漪效果限制在项目区域内。
    <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
                 xmlns:g='urn:import:com.google.gwt.user.client.ui'>
    
        <g:HTMLPanel>
            <style>
                paper-icon-item {
                    position: relative;
                    overflow: hidden;
                }
            </style>
            <paper-icon-item ui:field="menuClearAll">
                <iron-icon icon="delete" item-icon=""/>
                <div>Clear All</div>
                <paper-ripple/>
            </paper-icon-item>
            <paper-icon-item ui:field="menuClearDone">
                <iron-icon icon="clear" item-icon=""/>
                <div>Clear Done</div>
                <paper-ripple/>
            </paper-icon-item>
            <paper-icon-item ui:field="menuSettings">
                <iron-icon icon="settings" item-icon=""/>
                <div>Settings</div>
                <paper-ripple/>
            </paper-icon-item>
            <paper-icon-item ui:field="menuAbout">
                <iron-icon icon="help" item-icon=""/>
                <div>About</div>
                <paper-ripple/>
            </paper-icon-item>
        </g:HTMLPanel>
    </ui:UiBinder>
    
  4. 不要忘记在 TodoList.java 中导入涟漪效果。

    package org.gwtproject.tutorial.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.vaadin.polymer.Polymer;
    import com.vaadin.polymer.elemental.Function;
    
    public class TodoList implements EntryPoint {
    
      public void onModuleLoad() {
        // We have to load icon sets before run application
        Polymer.importHref(Arrays.asList(
              "iron-icons/iron-icons.html",
               PaperIconItemElement.SRC,
               IronIconElement.SRC,
               PaperRippleElement.SRC), new Function() {
          public Object call(Object arg) {
            // The app is executed when all imports succeed.
            startApplication();
            return null;
          }
        });
      }
    
      private void startApplication() {
        RootPanel.get().add(new Main());
      }
    }
    
  5. 重新加载应用程序以查看涟漪效果。

    比较添加 PaperRipple 效果之前和之后点击反应。

响应式布局

  1. 使用 Paper Draw Panel 布局应用程序。

    Paper 元素集合包括一个响应式抽屉面板。它是一个布局组件,可用于现代应用程序以确保它们在台式机和移动设备上都能正常运行。有关更多信息,请查看 paper-drawer-panel 演示

    <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
                 xmlns:g='urn:import:com.google.gwt.user.client.ui'>
    
        <g:HTMLPanel>
            <style>
                paper-icon-item {
                    position: relative;
                    overflow: hidden;
                }
            </style>
            <paper-drawer-panel ui:field="drawerPanel">
                <div drawer="">
                    <paper-header-panel mode="seamed">
                        <paper-toolbar/>
                        <paper-icon-item ui:field="menuClearAll">
                            <iron-icon icon="delete" item-icon=""/>
                            <div>Clear All</div>
                            <paper-ripple/>
                        </paper-icon-item>
                        <paper-icon-item ui:field="menuClearDone">
                            <iron-icon icon="clear" item-icon=""/>
                            <div>Clear Done</div>
                            <paper-ripple/>
                        </paper-icon-item>
                        <paper-icon-item ui:field="menuSettings">
                            <iron-icon icon="settings" item-icon=""/>
                            <div>Settings</div>
                            <paper-ripple/>
                        </paper-icon-item>
                        <paper-icon-item ui:field="menuAbout">
                            <iron-icon icon="help" item-icon=""/>
                            <div>About</div>
                            <paper-ripple/>
                        </paper-icon-item>
                    </paper-header-panel>
                </div>
                <div main="">
                    <paper-header-panel mode="seamed">
                        <paper-toolbar>
                            <paper-icon-button ui:field="menu" icon="more-vert" paper-drawer-toggle=""/>
                            <span>Todo List</span>
                        </paper-toolbar>
                    </paper-header-panel>
                </div>
            </paper-drawer-panel>
        </g:HTMLPanel>
    </ui:UiBinder>
    
  2. 添加 内容面板

    • 添加一个用于待办事项的容器。
        <g:HTMLPanel>
           ...
            <div main="">
                <paper-header-panel mode="seamed">
                    <paper-toolbar>
                        <paper-icon-button ui:field="menu" icon="more-vert" paper-drawer-toggle=""/>
                        <span>Todo List</span>
                    </paper-toolbar>
    
                    <div ui:field="content" class="content vertical center-justified layout"/>
    
                </paper-header-panel>
            </div>
           ...
        </g:HTMLPanel>
    
  3. TodoList.java 中添加必要的 HTML 导入 PaperDrawerPanelElement.SRC, PaperHeaderPanelElement.SRC, PaperToolbarElement.SRC

  4. 重新加载应用程序

    应用程序现在应该具有现代外观并具有响应性。如果调整浏览器窗口的大小使其宽度低于 640 像素,抽屉面板应该隐藏菜单。

?样式化应用程序

Web 组件使用 Shadow DOM 样式 规则来提供元素的范围样式。此外,Polymer 提供了 Shady DOM 来处理未实现本地阴影的浏览器。因此,Polymer 监视和解析每个 <style> 块,并在运行时重写规则。

GSS 处理器以及 GWT 加载 css 资源的方式是异步的,这使得在 <ui:style> 块中使用 shadowpolymer 选择器和属性成为不可能,因为首先 GSS 会抱怨某些语法,其次 Polymer 会忽略 CssResource 注入的块。因此,您必须在 UiBinder 文件中使用正常的 <style>

我们有两个选择

  1. 将 CSS 包含在您的主机页面(HTML 文件)中。
  2. 在您的 UiBinder 文件中使用正常的 <style> 标签。

我们将使用第二种方法,但请注意,它将在每次创建新小部件时在 DOM 树中包含一个新的 <style> 元素。在我们的例子中,我们在应用程序中只有一个 Main.ui.xml 实例,所以这不是问题。

  • 更改工具栏的颜色,设置标题文本和内容面板的样式

        <g:HTMLPanel>
            <style>
              .toolbar {
                background: #4285f4 !important;
               }
              .header {
                font-size: 200%;
                margin-left: 50px;
                background: #4285f4 !important;
               }
              .content {
                padding: 15px;
               }
               ...
            </style>
            ...
            <div main="">
                <paper-header-panel mode="seamed">
                    <paper-toolbar class="toolbar">
                        <paper-icon-button ui:field="menu" icon="more-vert" paper-drawer-toggle=""/>
                        <span class="header">Todo List</span>
                    </paper-toolbar>
                    <div ui:field="content" class="content vertical center-justified layout"/>
                </paper-header-panel>
            </div>
           ...
        </g:HTMLPanel>
    

注意verticalcenter-justifiedlayout 类由 Polymer 提供。

  • 添加并设置 浮动操作按钮 的样式

    Material Design 应用程序使用浮动按钮作为主要操作的特征。在 Paper 元素集合中,此按钮称为 paper-fab

    <g:HTMLPanel>
      <style is='custom-style'>
        ...
        .add {
            position: absolute;
            bottom: 20px;
            right: 20px;
            --paper-fab-background: var(--paper-red-500);
        }
      </style>
      <div main="">
        ...
        <paper-fab ui:field="addButton" icon="add" title="add" class="add"/>
      </div>
    </g:HTMLPanel>
    

提示:为了使 Polymer 处理您的样式块,请添加 is="custom-style" 属性。

提示:查看 paper-fab 文档以了解可用的自定义样式属性和混合。

摘要

最后,您的 Main.ui.xml 文件应如下所示

<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
             xmlns:g='urn:import:com.google.gwt.user.client.ui'>

    <g:HTMLPanel>
        <style is="custom-style">
            paper-icon-item {
                position: relative;
                overflow: hidden;
            }
            .toolbar {
                background: #4285f4 !important;
            }
            .header {
                font-size: 200%;
                margin-left: 50px;
            }
            .content {
                padding: 15px;
            }
            .add {
                position: absolute;
                bottom: 20px;
                right: 20px;
                --paper-fab-background: var(--paper-red-500);
            }
        </style>
        <paper-drawer-panel ui:field="drawerPanel">
            <div drawer="">
                <paper-header-panel mode="seamed">
                    <paper-toolbar class="toolbar"/>
                    <paper-icon-item ui:field="menuClearAll">
                        <iron-icon icon="delete" item-icon=""/>
                        <div>Clear All</div>
                        <paper-ripple/>
                    </paper-icon-item>
                    <paper-icon-item ui:field="menuClearDone">
                        <iron-icon icon="clear" item-icon=""/>
                        <div>Clear Done</div>
                        <paper-ripple/>
                    </paper-icon-item>
                    <paper-icon-item ui:field="menuSettings">
                        <iron-icon icon="settings" item-icon=""/>
                        <div>Settings</div>
                        <paper-ripple/>
                    </paper-icon-item>
                    <paper-icon-item ui:field="menuAbout">
                        <iron-icon icon="help" item-icon=""/>
                        <div>About</div>
                        <paper-ripple/>
                    </paper-icon-item>
                </paper-header-panel>
            </div>
            <div main="">
                <paper-header-panel mode="seamed">
                    <paper-toolbar class="toolbar">
                        <paper-icon-button ui:field="menu" icon="more-vert" paper-drawer-toggle=""/>
                        <span class="header">Todo List</span>
                    </paper-toolbar>
                    <div ui:field="content" class="content vertical center-justified layout"/>
                </paper-header-panel>
                <paper-fab ui:field="addButton" icon="add" title="add" class="add"/>
            </div>
        </paper-drawer-panel>
    </g:HTMLPanel>
</ui:UiBinder>

您的 TodoList.java 应如下所示

package org.gwtproject.tutorial.client;

    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.vaadin.polymer.Polymer;
    import com.vaadin.polymer.elemental.Function;
    import com.vaadin.polymer.iron.element.IronIconElement;
    import com.vaadin.polymer.paper.element.*;

    import java.util.Arrays;

    public class TodoList implements EntryPoint {

        public void onModuleLoad() {
            Polymer.importHref(Arrays.asList(
                    "iron-icons/iron-icons.html",
                    PaperIconItemElement.SRC,
                    PaperRippleElement.SRC,
                    IronIconElement.SRC,
                    PaperDrawerPanelElement.SRC,
                    PaperHeaderPanelElement.SRC,
                    PaperToolbarElement.SRC,
                    PaperFabElement.SRC
            ), new Function() {
                public Object call(Object arg) {
                    startApplication();
                    return null;
                }
            });
        }

        private void startApplication() {
            RootPanel.get().add(new Main());
        }
    }    

如果一切正常,重新加载后,您的应用程序应如下所示

下一步

  1. 我们学习了如何在 Elements 中使用 UiBinder
  2. 我们还知道如何将第三方 Web 组件添加到我们的 UI 中。
  3. 我们了解了如何导入 polymer 元素(如图标集合)并使用特殊组件(如效果)的机制。
  4. 我们可以使用 paper 面板并将它们与 GWT 中的传统 html 元素混合来处理响应式布局。
  5. 我们知道如何在 UiBinder XML 文件中使用 Polymer 解析器设置元素的样式。

步骤 3:向应用程序添加逻辑

免责声明