常见问题解答 - UI

  1. 布局
    1. 我在布局方面遇到了问题。如何查看我的面板在哪里?
    2. 如何创建一个应用程序,当浏览器窗口调整大小时,该应用程序会垂直填充页面?
    3. 为什么将小部件的高度设置为百分比不起作用?
    4. GWT 是否支持标准模式?
  2. 事件处理和性能
    1. 如何在表格中以良好的性能显示大量项目?
    2. 我该怎么做才能使图像和边框在第一次使用时看起来加载得更快?
    3. 为什么使用 1 像素高的背景图像时会出现性能低下?
    4. 随着应用程序的增长,事件处理程序似乎触发得越来越慢
    5. 如何有效地处理来自许多内部小部件的事件?

布局

我在布局方面遇到了问题。如何查看我的面板在哪里?

当您使用复杂的布局时,有时很难理解为什么您的小部件没有显示在您认为应该显示的位置。

例如,假设您要尝试创建一个包含两个按钮的布局,一个在浏览器的左侧,另一个在浏览器的右侧。以下是一些尝试执行此操作的代码

VerticalPanel vertPanel = new VerticalPanel();

    DockPanel dockPanel = new DockPanel();
    dockPanel.setWidth("100%");
    dockPanel.add(new Button("leftButton"), DockPanel.WEST);
    dockPanel.add(new Button("rightButton"), DockPanel.EAST);

    vertPanel.add(dockPanel);

    RootPanel.get().add(vertPanel);

以下是屏幕最终显示的样子

img

出了什么问题?我们将 DockPanel 设置为填充 100% 的空间,然后将每个按钮设置在屏幕的左侧或右侧。此时,探查并查看小部件的边界可能会有所帮助,因为它们可能不在您认为应该在的位置。

第一个技巧是使用 DOM 检查工具,例如 Firebug for the Firefox 浏览器。当您将鼠标悬停在 DOM 中的元素上时,它们将在浏览器窗口中突出显示。

您还可以使用的另一个技巧是修改您的 GWT 代码以在您的面板(或其他小部件)上打开边框。

VerticalPanel vertPanel = new VerticalPanel();

    DockPanel dockPanel = new DockPanel();
    dockPanel.setWidth("100%");
    dockPanel.add(new Button("leftButton"), DockPanel.WEST);
    dockPanel.add(new Button("rightButton"), DockPanel.EAST);
    dockPanel.setStylePrimaryName("dockPanel");
    dockPanel.setBorderWidth(5);

    vertPanel.add(dockPanel);
    vertPanel.setStylePrimaryName("vertPanel");
    vertPanel.setBorderWidth(5);

    RootPanel.get().add(vertPanel);

您还可以使用关联的 CSS 样式表来更改边框属性。在这种情况下,样式表会更改边框的颜色以使它们更容易区分

.dockPanel {
    border-color: orange;
}
.vertPanel {
    border-color: blue;
}

现在您可以清楚地看到每个面板的轮廓在哪里

img

在这种情况下,最外层面板也需要 100% 的宽度,因此我们更改代码,然后在浏览器窗口中点击“刷新”

vertPanel.setWidth("100%");

img

这更接近我们想要的结果,但仍然不正确。现在我们需要更改 DockPanel 实例中的单元格对齐方式

Button rightButton = new Button("rightButton");
    dockPanel.add(rightButton, DockPanel.EAST);
    dockPanel.setCellHorizontalAlignment(rightButton, HasHorizontalAlignment.ALIGN_RIGHT);

img

(完成后不要忘记关闭这些样式!)

如何创建一个应用程序,当浏览器窗口调整大小时,该应用程序会垂直填充页面?

从 GWT 2.0 开始,使用 布局面板 就可以轻松地创建一个填充浏览器的应用程序。诸如 DockLayoutPanelSplitLayoutPanel 之类的布局面板会自动调整大小以适应窗口的大小,当浏览器调整大小。

the Mail 示例应用程序 演示了 SplitLayoutPanel 的实际应用。

有关更多详细信息和代码片段,请参阅开发人员指南中的 布局面板

为什么将小部件的高度设置为百分比不起作用?

在某些情况下,Widget.setHeight() 和 Widget.setSize() 方法似乎不适用于以百分比表示的大小。GWT 小部件不会对这些值进行任何计算。这些方法会设置小部件的 CSS 高度和/或宽度属性,并依赖于封闭小部件的行为。

在标准模式下,如果元素(如 <div>)位于表格单元格 <td> 内,则无法将 <div> 高度和宽度属性设置为百分比。它们会被忽略。例如,下面 <div> 元素中的样式在标准模式下会被忽略

<td>
      <div style="height:100%;width:100%;>test</div>
    </td>

解决方法包括

  • 尝试将封闭的外层小部件更改为其他类型。例如,VerticalPanel 是用 HTML <table> 元素实现的,其行为可能与 AbsolutePanel 不同,AbsolutePanel 是用 HTML <div> 元素实现的。
  • 如果 HTML 主页中的 <DOCTYPE…> 声明设置为标准模式(如 HTML 4.01 Transitional),请尝试删除 <DOCTYPE …> 声明或将其更改为使浏览器渲染引擎进入怪异模式。

GWT 是否支持标准模式?

从 GWT 1.5 开始,是的。支持标准模式。为了向后兼容,怪异模式仍然受到支持。但是,对怪异模式的支持可能会在 GWT 的未来版本中被删除,建议您更新支持 GWT 的页面以使用标准模式。

事件处理和性能

如何在表格中以良好的性能显示大量项目?

以下是一些关于如何有效地通过表格小部件显示大量数据的技巧。

固定宽度表格

从性能角度来看,使用固定宽度表格比使用动态调整大小的表格更好。

RPC 和其他网络事务注意事项

大多数应用程序都在通过网络加载其表格的数据。尝试减少应用程序填充表格所需的往返次数。两个主要策略浮出水面

  • 在同一个请求中获取多行。
  • 将应用程序不同子系统的活动合并到一个 RPC 调用中。从组合的数据结构中,这样您就可以在同一个 RPC 调用中获取与应用程序不同部分相关的数据,并减少用户等待时间。

测试性能

如果您有一个大型表格,请确保您使用要支持的最大行数运行测试。在某些浏览器上,已经观察到表格性能随着行数的增加而呈非线性下降。

尝试使用 PagingScrollTable

如果您发现表格性能存在问题,解决问题的最佳方法可能是不要显示大部分数据。一种称为 PagingScrollTable 的表格实现可以帮助您一次显示一页数据,而不是必须等待整个数据集下载到浏览器。这种类型的表格在 Web 应用程序中非常常见,用户不会感到陌生。一般来说,如果您要显示 50 行以上的数据,请考虑使用 PagingScrollTable。

请注意,PagingScrollTable 是 GWT Incubator 项目的一部分,您需要从核心 GWT 发行版中单独下载该项目。

如果您可以使用 PagingScrollTable,那么请考虑使用 CachedTableModel(也是 GWT Incubator 的一部分),您可以使用它自动预取行,例如下一页。这可以显着提高感知速度。

尝试使用 BulkTableRenderer 和 PreloadedTable 类

BulkTableRenderer 将一次渲染表格中的所有行。BulkTableRenderer 有针对不同目的的派生类型。只要您的表格内容不是小部件,您就可以提供一个表格模型,BulkRenderer 会创建整个表格并将其渲染为单个 HTML 字符串,并使用 setInnerHtml() 设置,这比以往快 2-10 倍。

请注意,加载后,可以将小部件、单元格跨度、行跨度等添加到表格中,但这样做不会带来速度优势。

我该怎么做才能使图像和边框在第一次使用时看起来加载得更快?

第一次应用程序访问图像时,您可能会注意到它可能只加载了一部分,或者加载速度比后续调用慢。由于第一印象很重要,因此您可以尝试使用此技巧。

根本问题是,背景、边框或动画所需的图像都是按需从服务器获取的。一个解决方案是预取图像,以便它们在实际调用动画时已位于浏览器缓存中。检查您的 CSS 文件以查找在样式中使用的图像,如果这些图像运行缓慢,请使用 Image.prefetch() 方法。

Image.prefetch("images/corner-bl.png");
    Image.prefetch("images/corner-br.png");
    Image.prefetch("images/corner-tl.png");
    Image.prefetch("images/corner-tr.png");

为什么使用 1 像素高的背景图像时会出现性能低下?

使用单像素图片作为重复背景图很常见。但是请注意,用小图片覆盖大面积可能会导致性能下降(尤其是在 Internet Explorer 上我们发现这个问题)。在使用 GWT 动画时,这种情况尤为明显。

解决这个问题的一种方法是增加图片尺寸,这样渲染图片就不需要频繁地重复图片。因此,与其使用 20 x 1 的图片,你可以尝试使用 20 x 100 的图片。这样做需要权衡的是,浏览器需要从网络上下载更大的图片。

随着应用程序的增长,事件处理程序的触发速度似乎变慢了

如果你正在创建一个包含许多需要响应事件的组件的界面(例如 `click` 事件),你可能会发现应用程序处理事件的速度越来越慢。例如,在一个表格中有一系列组件。

导致性能下降的一个因素可能是管理大量点击处理程序实例的开销。考虑一个常见的处理点击事件的方法。

Button newButton = new Button(titleText);
  newButton.addClickHandler(new ClickHandler() {
    public void onClick(ClickEvent event) {
      dialogBox.hide();
    }
  });

每个组件都有自己的点击处理程序实例,该实例是在创建组件时通过添加匿名内部类创建的。这种构造很方便,在大多数情况下不会导致性能问题,但当创建许多需要事件处理的组件时,考虑创建一个单一的点击处理程序并将其在多个组件之间共享。

class MyApp implements EntryPoint, ClickHandler {

   /* Be careful with saving UI components in datastructures like this:
    * if you remove a button from the app, make sure you also remove
    * its reference from buttonMap HashMap to avoid memory leaks.
    */
   Map buttonMap<Button, Integer> = new HashMap<Button,Integer>();

   public void onModuleLoad() {
     FlowPanel panel = new FlowPanel();
     for (int i = 1; i < 100; ++i) {
        Button newButton = new Button("Option " + i);
        newButton.addClickHandler(this);
        panel.add(newButton);
        buttonmap.add(newButton, Integer.valueOf(i));
     }
     RootPanel.get().add(panel);
   }

   // The shared ClickHandler code.
   public void onClick(ClickEvent event) {
     Object sender = event.getSource();
     if (sender instanceof Button) {
       Button b = (Button) sender;
       Integer context = buttonMap.get(b);
       if (context != null) {
         // ... Handle the button click for this button.
       }
     }
   }
 }

如何有效地处理来自许多内部组件的事件?

Quirksmode 网站有一篇关于 事件冒泡 的文章,解释了不同浏览器传递事件的方式。在使用 GWT 事件处理程序时,GWT 会将这一切隐藏起来,只为你传递你感兴趣的元素的事件。这是一个有用的编程抽象,但在系统中添加了大量处理程序时会导致性能下降。

如果你正在构建一个组件,你可能想考虑以另一种方式处理事件,称为事件冒泡。本质上,这种技术意味着你为整个组件设置一个处理程序,并且你有一个处理程序回调,它代表所有内部组件执行。

请参阅 Tree 类实现,了解在 GWT 中使用事件冒泡的示例。