序言

本文档介绍了 CUBA 框架的两个功能模块,图表展示和地图展示。这两个子系统在同一个扩展中实现 - charts, 因而在一个项目中只能一起引入。

图表和地图的展示功能目前只能在 web 客户端使用。

目标读者

本手册适用于使用 CUBA 框架构建应用程序的开发人员。此处假设读者熟悉 开发者手册

其它材料

本指南以及 CUBA 框架的任何其它文档可从 https://www.cuba-platform.com/documentation 获取。

CUBA 图表展示子系统是基于 AmCharts 库实现的,所以熟悉这个库对图表开发很有帮助。参阅 http://www.amcharts.com

反馈

如果您有任何改进本手册的建议,请随时在 GitHub 上报告问题。如果你看到拼写或措辞错误、bug 或不一致的地方,可以直接 fork repo 进行修复。谢谢!

1. 图表展示

CUBA 框架的图表展示扩展支持多样化的图表类型:饼图,折线图,气泡图,雷达图,漏斗图,股票图等等。图表也支持导出。大部分的图表都支持缩放和滚轮操作。图表 只支持 web 客户端。

图表扩展是基于 AmCharts 类库开发的,并且在一定的许可下进行发布使用,如果保留到 AmCharts 网站的链接的话,许可范围内可以免费使用。或者,可以为项目 购买 AmCharts 的许可,这样可以移除链接。

1.1. 添加图表依赖

要在项目中使用图表,在 CUBA Studio 中打开 Project properties 编辑器:点击 CUBA → Project Properties 主菜单项,在 App components 列表中,选中 charts 应用程序组件。Studio 提示重建 Gradle 脚本时,点击确定。

1.2. 配置图表

图表通过 Chart 组件进行展示,该组件可以作为全局的画布(canvas)。图表类型通过子类型接口定义,该接口继承了 Chart 接口。

图表可以通在 界面 XML 描述 或者 界面控制器 中创建。此时需要在 XML 描述中加入相应的 chart 命名空间

<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
        ...>

不同图表类型对应的 XML 元素:

每种图表类型有其本身的属性和方法,这些属性和方法是从 AmCharts 库的相应图表拷贝过来的。图表的属性和方法的文档可以参阅: http://docs.amcharts.com/3/javascriptcharts

下列元素可以用来为所有类型的图表做声明式的配置:

  • chart:allLabels – 包含 label 元素,带有标签文字和其它属性。标签可以放在图表的特定位置,比如:

    charts allLabels
    Figure 1. chart:allLabels
    <chart:allLabels>
        <chart:label x="200"
                     y="700"
                     text="You can place a label at any position on the chart"
                     color="DARKBLUE"
                     align="CENTER"
                     rotation="-30"/>
    </chart:allLabels>
  • chart:balloon – 配置鼠标悬浮在图表上时显示的气球(提示窗)的样式,示例:

    charts balloon
    Figure 2. chart:balloon
    <chart:graphs>
        <chart:graph balloonText="[[category]]: [[value]] m/s"
                     bullet="ROUND"
                     fillAlphas="0.3"
                     valueField="value"/>
    </chart:graphs>
    <chart:balloon adjustBorderColor="false"
                   color="WHITE"
                   horizontalPadding="10"
                   verticalPadding="8"/>

    气球的文字通过每个图表的 balloonText 属性来定义。

    • additionalFields 属性

      可以在图表的所有字段中插入图表的数据值,比如 titleField, valueField, category, value, description, percents, open 等等,另外,HTML 中也能使用。

      也可以使用 additionalFields 属性从数据提供方获取更多的实体属性。通过这个属性可以将实体属性传递到图表的查询语句中,之后将数据返回给 UI。因此可以在图表的配置中直接使用实体属性的名称。

      在下面的例子中,title 是图表的标题,category 是 category 轴的值,value 是 value 轴的值,optionalIncomeExpenses 实体属性,取出来用来插入到 balloonText 中:

      <chart:serialChart additionalFields="optional"
                         addClassNames="true"
                         categoryField="year"
                         dataContainer="incomeExpensesDc"
                         startDuration="1">
          <chart:graphs>
              <chart:graph alphaField="alpha"
                           balloonText="&lt;span style='font-size:12px;'&gt;[[title]] in [[category]]:&lt;br&gt; &lt;span style='font-size:20px;'&gt;[[value]]&lt;/span&gt; [[optional]]&lt;/span&gt;"
                           dashLengthField="dashLengthColumn"
                           title="Income"
                           type="COLUMN"
                           valueField="income"/>
              <...>
          </chart:graphs>
      </chart:serialChart>
      charts balloon additiional
      Figure 3. additionalFields

      字段列表可以通过英文逗号分隔的字符串声明式添加:

      additionalFields="income,expense,vat"

      或者通过编程的方式,在界面控制器中添加:

      List<String> fields = Arrays.asList("income", "expense", "vat");
      ganttChart.setAdditionalFields(fields);
  • chart:chartScrollbar (对于 序列图散点图 有效) – 图表的滚动条。

    • 可以设置具体图表通过滚动条进行缩放,示例:

      charts zoom
      Figure 4. chart:chartScrollbar
      <chart:chartScrollbar graph="g1"
                            autoGridCount="true"
                            scrollbarHeight="30"/>
    • 通过 chart:chartScrollbarSettings 元素可以对滚动条的属性进行定制化设置。

      <chart:chartScrollbarSettings graph="stockGraph"
                                    usePeriod="10mm"
                                    position="TOP"/>
    • 另外,甘特图 可以有一个 chart:valueScrollbar 元素用来做 value 轴的滚动,chart:chartScrollbar 用来做 category 轴的缩放。

      charts valueScrollBar
      Figure 5. chart:valueScrollbar
      <chart:valueScrollbar autoGridCount="true"
                            color="BLACK"/>
      <chart:chartScrollbar autoGridCount="true"
                            color="DARKOLIVEGREEN"/>
  • chart:cursor – 可选元素,能在图标上添加光标;光标跟着鼠标指针走,展示图表上对应点的数值小弹窗。

    charts cursor
    Figure 6. chart:cursor
    <chart:chartCursor cursorAlpha="1"
                       cursorColor="#258cbb"
                       cursorPosition="MOUSE"
                       limitToGraph="g1"
                       pan="true"
                       valueLineAlpha="0.2"
                       valueLineBalloonEnabled="true"
                       valueLineEnabled="true"
                       valueZoomable="true"/>
  • chart:data – 可选元素,用来做数据绑定。一般在做原型的时候使用。

  • chart:export – 可选元素,可以启用图表导出。默认的导出实现会在图表上添加一个浮动的 download 按钮:

    charts export
    Figure 7. chart:export
  • chart:guides – 水平/垂直参考线。

    charts guides
    Figure 8. chart:guides
    <chart:guides>
        <chart:guide category="2001"
                     dashLength="2"
                     fillAlpha="0.2"
                     fillColor="#CC0000"
                     inside="true"
                     label="fines for speeding increased"
                     labelRotation="90"
                     toCategory="2003"/>
        <chart:guide category="2007"
                     dashLength="2"
                     inside="true"
                     label="motorcycle fee introduced"
                     labelRotation="90"
                     lineAlpha="1"
                     lineColor="#CC0000"/>
    </chart:guides>
  • chart:legend – 定义图表图例的元素,示例:

    charts legend
    Figure 9. chart:legend
    <chart:legend autoMargins="false"
                  marginRight="20"
                  markerType="CIRCLE"
                  position="RIGHT"
                  valueText="[[category]]: [[value]] %"/>
  • chart:nativeJson – 图表的 JSON 配置

  • chart:titles – 图表标题,示例:

    charts titles
    Figure 10. chart:titles
    <chart:titles>
        <chart:title alpha="1"
                     bold="true"
                     color="DARKSLATEGREY"
                     size="20"
                     tabIndex="0"
                     text="Main title"/>
        <chart:title alpha="0.5"
                     color="BISQUE"
                     size="12"
                     text="Subtitle"/>
    </chart:titles>
  • chart:responsive – 图表的响应式插件。

    此插件可以自动调整图表的大小以适应不同的屏幕分辨率。关于 responsive 插件的更多信息可以参阅 AmCharts website

    responsive 元素需要包含嵌套的 rules 元素,这里可以定义适配的规则。可以设置图表是否显示图例、轴标题、参考线、图表标题、缩放控制器,或者将标签移入图表区域等:

    <chart:responsive enabled="true">
        <chart:rules>
            <chart:rule maxWidth="400">
                <![CDATA[
                {
                    "precision": 2,
                    "legend": {
                      "enabled": false
                    },
                    "valueAxes": {
                      "inside": true
                    }
                }
                ]]>
            </chart:rule>
        </chart:rules>
    </chart:responsive>

任何配置属性都可以设置为 null;此时,系统会使用默认值(除了 AmCharts 文档中提到的特殊情况)。

在界面控制器配置图表也是按照相同的逻辑。可以配置单一属性,也可以配置组合对象:

pieChart.setWidth("700px");
pieChart.setTitleField("description")
        .setValueField("value")
        .setStartAngle(312)
        .setLegend(new Legend()
                .setMarkerType(MarkerType.CIRCLE)
                .setPosition(LegendPosition.RIGHT)
                .setMarginRight(80))
        .addLabels(
                new Label()
                    .setText("Sample Chart")
                    .setSize(26)
                    .setBold(true)
                    .setAlign(Align.CENTER),
                new Label()
                    .setText("extra information")
                    .setAlign(Align.RIGHT))
        .setLabelTickColor(Color.GOLDENROD)
        .setColors(Arrays.asList(
                    Color.valueOf("#446493"),
                    Color.valueOf("#5E3D2C"),
                    Color.valueOf("#D0A557")))
        .setDataProvider(dataProvider);

展示图表使用的信息可以使用 web 模块的主语言消息包进行覆盖或者本地化。在 CUBA GitHub 已经有一些本地化语言包。

1.3. 图表导出

运行程序的所有图表都可以作为图片或者原始数据导出。使用 chart:export 元素来创建默认的导出菜单,默认的菜单有如下选项:

  • Download as…​ 支持的格式: PNG, JPG, SVG, PDF

  • Save as…​ 支持的格式: CSV, XLSX, JSON

  • Annotate…​ 用来在图表中添加个人注解或矢量图形。细节可以参阅:https://www.amcharts.com/export-now-supports-annotating-charts-with-text-icons-arrows/[注解插件]。

  • Print 打开标准打印设置窗口。

charts export menu 1
Figure 11. 图表导出菜单

导出菜单可以定制化,以便限制用户对图表数据的访问,示例:

<chart:export fileName="my-chart" position="TOP_RIGHT">
    <chart:menu>
        <chart:item label="PNG" title="Save as PNG" format="PNG"/>
        <chart:item label="JPG" title="Save as JPG" format="JPG"/>
    </chart:menu>
</chart:export>

这样的话,只显示选择的文件类型相应的下载按钮:

charts export menu 2
Figure 12. 图表导出菜单

可以定义如下导出设置:

  • backgroundColor – 导出图片背景色的颜色代码,默认值是 #FFFFFF

  • dataDateFormat – 将日期字符串转换成日期对象的格式(只在导出数据时有效)。

  • dateFormat – 按照给定的格式对 category 轴的时间数据做格式化(只在导出数据时有效)。

  • enabled – 启用或禁用导出功能。

  • exportSelection – 只导出选中的数据。默认值是 false

  • exportTitles – 将数据字段的名称换成定义的标题。默认值是 false

  • fileListener – 如果设置成 true,则会监听拖拽功能,将投入的图片加载作为注解。默认值是 false

  • fileName – 生成文件的文件名(根据导出格式的不同会添加相应的文件后缀)。

  • keyListener – 如果设置成 true,观察按下的键来撤销/恢复对注解的编辑。

  • position – 导出图标的位置。可能值:TOP_LEFT, TOP_RIGHT (默认), BOTTOM_LEFT, BOTTOM_RIGHT

  • removeImages – 如果设置成 true,导出时会检查并且删除区域从不同领域加载的不必要的图片。

下列属性可以用来定制化每个导出选项:

JPG
  • quality – 导出图片的质量。可能值: 01。默认值是 1

PNG, JPG, SVG, PDF
  • multiplier – 生成图片的缩放比例

CSV
  • quotes – 设置是否需要在字符串两端加上双引号。默认值是 true.

  • delimiter – 列分隔符,默认值是 "," (逗号).

  • escape – 定义是否需要转译字符串。默认值是 true.

  • withHeader – 是否带用列名称的表头。默认值是 true.

XLSX
  • dateFormat – XLSX 日期格式,别忘了在 CategoryAxis 里面设置 parseDatestrue

  • stringify – 将所有单元格的内容转化成字符串。默认值是 false

  • withHeader – 是否带用列名称的表头。默认值是 true

PDF
  • pageOrientation – 页面方向。默认值是 PORTRAIT

  • pageOrigin – 显示/隐藏生成的 PDF 的来源。默认值是 true

  • pageSize – PDF 列表的格式。默认值是 A4

此外,可以在主消息包中覆盖 label.saved.from 消息的字符串。

PRINT
  • delay – 触发打印之前的延时,单位是秒。

  • lossless – 启用/禁用打印时图像优化。默认值是 false

1.4. 连接数据

有两种方法可以向图表传递数据:通过 DataProvider 接口或者使用简化的数据绑定 API。简化的数据绑定 API 可以直接使用 addData() 方法和 MapDataItem 类构造器为没有绑定数据源的图表来添加数据。图表使用示例包含了为图标提供数据的所有方法的介绍。

DataProvider:

DataProvider 接口有两个标准实现: ListDataProvider 类和 ContainerDataProvider 类。

  • ListDataProvider 包含了一个 DataItem 实例的列表,图表的数据将从这个列表里获取。DataItem 接口有一些标准实现:

    • EntityDataItem - 表示任意实体类的实例,从该实例中获取图表数据。

      @Inject
      private PieChart chart;
      
      @Inject
      private Metadata metadata;
      
      @Subscribe
      protected void onInit(InitEvent event) {
          ListDataProvider dataProvider = new ListDataProvider();
          dataProvider.addItem(new EntityDataItem(valueDescription(75, "Sky")));
          dataProvider.addItem(new EntityDataItem(valueDescription(7, "Shady side of pyramid")));
          dataProvider.addItem(new EntityDataItem(valueDescription(18, "Sunny side of pyramid")));
      
          chart.setDataProvider(dataProvider);
      }
      
      private ValueDescription valueDescription(Integer value, String description) {
          ValueDescription entity = metadata.create(ValueDescription.class);
          entity.setValue(value);
          entity.setDescription(description);
          return entity;
      }
    • MapDataItem - 键值对集合,从中获取图表数据。

      @Inject
      private PieChart chart;
      
      @Subscribe
      protected void onInit(InitEvent event) {
          ListDataProvider dataProvider = new ListDataProvider();
          dataProvider.addItem(new MapDataItem(
                  ImmutableMap.of("value", 75, "description", "Sky")));
          dataProvider.addItem(new MapDataItem(
                  ImmutableMap.of("value", 7, "description", "Shady side of pyramid")));
          dataProvider.addItem(new MapDataItem(
                  ImmutableMap.of("value", 18, "description", "Sunny side of pyramid")));
      
          chart.setDataProvider(dataProvider);
      }
    • SimpleDataItem - 表示任意 public 类的实例,从中获取图表数据。

      @Inject
      private PieChart chart;
      
      @Subscribe
      protected void onInit(InitEvent event) {
          ListDataProvider dataProvider = new ListDataProvider();
          dataProvider.addItem(new SimpleDataItem(new ValueDescription(75, "Sky")));
          dataProvider.addItem(new SimpleDataItem(new ValueDescription(7, "Shady side of pyramid")));
          dataProvider.addItem(new SimpleDataItem(new ValueDescription(18, "Sunny side of pyramid")));
      
          chart.setDataProvider(dataProvider);
      }
      
      public class ValueDescription {
          private Integer value;
          private String description;
      
          public ValueDescription(Integer value, String description) {
              this.value = value;
              this.description = description;
          }
      
          public Integer getValue() {
              return value;
          }
      
          public String getDescription() {
              return description;
          }
      }
  • ContainerDataProvider 用于给 Chart 组件分配一个 CollectionContainer

    假设我们有数据容器和数据加载器用来加载 TransportCount 实例。下面是界面 XML 描述的片段:

    <data>
        <collection id="transportCountsDc"
                    class="com.company.sampler.entity.TransportCount"
                    view="_local">
            <loader id="transportCountsDl">
                    <query><![CDATA[select e from sampler_TransportCount e order by e.year]]></query>
            </loader>
        </collection>
    </data>
    <layout>
        <chart:serialChart id="stackedArea"
                           marginLeft="0"
                           marginTop="10"
                           plotAreaBorderAlpha="0"
                           height="100%"
                           width="100%">
            <!--...-->
        </chart:serialChart>
    </layout>

    界面控制器定义了 onInit() 方法,设置了 stackedArea 图表的 data provider。Data provider 是基于 transportCountsDc 数据容器。

    @UiController("sampler_StackedareaChartSample")
    @UiDescriptor("stackedarea-chart-sample.xml")
    @LoadDataBeforeShow
    public class StackedareaChartSample extends Screen {
    
        @Inject
        private CollectionContainer<TransportCount> transportCountsDc;
    
        @Inject
        private SerialChart stackedArea;
    
        @Subscribe
        private void onInit(InitEvent event) {
            stackedArea.setDataProvider(new ContainerDataProvider(transportCountsDc));
            stackedArea.setCategoryField("year");
        }
    }

    这种方式需要一个实体来表示图表数据。如果应用程序数据模型中已经存在这样的一个实体,并且图表数据是要显示为表格时,这种方式可能就比较方便。

DataProvider 的实例被传递给图表(chart configuration)配置的 setDataProvider() 方法。 这种提供图表数据的方法是最通用的,但它需要在界面控制器中创建 DataProviderDataItem 的实例。

DataProvider 实例中包含用来展示的实体属性或者值需要在图表属性中定义。根据图表类型的不同,图表属性集合也不一样。比如,对于 chart:pieChart 组件,需要定义 valueFieldtitleField 属性。属性值可以使用这些类型:IntegerLongDoubleStringBooleanDate

DataProvider 机制支持对已经渲染的图表动态添加数据。

chart:data 元素:

这个元素可以用来快速的做图表原型。使用 chart:data 和内部嵌套的 item 元素可以直接在图表的 XML 描述中设置键值对类型的数据,示例:

<chart:pieChart id="pieChart"
                titleField="key"
                valueField="value">
    <chart:data>
        <chart:item>
            <chart:property name="key" value="piece of apple pie"/>
            <chart:property name="value" value="70" type="int"/>
        </chart:item>
        <chart:item>
            <chart:property name="key" value="piece of blueberry pie"/>
            <chart:property name="value" value="20" type="int"/>
        </chart:item>
        <chart:item>
            <chart:property name="key" value="piece of cherry pie"/>
            <chart:property name="value" value="10" type="int"/>
        </chart:item>
    </chart:data>
</chart:pieChart>

1.5. 事件

可以为每种图表的不同类型的事件配置处理函数。以下监听器类型对于所有图表子类型都适用:

  • LegendItemHideListener – 隐藏一个图例。

  • LegendItemShowListener – 显示一个图例。

  • LegendLabelClickListener – 点击图例标签。

  • LegendMarkerClickListener – 点击图例标记。

其它针对具体图表类型的事件监听器可以在手册的这部分找到。

对事件处理的例子可以参阅 使用事件

除了监听器之外,SeriesBasedChart 接口包含 zoomOutzoomToIndexeszoomToDates 方法用来操纵图表的轴。

类似的 ,CoordinateChart 接口也提供这些方法用来操纵值坐标轴: zoomOutValueAxeszoomOutValueAxiszoomOutValueAxiszoomValueAxisToValueszoomValueAxisToValues

1.6. 图表类型

CUBA 框架支持以下这些不同类型的图表。

charts hierarchy diagram
Figure 13. 图表类型层级结构

以下图表组件实现了图表接口:

所有组件都有 NAME 常量,所以可以使用 UiComponents 来创建。

1.6.1. 仪表盘图

使用 AngularGaugeChart 组件可以创建仪表盘图。

gauge chart
Figure 14. 仪表盘图

该组件的 XML 名称:chart:gaugeChart

chart:gaugeChart 的元素
  • arrows – 包含嵌套的 arrow 元素,作为图表的箭头。

    <chart:arrows>
        <chart:arrow value="60"/>
    </chart:arrows>
  • axes – 包含嵌套的 axis 元素,作为仪表盘的坐标轴。

    <chart:axes>
        <chart:axis id="blue"
                    axisColor="#67b7dc"
                    axisThickness="3"
                    gridInside="false"
                    inside="false"
                    radius="100%"
                    valueInterval="20"
                    tickColor="#67b7dc"/>
        <chart:axis labelsEnabled="true"
                    axisColor="#fdd400"
                    axisThickness="3"
                    endValue="160"
                    radius="80%"
                    valueInterval="20"
                    tickColor="#fdd400"/>
    </chart:axes>

    使用 band 元素可以将坐标轴分成不同的部分,如上面的图所示:

    <chart:axes>
        <chart:axis axisAlpha="0.2"
                    axisThickness="1"
                    bottomText="60 km/h"
                    bottomTextYOffset="-20"
                    endValue="220"
                    tickAlpha="0.2"
                    valueInterval="20">
            <chart:bands>
                <chart:band color="#84b761"
                            endValue="90"
                            startValue="0"/>
                <chart:band color="#fdd400"
                            endValue="130"
                            startValue="90"/>
                <chart:band color="#cc4748"
                            endValue="220"
                            innerRadius="95%"
                            startValue="130"/>
            </chart:bands>
        </chart:axis>
    </chart:axes>

    endValuestartValue 属性用来设置图表的值域范围,valueInterval 属性用来显示仪表盘标尺刻度。

AngularGaugeChart 事件监听器:
  • ChartClickListener – 点击画布。

  • ChartRightClickListener – 右键点击画布。

更多细节,参阅 AmCharts 文档

1.6.2. 漏斗图

使用 FunnelChart 组件可以创建漏斗/金字塔图。

funnel chart
Figure 15. 漏斗图

该组件的 XML 名称:chart:funnelChart

数据绑定:
  1. 可以为图表指定集合数据容器,然后为 funnelChart 元素定义 titleFieldvalueField 属性:

    <chart:funnelChart id="ratingChart"
                       align="MIDDLE_CENTER"
                       dataContainer="ratingDc"
                       height="200px"
                       labelPosition="RIGHT"
                       labelText="[[title]]: [[value]]"
                       marginRight="120"
                       maxLabelWidth="110"
                       marginTop="20"
                       titleField="mechanic"
                       valueField="count"
                       width="500px">
    </chart:funnelChart>
  2. 使用 chart:data 元素。

    <chart:funnelChart id="ratingChart"
                       titleField="mechanic"
                       valueField="count">
        <chart:data>
            <chart:item>
                <chart:property name="mechanic" value="Jack"/>
                <chart:property name="count" value="1" type="int"/>
            </chart:item>
            <chart:item>
                <chart:property name="mechanic" value="Bob"/>
                <chart:property name="count" value="2" type="int"/>
            </chart:item>
            <chart:item>
                <chart:property name="mechanic" value="Sam"/>
                <chart:property name="count" value="3" type="int"/>
            </chart:item>
        </chart:data>
    </chart:funnelChart>
FunnelChart 事件监听器:
  • SliceClickListener – 点击漏斗图的一层。

  • SlicePullInListener – 漏斗图的一层移回图表。

  • SlicePullOutListener – 漏斗图的一层移出图表。

  • SliceRightClickListener – 右键点击漏斗图的一层。

更多细节,参阅 AmCharts 文档

1.6.3. 甘特图

使用 GanttChart 组件可以创建甘特图。

gantt chart
Figure 16. 甘特图

该组件的 XML 名称:chart:ganttChart

chart:ganttChart 的元素:
  • categoryAxis – 描述分类轴的元素。

  • graph – 包含 chart:graph 元素集合;图形是通过 chart:graph 元素进行描述。

    • type 属性定义图的类型,可以是:线(line),列(column),步进线(step line),平滑线(smoothed line),蜡烛图(OHLC)以及烛台图(candlestick)。

    • valueField 属性定义数据提供者的键值对列表的键(key)。

  • valueAxis – 图表的值坐标轴。如果图表数据是基于日期或者时间的,可以设置值坐标轴类型为 date

chart:ganttChart 的属性:
  • segmentsField – 图表的分段字段。

  • additionalSegmentFields – 需要从数据提供器中获取的对应于实体属性的额外分段字段,跟 additionalFields 属性类似。

  • endField/endDateField – 图表的终止值或者终止时间。

  • startField/startDateField – 图表的起始值或者起始时间。

  • startDate – 如果值坐标轴的类型是 date,定义图表的起始时间。

  • categoryField – 图表的分类字段。

数据绑定

可以为图表指定一个 CollectionContainer 。在下面的例子中,使用实体的 startend 属性来为 XML 的 startDateFieldendDateField 属性赋值。

<chart:ganttChart id="ganttChart"
                  additionalSegmentFields="task"
                  balloonDateFormat="JJ:NN"
                  brightnessStep="7"
                  categoryField="category"
                  colorField="color"
                  columnWidth="0.5"
                  dataContainer="taskSpansDc"
                  endDateField="end"
                  height="100%"
                  marginRight="70"
                  period="DAYS"
                  rotate="true"
                  segmentsField="segments"
                  startDate="2016-01-01"
                  startDateField="start"
                  theme="LIGHT"
                  width="100%">
    <chart:graph balloonText="&lt;strong&gt;[[task]]&lt;/strong&gt;: [[open]] - [[value]]"
                 fillAlphas="1"
                 lineAlpha="1"
                 lineColor="WHITE"/>
    <chart:valueAxis type="DATE"/>
    <chart:valueScrollbar autoGridCount="true"
                          color="BLACK"/>
    <chart:chartCursor cursorAlpha="0"
                       cursorColor="#55bb76"
                       fullWidth="true"
                       valueLineAlpha="0.5"
                       valueBalloonsEnabled="false"
                       valueLineBalloonEnabled="true"
                       valueLineEnabled="true"
                       valueZoomable="true"
                       zoomable="false"/>
    <chart:export/>
</chart:ganttChart>
GanttChart 事件监听器:
  • AxisZoomListener – 图表坐标轴缩放。

  • CategoryItemClickListener – 在分类轴点击一个分类。

  • ChartClickListener – 点击画布。

  • ChartRightClickListener – 右键点击画布。

  • CursorPeriodSelectListener – 用光标选择显示周期。

  • CursorZoomListener – 用光标对图表区域进行缩放。

  • GraphClickListener – 点击一个图形。

  • GraphItemClickListener – 点击图形的条目。

  • GraphItemRightClickListener – 右键点击图形的条目。

  • ZoomListener – 画布的缩放。

更多细节,参阅 AmCharts 文档

1.6.4. 饼图

使用 PieChart 组件可以创建饼图/多纳圈图。

pie chart
Figure 17. 饼图

该组件的 XML 名称:chart:pieChart

数据绑定:
  1. 使用数据容器。

    可以为图表指定一个 CollectionContainer 。然后为 pieChart 元素定义 titleFieldvalueField 属性:

    <chart:pieChart id="pieChart"
                    dataContainer="countryLitresDc"
                    height="100%"
                    titleField="country"
                    valueField="litres"
                    width="100%"/>
  2. 使用 chart:data 元素。

PieChart 事件监听器:
  • ChartClickListener – 点击画布。

  • ChartRightClickListener – 右键点击画布。

  • SliceClickListener – 点击饼图的一个分片。

  • SlicePullInListener – 饼图的一个分片移入图表。

  • SlicePullOutListener – 饼图的一个分片移出图表。

  • SliceRightClickListener – 右键点击饼图的一个分片。

更多细节,参阅 AmCharts 文档

1.6.5. 雷达图

使用 RadarChart 组件可以创建雷达/极坐标图。

radar chart
Figure 18. 雷达图

该组件的 XML 名称:chart:radarChart

数据绑定:

可以为图表指定一个 CollectionContainer。然后为 radarChart 元素定义 categoryField 属性,为嵌套的 graph 元素定义 valueField 属性:

<chart:radarChart id="radarChart"
                  categoryField="country"
                  dataContainer="countryLitresDc"
                  height="100%"
                  startDuration="2"
                  theme="LIGHT"
                  width="100%">
    <chart:graphs>
        <chart:graph balloonText="[[value]] litres of beer per year"
                     bullet="ROUND"
                     valueField="litres"/>
    </chart:graphs>
</chart:radarChart>
RadarChart 事件监听器
  • AxisZoomListener – 图表坐标轴缩放。

  • ChartClickListener – 点击画布。

  • ChartRightClickListener – 右键点击画布。

  • GraphClickListener – 点击图形。

  • GraphItemClickListener – 点击图形的条目。

  • GraphItemRightClickListener – 右键点击图形的条目。

更多细节,参阅 AmCharts 文档

1.6.6. 序列图

使用 SerialChart 组件可以创建线图、区域图、列图、柱图、步进线图,平滑线图,烛台图以及蜡烛图。图表支持多坐标轴,坐标轴支持简单或者对数刻度,数据点可以等距/非等距展示或者基于时间线展示。

line chart
Figure 19. 线图展示的序列图
column chart
Figure 20. 列图展示的序列图

该组件的 XML 名称:chart:serialChart

数据绑定:

可以为图表指定一个 CollectionContainer。然后为 serialChart 元素定义 categoryField 属性,为嵌套的 graph 元素定义 valueField 属性:

<chart:serialChart categoryField="date"
                   dataContainer="dateValueDc">
    <chart:graphs>
        <chart:graph valueField="value"
                     balloonText="[[value]]">
        </chart:graph>
    </chart:graphs>
    <chart:categoryAxis dashLength="1"
                        minorGridEnabled="true"/>
</chart:serialChart>
SerialChart 事件监听器
  • AxisZoomListener – 图表坐标轴缩放。

  • CategoryItemClickListener – 在分类轴点击一个分类。

  • ChartClickListener – 点击画布。

  • ChartRightClickListener – 右键点击画布。

  • CursorPeriodSelectListener – 用光标选择显示周期。

  • CursorZoomListener – 用光标对图表区域进行缩放。

  • GraphClickListener – 点击一个图形。

  • GraphItemClickListener – 点击一个图形条目。

  • GraphItemRightClickListener – 右键点击一个图形条目。

  • ZoomListener – 画布的缩放。

更多细节,参阅 AmCharts 文档

1.6.7. 股票图

使用 StockChartGroup 组件可以创建股票走势图。

股票图支持多个数据集合并且有开箱即用的数据集合选择器。数据集合可以相互比较。

stock chart with datasets
Figure 21. 有多数据集合的股票图

股票图可以在图上或者坐标轴上展示不同类型的注释。这些注释称为股票事件。

stock chart with stockevents
Figure 22. 有股票事件的股票图

股票图能支持多个股票面板。每个股票面板可以有任意数量的图。每个股票面板是个基于SerialChart的单独序列图,所以能做任何SerialChart可做的事。

stock chart with panels
Figure 23. 带有多个股票面板的股票图
StockChartGroup 事件监听器
  • DataSetSelectorCompareListener – 数据集合选择器比较事件。

  • DataSetSelectorSelectListener – 数据集合选择器的选择事件。

  • DataSetSelectorUnCompareListener – 数据集合选择器取消比较事件。

  • PeriodSelectorChangeListener – 用光标选择显示周期。

  • StockChartClickListener – 点击股票图区域。

  • StockChartRightClickListener – 右键点击股票图区域。

  • StockEventClickListener – 点击股票事件。

  • StockEventRollOutListener – 股票事件弹出。

  • StockEventRollOverListener – 股票事件翻转。

  • StockGraphClickListener – 点击股票图形。

  • StockGraphItemClickListener – 点击股票图形条目。

  • StockGraphItemRightClickListener – 右键点击股票图形条目。

  • StockGraphItemRollOutListener – 股票图形条目弹出事件。

  • StockGraphItemRollOverListener – 股票图形条目翻转事件。

  • StockGraphRollOutListener – 股票图形弹出事件。

  • StockGraphRollOverListener – 股票图形翻转事件。

  • ZoomListener – 画布的缩放。

1.6.8. 散点图

使用 XYChart 组件可以创建坐标点图/气泡图/散点图。图表支持多个坐标轴,可以使用简单或者对数刻度。

xy chart
Figure 24. 散点图

该组件的 XML 名称:chart:xyChart

数据绑定:

可以为图表指定 CollectionContainer。然后为嵌套的 graph 元素定义 xFieldyFields 属性:

<chart:xyChart dataContainer="pointPairDc"
               startDuration="1">
    <chart:graphs>
        <chart:graph balloonText="x:[[x]] y:[[y]]"
                     xField="ax"
                     fillToAxis="x"
                     yField="ay"/>
        <chart:graph balloonText="x:[[x]] y:[[y]]"
                     fillToAxis="y"
                     xField="bx"
                     yField="by"/>
    </chart:graphs>
    <chart:valueAxes>
        <chart:axis id="x"
                    axisAlpha="0"
                    dashLength="1"
                    position="BOTTOM"
                    title="X Axis"/>
        <chart:axis id="y"
                    axisAlpha="0"
                    dashLength="1"
                    position="LEFT"
                    title="Y Axis"/>
    </chart:valueAxes>
</chart:xyChart>
XYChart 事件监听器
  • AxisZoomListener – 图表轴缩放。

  • ChartClickListener – 点击画布。

  • CursorPeriodSelectListener – 用光标选择显示周期。

  • CursorZoomListener – 用光标对图表区域进行缩放。

  • GraphClickListener – 点击一个图形

  • GraphItemClickListener – 点击一个图形条目。

  • GraphItemRightClickListener – 右键点击一个图形条目。

更多细节,参阅 AmCharts 文档

1.7. 图表使用示例

本章节展示如何使用图表展示扩展。

1.7.1. 配置应用程序项目

  1. 运行 CUBA Studio,按照 CUBA Studio 用户指南创建新项目 章节的描述创建一个新的项目,项目名称为 sampler

  2. 在 CUBA Studio 中打开 Project Properties : 点击 CUBA → Project Properties 主菜单项。在 App components 列表中添加 charts 应用程序组件。 Studio 如果建议重新创建 Gradle 脚本的时候,点确定。

  3. 点击 CUBABuild TasksDeploy 。这时, 应用程会被装配并部署到位于 build/tomcat 的 Tomcat 应用程序服务。

以上步骤完成后,展示图表的功能就可以使用了。

1.7.2. 使用简化的数据绑定 API 创建图表

作为第一个示例,我们将创建一个简单的图表,使用简化的数据绑定 API。

在界面添加图表组件然后使用 addData() 方法来填充数据,将带有一组键值对的 MapDataItem 实例作为参数传递给此方法:

<chart:pieChart id="pieChart"
                titleField="key"
                valueField="value"/>
@Inject
private PieChart pieChart;

@Subscribe
private void onBeforeShow(BeforeShowEvent event) {
    pieChart.addData(MapDataItem.of("key", "piece of apple pie",
                "value", 70),
            MapDataItem.of("key", "piece of blueberry pie",
                "value", 20),
            MapDataItem.of("key", "piece of cherry pie",
                "value", 10));
}
chart simple
Figure 25. 简单饼图

1.7.3. 使用实体创建图表

在本章节我们将创建类似 AmCharts 示例中 3D 重叠列图的图表。这个图将从数据库获取数据,所以必须要定义 dataContainer 属性。amCharts 用来定义这种图所使用的 JavaScript 如下:

var chart = AmCharts.makeChart("chartdiv", {
    "theme": "light",
    "type": "serial",
    "dataProvider": [{
        "country": "USA",
        "year2004": 3.5,
        "year2005": 4.2
    }, {
        "country": "UK",
        "year2004": 1.7,
        "year2005": 3.1
    }, {
        "country": "Canada",
        "year2004": 2.8,
        "year2005": 2.9
    }, {
        "country": "Japan",
        "year2004": 2.6,
        "year2005": 2.3
    }, {
        "country": "France",
        "year2004": 1.4,
        "year2005": 2.1
    }, {
        "country": "Brazil",
        "year2004": 2.6,
        "year2005": 4.9
    }, {
        "country": "Russia",
        "year2004": 6.4,
        "year2005": 7.2
    }, {
        "country": "India",
        "year2004": 8,
        "year2005": 7.1
    }, {
        "country": "China",
        "year2004": 9.9,
        "year2005": 10.1
    }],
    "valueAxes": [{
        "stackType": "3d",
        "unit": "%",
        "position": "left",
        "title": "GDP growth rate",
    }],
    "startDuration": 1,
    "graphs": [{
        "balloonText": "GDP grow in [[category]] (2004): <b>[[value]]</b>",
        "fillAlphas": 0.9,
        "lineAlpha": 0.2,
        "title": "2004",
        "type": "column",
        "valueField": "year2004"
    }, {
        "balloonText": "GDP grow in [[category]] (2005): <b>[[value]]</b>",
        "fillAlphas": 0.9,
        "lineAlpha": 0.2,
        "title": "2005",
        "type": "column",
        "valueField": "year2005"
    }],
    "plotAreaFillAlphas": 0.1,
    "depth3D": 60,
    "angle": 30,
    "categoryField": "country",
    "categoryAxis": {
        "gridPosition": "start"
    },
    "export": {
            "enabled": true
     }
});
1.7.3.1. 创建一个实体

创建一个 CountryGrowth 实体类。

  1. 在 CUBA 项目树的 Data Model 部分,单击 New → Entity。 将弹出 New CUBA Entity 对话框窗口。

  2. Entity name 字段中输入实体类的名称 – CountryGrowth,选择 Entity typeNot persistent,然后点击 OK 按钮。实体设计器界面将显示在工作区。

  3. 使用 Entity Designer 添加如下属性:

    • country,类型是 String

    • year2014,类型是 Double

    • year2015,类型是 Double

  4. 切换到 Text 标签页,这个标签页上显示 CountryGrowth 类的源代码。

    package com.company.sampler.entity;
    
    import com.haulmont.chile.core.annotations.MetaClass;
    import com.haulmont.chile.core.annotations.MetaProperty;
    import com.haulmont.cuba.core.entity.BaseUuidEntity;
    
    @MetaClass(name = "sampler_CountryGrowth")
    public class CountryGrowth extends BaseUuidEntity {
        @MetaProperty
        protected String country;
    
        @MetaProperty
        protected Double year2014;
    
        @MetaProperty
        protected Double year2015;
    
        public Double getYear2015() {
            return year2015;
        }
    
        public void setYear2015(Double year2015) {
            this.year2015 = year2015;
        }
    
        public Double getYear2014() {
            return year2014;
        }
    
        public void setYear2014(Double year2014) {
            this.year2014 = year2014;
        }
    
        public String getCountry() {
            return country;
        }
    
        public void setCountry(String country) {
            this.country = country;
        }
    }

    这个类是非持久化实体。这个类的一个实例包含一个国家 2014 和 2015 年的 GDP 增长率。

现在,CountryGrowth 实体类就创建完成了。

1.7.3.1.1. 界面 XML 描述

现在我们创建一个新界面来显示图表。

  1. 在项目树中选择 Generic UI,在右键菜单中点击 New → Screen。这时会出现模板浏览页面。

  2. 在可用模板列表中选择 Blank screen,再点击 Next

  3. Descriptor name 字段输入 column3d-chart 后点击 Next

  4. 打开 Text 标签页,使用下面的内容替换里面的值:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
        caption="msg://caption"
        messagesPack="com.company.sampler.web"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <data>
        <collection id="countryGrowthDc"
                    class="com.company.sampler.entity.CountryGrowth"
                    view="_local"/>
    </data>
    <layout>
        <chart:serialChart id="chart"
                           angle="30"
                           categoryField="country"
                           dataContainer="countryGrowthDc"
                           depth3D="60"
                           height="100%"
                           plotAreaFillAlphas="0.1"
                           startDuration="1"
                           width="100%">
            <chart:categoryAxis gridPosition="START"/>
            <chart:valueAxes>
                <chart:axis position="LEFT"
                            stackType="BOX_3D"
                            title="GDP growth rate"
                            unit="%"/>
            </chart:valueAxes>
            <chart:graphs>
                <chart:graph id="graph2014"
                             balloonText="GDP grow in [[category]] (2014): &lt;b&gt;[[value]]&lt;/b&gt;"
                             fillAlphas="0.9"
                             lineAlpha="0.2"
                             title="2014"
                             type="COLUMN"
                             valueField="year2014"/>
                <chart:graph id="graph2015"
                             balloonText="GDP grow in [[category]] (2015): &lt;b&gt;[[value]]&lt;/b&gt;"
                             fillAlphas="09."
                             lineAlpha="0.2"
                             title="2015"
                             type="COLUMN"
                             valueField="year2015"/>
            </chart:graphs>
            <chart:export/>
        </chart:serialChart>
    </layout>
</window>

界面描述的根元素包含了一个新的 xmlns:chart 属性:

<window xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
    ...
>

图表从 dataContainer 属性定义的 countryGrowthDc 数据容器获取数据。名称和数值使用 CountryGrowth 实体的 country, year2014year2015 属性来展示;这个实体的实例列表保存在数据容器中。

chart:serialChart 组件包含以下属性:

  • angle - 定义图表的角度。值可以从 090

  • balloonText - 定义鼠标浮动到一列时,提示框内的文字。这里可以使用以下标签:[[value]], [[title]], [[persents]], [[description]],以及 DataProvider 实例中列举的 DataItem 的键值,还能使用数据容器中实体属性的名称。如果要使用 html 标签,则需要转义。

  • depth3D - 图表的厚度。跟 angle 属性一起使用,可以帮助创建 3D 效果。

  • plotAreaFillAlphas - 绘图区域的透明度。

  • startDuration - 动画的持续时间,单位是秒。

  • categoryField - DataProvider 实例中列举的 DataItem 包含的键值对的键值;用来为分类轴定义标签。

chart:serialChart 组件包含下列元素:

  • chart:categoryAxis - 描述分类轴的元素。

    • gridPosition 属性指定网格线的摆放位置,在分类轴单元的中间还是起始处。

  • chart:valueAxes - 定义垂直轴数值的元素。在这里,只使用了一个垂直轴,通过 chart:axis 元素描述。

    • position 属性定义数值轴跟图表的相对位置。

    • 设置 stackTypeBOX_3D 可以让图表展示列柱一个在另一个后面。

  • chart:graphs - 包含 chart:graph 元素集合;图形是通过 chart:graph 元素进行描述。

    • type 属性定义图形类型:线图、列图、步进线图,平滑线图,烛台图以及蜡烛图。

    • valueField 属性定义 DataProvider 实例中列举的 DataItem 包含的键值对的键值;用来为定义数值。

    • fillAlphas 属性定义填充色的透明度。

    • lineAlpha 属性定义线(或者列边框)的透明度。值域: 0 - 1。

  • chart:export – 可选元素,用来启用图表导出。

1.7.3.1.2. 界面控制器

打开 Column3dChart 界面控制器,然后用下面的代码替换其内容:

package com.company.sampler.web;

import com.company.sampler.entity.CountryGrowth;
import com.haulmont.cuba.gui.model.CollectionContainer;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

@UiController("sampler_Column3dChart")
@UiDescriptor("column3d-chart.xml")
public class Column3dChart extends Screen {

    @Inject
    private CollectionContainer<CountryGrowth> countryGrowthDc;

    @Subscribe
    private void onInit(InitEvent event) {
        List<CountryGrowth> items = new ArrayList<>();
        items.add(countryGrowth("USA", 3.5, 4.2));
        items.add(countryGrowth("UK", 1.7, 3.1));
        items.add(countryGrowth("Canada", 2.8, 2.9));
        items.add(countryGrowth("Japan", 2.6, 2.3));
        items.add(countryGrowth("France", 1.4, 2.1));
        items.add(countryGrowth("Brazil", 2.6, 4.9));
        items.add(countryGrowth("Russia", 6.4, 7.2));
        items.add(countryGrowth("India", 8.0, 7.1));
        items.add(countryGrowth("China", 9.9, 10.1));
        countryGrowthDc.setItems(items);
    }

    private CountryGrowth countryGrowth(String country, double year2014, double year2015) {
        CountryGrowth cg = new CountryGrowth();
        cg.setCountry(country);
        cg.setYear2014(year2014);
        cg.setYear2015(year2015);
        return cg;
    }
}

onInit 方法为 countryGrowthDc 数据容器提供数据。


结果

现在我们看看创建的界面在真实应用程序中的外观。 选择 CUBA → Start application server

在登录窗口使用默认凭据登录。点击菜单 Application → Column3dChart ,你将看到类似下图的界面:

column3d chart
Figure 26. 3D 列图

1.7.4. 使用 DataProvider 提供的数据创建图表

这个图表通过控制器编码创建的 DataProvider 获取数据,所以并没有定义 dataContainer 属性。

1.7.4.1. 界面 XML 描述

现在我们创建一个新界面来显示图表。

  1. 在项目树中选择 Generic UI,在右键菜单中点击 New → Screen。这时会出现模板浏览页面。

  2. 在可用模板列表中选择 Blank screen,再点击 Next

  3. Descriptor name 字段输入 stackedarea-chart 后点击 Next

  4. 打开 Text 标签页,使用下面的内容替换里面的值:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
        caption="msg://caption"
        messagesPack="com.company.sampler.web"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
    <layout>
        <chart:serialChart id="chart"
                           categoryField="year"
                           height="100%"
                           marginLeft="0"
                           marginTop="10"
                           plotAreaBorderAlpha="0"
                           width="100%">
            <chart:chartCursor cursorAlpha="0"/>
            <chart:legend equalWidths="false"
                          periodValueText="total: [[value.sum]]"
                          position="TOP"
                          valueAlign="LEFT"
                          valueWidth="100"/>
            <chart:valueAxes>
                <chart:axis gridAlpha="0.07"
                            position="LEFT"
                            stackType="REGULAR"
                            title="Traffic incidents"/>
            </chart:valueAxes>
            <chart:graphs>
                <chart:graph fillAlphas="0.6"
                             hidden="true"
                             lineAlpha="0.4"
                             title="Cars"
                             type=""
                             valueField="cars"/>
                <chart:graph fillAlphas="0.6"
                             lineAlpha="0.4"
                             title="Motorcycles"
                             valueField="motorcycles"/>
                <chart:graph fillAlphas="0.6"
                             lineAlpha="0.4"
                             title="Bicycles"
                             valueField="bicycles"/>
            </chart:graphs>
            <chart:categoryAxis axisColor="#DADADA"
                                gridAlpha="0.07"
                                startOnAxis="true"/>
            <chart:export/>
        </chart:serialChart>
    </layout>
</window>

界面描述的根元素包含了一个新的 xmlns:chart 属性:

<window xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
    ...
>

chart:serialChart 属性:

  • categoryField - DataProvider 实例中列举的 DataItem 包含的键值对的键值;用来为分类轴定义标签。

chart:serialChart 的元素:

  • chart:chartCursor - 可选元素,在图表中添加一个光标。光标跟随鼠标指针并且以提示窗的方式显示图表中相应点的数据。

    • cursorAlpha 属性定义光标线的透明度。

  • chart:legend - 定义图表图例的元素。

    • position 属性定义图例相对于图表的位置。

    • equalWidths 属性指定是否每个图例条目要跟最宽的那个等宽。

    • periodValueText 属性定义在图例中作为值部分显示的文字,当用户的鼠标指针没有悬浮在任何数据点时显示。这个文字标签需要由两部分构成 - 字段名称(值/开盘/收盘/最高/最低)以及该时间段需要显示的值 - 开盘/收盘/最高/最低/总和/平均/计数。

    • valueAlign 属性定义值文字的对其方式。可能值:leftright

    • valueWidth 属性定义值文字的宽度。

  • chart:valueAxes - 定义垂直轴数值的元素。在这里,只使用了一个垂直轴,通过 chart:axis 元素描述。

    • position 属性定义数值轴跟图表的相对位置。

    • title 属性定义数值轴标题。

    • 设置 stackTypeREGULAR 可以让图表展示滚动值,将该属性设置为 none 则显示非滚动值。

    • gridAlpha 定义网格线的透明度。

  • chart:graphs - 包含 chart:graph 元素集合;图形是通过 chart:graph 元素进行描述。

    • type 属性定义图形类型:线图、列图、步进线图,平滑线图,烛台图以及蜡烛图。

    • valueField 属性定义 DataProvider 实例中列举的 DataItem 包含的键值对的键值;用来为定义数值。

    • fillAlphas 属性定义填充色的透明度。

    • lineAlpha 属性定义线(或者列边框)的透明度。值域: 0 - 1。

    • hidden 属性指定图形是否隐藏

  • chart:categoryAxis - 描述分类轴的元素。

    • 设置 startOnAxistrue 会导致直接挨着数值轴开始绘制图表。该属性的默认值是 false,此时,在数值轴和图表之间会有个小间隔。

    • gridAlpha 定义网格线的透明度。

    • axisColor 属性定义轴的颜色。

  • chart:export – 可选元素,用来启用图表导出。

1.7.4.2. 界面控制器

打开 StackedareaChart 界面控制器,然后用下面的代码替换其内容:

package com.company.sampler.web;

import com.haulmont.charts.gui.components.charts.SerialChart;
import com.haulmont.charts.gui.data.DataItem;
import com.haulmont.charts.gui.data.ListDataProvider;
import com.haulmont.charts.gui.data.MapDataItem;
import com.haulmont.cuba.gui.screen.Screen;
import com.haulmont.cuba.gui.screen.Subscribe;
import com.haulmont.cuba.gui.screen.UiController;
import com.haulmont.cuba.gui.screen.UiDescriptor;

import javax.inject.Inject;

@UiController("sampler_StackedareaChart")
@UiDescriptor("stackedarea-chart.xml")
public class StackedareaChart extends Screen {
    @Inject
    private SerialChart chart;

    @Subscribe
    private void onInit(InitEvent event) {
        ListDataProvider dataProvider = new ListDataProvider();
        dataProvider.addItem(transportCount(1994, 1587, 650, 121));
        dataProvider.addItem(transportCount(1995, 1567, 683, 146));
        dataProvider.addItem(transportCount(1996, 1617, 691, 138));
        dataProvider.addItem(transportCount(1997, 1630, 642, 127));
        dataProvider.addItem(transportCount(1998, 1660, 699, 105));
        dataProvider.addItem(transportCount(1999, 1683, 721, 109));
        dataProvider.addItem(transportCount(2000, 1691, 737, 112));
        dataProvider.addItem(transportCount(2001, 1298, 680, 101));
        dataProvider.addItem(transportCount(2002, 1275, 664, 97));
        dataProvider.addItem(transportCount(2003, 1246, 648, 93));
        dataProvider.addItem(transportCount(2004, 1318, 697, 111));
        dataProvider.addItem(transportCount(2005, 1213, 633, 87));
        dataProvider.addItem(transportCount(2006, 1199, 621, 79));
        dataProvider.addItem(transportCount(2007, 1110, 210, 81));
        dataProvider.addItem(transportCount(2008, 1165, 232, 75));
        dataProvider.addItem(transportCount(2009, 1145, 219, 88));
        dataProvider.addItem(transportCount(2010, 1163, 201, 82));
        dataProvider.addItem(transportCount(2011, 1180, 285, 87));
        dataProvider.addItem(transportCount(2012, 1159, 277, 71));

        chart.setDataProvider(dataProvider);
    }

    private DataItem transportCount(int year, int cars, int motorcycles, int bicycles) {
        MapDataItem item = new MapDataItem();
        item.add("year", year);
        item.add("cars", cars);
        item.add("motorcycles", motorcycles);
        item.add("bicycles", bicycles);
        return item;
    }
}

onInit 方法将数据作为滚动值提交给了图表。这种类型的图表展示各部分在总和中的占比。


结果

现在我们看看创建的界面在真实的应用程序中的外观。选择 CUBA → Start application server

在登录窗口使用默认凭据登录。 点击 Application → StackedareaChart 菜单,将看到下图:

stackedarea chart
Figure 27. 堆积面积图

1.7.5. 创建带增量数据更新的图表

这种图表从 数据容器 中获取数据,并且数据会被自动更新。当数据容器中添加了新数据时,图表没有完全刷新:数据点每两秒添加一次。此方法非常有用,比如,用来创建自动更新的仪表板。

在此例中,我们将使用 Sales 示例应用程序中的 Order 实体来显示新订单金额的动态。

  1. 下载 Sales 应用程序然后按照 配置应用程序项目 的描述添加 charts 组件。

  2. 用 Studio 创建一个新界面,名称为 orders-history,因为要用这个界面来显示新订单条目的历史仪表板。

  3. 在界面布局中添加 serialChart 组件。要使用增量数据更新功能,我们必须创建一个 CollectionContainer 类型的 数据容器 ,并将图表与其绑定。在此示例中,我们不会从数据窗口加载数据,而是动态生成示例数据,因此您无需创建加载器(loader)。

    设置分类轴的 date 属性和数值轴的 amount 属性。

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
            caption="msg://caption"
            messagesPack="com.company.sales.web"
            xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd">
        <data>
            <collection id="ordersDc"
                        class="com.company.sales.entity.Order"
                        view="_local"/>
        </data>
        <layout>
            <chart:serialChart id="orderHistoryChart"
                               categoryField="date"
                               dataContainer="ordersDc"
                               width="100%">
                <chart:graphs>
                    <chart:graph valueField="amount"/>
                </chart:graphs>
            </chart:serialChart>
        </layout>
    </window>
  4. 我们将使用 定时器 动态更新图表,定时器是一个特殊的 UI 组件,能将 HTTP 请求发送到服务端。

    • 切换到 Designer 标签页。

    • 在工具箱的 Non-visual components 分组中选择 Timer 组件。

    • 将这个组件拖放到界面组件树(hierarchy)面板。

    • 在界面组件树中选中 timer 组件,然后切换到属性面板的 Properties 标签页。

    • 给 timer 组件设置 id.

    • 比如说数据需要两秒更新一次, 那么我们设置 delay 属性为 2000 毫秒.

    • onTimer 定义 java 方法名为 updateChart 。这个方法在 timer 组件每次触发事件时都会调用。 点击 >> 按钮可以在控制器中生成这个方法。

    • 选中 repeatingautostart 复选框。

      chart incremental update
      Figure 28. 创建定时器
  5. 在处理定时器逻辑之前,先注入必要的依赖:timeSourcemetadata 和数据源实例。每次触发定时器事件时,我们都会生成一个带有随机价格的新 Order 实例。使用 includeItem() 方法将新实例添加到数据源。

  6. 切换到 OrdersHistory 控制器。在开始实现定时器逻辑之前, 注入必要的依赖: timeSourcemetadataOrder 实体的数据容器。 每次触发计时器事件时,我们会生成一个新的 Order 实例,这个实例的 amount 属性使用一个随机值。 使用 getMutableItems().add() 方法将新实例添加到集合数据容器中。

    使用跟创建随机 Order 实例相同的逻辑在 onInit() 方法中初始化图表。

    package com.company.sales.web;
    
    import com.company.sales.entity.Order;
    import com.haulmont.cuba.core.global.Metadata;
    import com.haulmont.cuba.core.global.TimeSource;
    import com.haulmont.cuba.gui.components.Timer;
    import com.haulmont.cuba.gui.model.CollectionContainer;
    import com.haulmont.cuba.gui.screen.*;
    
    import javax.inject.Inject;
    import java.math.BigDecimal;
    import java.util.Random;
    
    @UiController("sales_OrdersHistory")
    @UiDescriptor("orders-history.xml")
    public class OrdersHistory extends Screen {
        @Inject
        private Metadata metadata;
        @Inject
        private TimeSource timeSource;
    
        @Inject
        private CollectionContainer<Order> ordersDc;
    
        private Random random = new Random(42);
    
        @Subscribe
        private void onInit(InitEvent event) {
            Order initialValue = metadata.create(Order.class);
            initialValue.setAmount(new BigDecimal(random.nextInt(1000) + 100));
            initialValue.setDate(timeSource.currentTimestamp());
    
            ordersDc.getMutableItems().add(initialValue);
        }
    
    
    
        public void updateChart(Timer source) {
            Order orderHistory = metadata.create(Order.class);
            orderHistory.setAmount(new BigDecimal(random.nextInt(1000) + 100));
            orderHistory.setDate(timeSource.currentTimestamp());;
            ordersDc.getMutableItems().add(orderHistory);
        }
    }

    此时,图表已经能正常运行,但是数据容器中的实例数量会快速增加,所以我们需要限制显示的条目数量。

    chart incremental update 2
    Figure 29. 数据每两秒自动更新
  7. 创建 Queue 的队列。每次触发定时器事件时,生成的条目都会添加到 itemsQueue 的顶端。当队列大小超过 10 个时,删掉最早创建的条目。

    private Queue<Order> itemsQueue = new LinkedList<>();
    public void updateChart(Timer source) {
        Order orderHistory = metadata.create(Order.class);
        orderHistory.setAmount(new BigDecimal(random.nextInt(1000) + 100));
        orderHistory.setDate(timeSource.currentTimestamp());;
        ordersDc.getMutableItems().add(orderHistory);
    
        itemsQueue.add(orderHistory);
    
        if (itemsQueue.size() > 10) {
            Order item = itemsQueue.poll();
            ordersDc.getMutableItems().add(item);
        }
    }

结果

所有数据都会以递增方式发送到浏览器。如果在 Chrome 开发者控制台打开 Network 标签页,则会看到我们的网页每 2 秒向后端发送一个 HTTP 请求,并且后端会响应这个请求送回非常小的 JSON 消息。JSON 包含对价格值的一个 addremove 操作。所以,并没有重新发送所有数据。

chart incremental update 3
Figure 30. 图表每次只显示 10 条记录

1.7.6. 使用事件

这里看看事件的使用。我们将在界面控制器中创建的界面中添加对图形项目点击事件的处理。在 IDE 中打开界面控制器并且注入图表。为了显示通知,在界面控制器中注入了 Notifications bean。

@Inject
private Notifications notifications;

@Inject
private SerialChart serialChart;

然后在 onInit 方法的最后添加一个监听器。当图表从 DataProvider 接收数据时,可以使用 getDataItemNN() 方法来取到点击的条目。在此示例中,SerialChart 组件绑定了数据容器,因此应使用另一种方法获取点击的条目:getEntityNN()

@Subscribe
private void onInit(InitEvent event) {
    chart.addGraphItemClickListener(graphItemClickEvent ->
            notifications.create()
                    .withCaption(itemClickEventInfo(graphItemClickEvent))
                    .withContentMode(ContentMode.HTML)
                    .show());
}

private String itemClickEventInfo(Chart.GraphItemClickEvent event) {
    CountryGrowth countryGrowth = (CountryGrowth) event.getEntityNN();
    return String.format("GDP grow in %s (%s): %.1f%%",
            countryGrowth.getCountry(),
            event.getGraphId().substring(5),
            "graph2014".equals(event.getGraphId()) ? countryGrowth.getYear2014() : countryGrowth.getYear2015());
}

查看结果,使用 RunRestart application server 重新构建项目然后登录系统。打开界面点击其中一列。

chart with event
Figure 31. 处理图形条目点击事件的图表

1.7.7. 使用 JSON 配置

需要配置图表,除了使用 XML 属性之外,还可以使用 AmCharts 文档 描述的自定义 JSON。

比如,对于 serialChart:

<chart:serialChart id="serialChart">
    <chart:valueAxes>
        <chart:axis axisAlpha="0" position="LEFT" title="Incidents"/>
    </chart:valueAxes>
    <chart:graphs>
        <chart:graph id="g1" bullet="ROUND" type="COLUMN" valueField="value"/>
    </chart:graphs>
    <chart:categoryAxis position="TOP" title="Time" labelsEnabled="false"/>
</chart:serialChart>

这个图有些数据:

@Inject
private SerialChart serialChart;

@Subscribe
private void onInit(InitEvent event) {
    ListDataProvider serialChartDataProvider = new ListDataProvider();
    int[] serialChartData = {5, 7, 6, 9, 7, 8, 5, 6, 4, 6, 5, 7, 4, 5, 3, 4, 2, 0};

    for (int i = 0; i < serialChartData.length; i++) {
        serialChartDataProvider.addItem(graphData(serialChartData[i]));
    }

    serialChart.setDataProvider(serialChartDataProvider);
}

private DataItem graphData(int value) {
    MapDataItem item = new MapDataItem();
    item.add("value", value);
    return item;
}
chart custom json
Figure 32. 序列图

现在可以改变图表的配置。举个例子,给图表添加一个标题:

serialChart.setNativeJson("{\n" +
        " \"titles\": [\n" +
        " {\n" +
        " \"size\": 15,\n" +
        " \"text\": \"Chart Title\"\n" +
        " }\n" +
        " ]\n" +
        "}");
chart custom json title
Figure 33. 带有 JSON 标题的序列图

也可以在 XML 设置 JSON 配置:

<chart:serialChart id="serialChart">
    <chart:nativeJson>
        <![CDATA[
        {
            "titles": [
                {
                    "size": 15,
                    "text": "Chart Title"
                }
            ]
        }
        ]]>
    </chart:nativeJson>
    <chart:valueAxes>
        <chart:axis axisAlpha="0" position="LEFT" title="Incidents"/>
    </chart:valueAxes>
    <chart:graphs>
        <chart:graph id="g1" bullet="ROUND" type="COLUMN" valueField="value"/>
    </chart:graphs>
    <chart:categoryAxis position="TOP" title="Time" labelsEnabled="false"/>
</chart:serialChart>

1.8. 更换 AmCharts 版本

CUBA 框架包含的 AmCharts 库实例可以用另外一个版本替换。步骤:

  1. AmCharts 网站 下载图表和股票图。

  2. 将两个图表的 amcharts 目录合并成一个。

  3. 拷贝 amcharts 目录至 {project.rootDir}/modules/web/web/VAADIN/webjars

  4. 重新部署应用程序。

如果需要使用新版本带的新属性,需要按照下面的方式在界面控制器中设置自定义的 JSON。

chart.setNativeJson("{\"valueScrollbar\":{\"autoGridCount\":true}}");

2. 地图展示

CUBA 框架的地图展示子系统是基于集成第三方地图服务提供商。目前,只支持 Google 地图

2.1. 地图展示功能

  • 能响应的事件:

    • 鼠标点击。

    • 地图平移和缩放。

    • 点击和拖动标记。

    • 关闭弹窗。

    map demo click
    Figure 34. 地图
  • 添加标记。标记可以是固定位置的或者允许用户拖动的。标记可以处理鼠标点击事件并且发送相应的事件到界面代码中。

    map demo marker
    Figure 35. 地图标记
  • 展示折线和多边形。

    map demo polygon display
    Figure 36. 折线
  • 绘制多边形。

    map demo polygon draw
    Figure 37. 多边形
  • 热力图渲染。

    map demo heatmap
    Figure 38. 热力图

2.2. 配置应用程序项目

为了在应用程序中展示地图,需要添加 charts 应用程序组件,正如在图表展示扩展中描述的。另外,需要为 Web Client block 定义下列应用程序属性:

  • 需要定义下列参数之一(这些参数的详细信息,可以参阅 Google 地图 API 文档 ):

    • charts.map.apiKey – 浏览器的 API 键值。

    • charts.map.clientId – 客户端 ID。

  • 可选参数:

    • charts.map.defaultZoom – 地图的默认缩放级别。

    • charts.map.defaultLatitude – 默认地图中心的纬度。

    • charts.map.defaultLongitude – 默认地图中心的经度。

    • charts.map.apiVersion – 定义使用 Google Map API 的版本。默认值是 3.35。可以设置该属性为 3,这样能用上一个发行版的 API。或者设置成 3.exp,如果需要使用实验版。访问官方 Google Maps API 文档 了解更多信息。

web-app.properties 文件示例:

charts.map.apiKey = my_key
charts.map.defaultZoom = 13.0
charts.map.defaultLatitude = 51.5001
charts.map.defaultLongitude = -0.1262

2.3. MapViewer 组件

可以使用 com.haulmont.charts.gui.components.map.MapViewer 组件在应用程序界面中展示地图。

要添加该组件,需要在界面 XML 描述的根元素中声明 chart 命名空间:

<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        xmlns:chart="http://schemas.haulmont.com/charts/charts.xsd"
        ...>

该组件的 XML 名称: mapViewer。组件声明示例:

<layout>
    <vbox id="mapBox" height="100%">
        <chart:mapViewer id="map" width="100%" height="100%"/>
    </vbox>
</layout>

可以在界面 XML 描述中定义以下组件参数:

  • id, width, height - 标准组件属性。

  • mapType - 对应于 MapViewer.Type 选项的地图类型: roadmap, satellite, hybrid, terrain。默认值 roadmap

  • vendor - 地图服务提供商。目前只支持唯一的值: google

地图及其组件的主要配置都在界面控制器中进行,只需要注入在 XML 描述中声明的组件:

@Inject
private MapViewer map;

@Subscribe
protected void onInit(InitEvent event) {
    GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
    map.setCenter(center);
}
地图配置方法
  • fitToBounds() – 设置最小地图缩放比例为:足以展示由东北和西南坐标限制的区域的最小比例。

  • removePolygonVertex() – 删除多边形的顶点。

  • setCenter() – 设置地图中心点。

  • setCenterBoundLimits() – 设置地图中心可能位置的边界。

  • setCenterBoundLimitsEnabled() – 设置是否应限制地图中心的可能位置。

  • setDraggable() – 启用/禁用地图拖动模式。

  • setKeyboardShortcutsEnabled() – 启用/禁用键盘快捷键。

  • setMapType() – 定义地图类型。

  • setMaxZoom() – 设置最大可用的地图缩放级别。

  • setMinZoom() – 设置最小可用的地图缩放级别。

  • setRemoveMessage() – 设置删除顶点的弹出消息。

  • setScrollWheelEnabled() – 启用/禁用使用鼠标滚轮缩放地图。

  • setVertexRemovingEnabled() – 切换是否可以做顶点删除。

  • setVisibleAreaBoundLimits() – 设置地图可见区域的边界。

  • setVisibleAreaBoundLimitsEnabled() – 启用可见区域限制模式。

  • setZoom() – 设置地图缩放级别。

地图组件接口

下列接口可以在 com.haulmont.charts.gui.map.model 包找到。

  • Circle - 展示一个圆圈的组件。Circle 属性跟 Polygon 属性类似,除了圆需要用两个额外的属性来定义形状:中心(GeoPoint)和半径。可以使用 MapViewer 接口的 createCircle()addCircle() 方法来创建此对象并放置在地图上。

    Circle circle = map.createCircle(center, 130.5);
    circle.setDraggable(true);;
    circle.setFillOpacity(0.5);
    map.addCircleOverlay(circle);
    Circle
    Figure 39. 地图中的圆
  • DrawingOptions - 辅助绘图组件。目前仅支持绘制多边形。可以通过给 MapViewer 传递 DrawingOptions 的实例来启用绘图模式。示例:

    DrawingOptions options = new DrawingOptions();
    PolygonOptions polygonOptions = new PolygonOptions(true, true, "#993366", 0.6);
    ControlOptions controlOptions = new ControlOptions(
    Position.TOP_CENTER, Arrays.asList(OverlayType.POLYGON));
    options.setEnableDrawingControl(true);
    options.setPolygonOptions(polygonOptions);
    options.setDrawingControlOptions(controlOptions);
    options.setInitialDrawingMode(OverlayType.POLYGON);
    map.setDrawingOptions(options);
  • GeoPoint - 辅助组件,未在地图上显示。可以使用此组件将地图参数设置为中心点、边界或用来创建更复杂的地图组件。可以使用 MapViewer 接口的 createGeoPoint() 方法创建该对象。示例:

    GeoPoint center = map.createGeoPoint(53.490905, -2.249558);
    map.setCenter(center);
  • HeatMapLayer - 展示热力图的地图图层,用于显示不同地理位置的数据密度分布,不同数据密度用不同颜色突出显示。默认情况下,密度较高的区域显示为红色,密度较低的区域显示为绿色。可以使用 MapViewer 接口的 createHeatMapLayer()addHeatMapLayer() 方法来创建此对象并放置在地图上。示例:

    HeatMapLayer heatMapLayer = map.createHeatMapLayer();
    List<GeoPoint> data = new ArrayList<>();
    data.add(map.createGeoPoint(53.450, -2.090));
    data.add(map.createGeoPoint(53.451, -2.095));
    data.add(map.createGeoPoint(53.452, -2.092));
    data.add(map.createGeoPoint(53.453, -2.093));
    data.add(map.createGeoPoint(53.454, -2.093));
    data.add(map.createGeoPoint(53.454, -2.092));
    data.add(map.createGeoPoint(53.453, -2.092));
    heatMapLayer.setData(data);
    map.addHeatMapLayer(heatMapLayer);
    HeatMap
    Figure 40. 热力图层

    可以独立使用 setData() 方法更改热图层的数据。这个改动不需要将图层重新添加到地图中。

  • InfoWindow - 在弹窗中显示信息的地图组件。可以使用 MapViewer 接口的 createInfoWindow()openInfoWindow() 方法来创建此对象并放置在地图上。示例:

    InfoWindow w = map.createInfoWindow("Some text");
    map.openInfoWindow(w);

    信息窗口可以绑定到标记上,示例:

    map.addMarkerClickListener(event -> {
        Marker marker = event.getMarker();
        String caption = String.format("Marker clicked: %.2f, %.2f",
                marker.getPosition().getLatitude(),
                marker.getPosition().getLongitude());
        InfoWindow w = map.createInfoWindow(caption, marker);
        map.openInfoWindow(w);
    });
    InfoWindow
    Figure 41. 信息窗口
  • Label - 在地图上显示文本标签的组件。

    可以使用 MapViewer 接口的 createLabel()addLabel() 方法来创建 Label 对象并放置在地图上。标签可以使用 removeLabel() 按顺序删除。样式方面,标签支持 HTML 标签。

    Label 组件有下列属性:

    • value - 标签的字符串值。如果标签内容类型设置为 HTML,则浏览器将解析标签值。

    • position - GeoPoint 的实现,表示标签的地理位置。

    • contentType - 设置标签是否可以作为 HTML 来解析。可以有两个可能值: PLAIN_TEXTHTML

    • adjustment - 设置标签相对于 GeoPoint 位置标记的调整位置。

    • styleName - 为标签设置额外的样式名称。

      Label label = map.createLabel();
      label.setValue("<span style=\"color: #ffffff; font-size: 24px;\">White label</span>");
      label.setPosition(map.createGeoPoint(42.955, 32.883));
      label.setAdjustment(Label.Adjustment.BOTTOM_CENTER);
      label.setContentType(Label.ContentType.HTML);
      map.addLabel(label);
      MapLabel
      Figure 42. 地图标签
  • Marker - 标记地图上位置的组件。默认情况下,使用地图服务供应商的标准图标。可以使用 MapViewer 接口的 createMarker()addMarker() 方法来创建此对象并放置在地图上。示例:

    Marker marker = map.createMarker("My place", map.createGeoPoint(53.590905, -2.249558), true);
    marker.setClickable(true);
    map.addMarker(marker);

    clearMarkers() 方法顺序删除地图上的所有标记。

    MarkerImage 接口用来设置标记图标或者阴影图像。

    MarkerImage markerImage = map.createMarkerImage("https://www.cuba-platform.com/sites/logo.png");
    GeoPoint center = map.createGeoPoint(21.11, -76.20);
    markerImage.setSize(map.createSize(44, 44));
    markerImage.setOrigin(map.createPoint(0, 0));
    markerImage.setAnchor(map.createPoint(-5, 50));
    Marker marker = map.createMarker("Cuba", center, true, markerImage);
    map.addMarker(marker);
    MarkerImage
    Figure 43. 标记图片
  • Polyline - 展示折线的组件。可以使用 MapViewer 接口的 createPolyline()addPolyline() 方法来创建此对象并放置在地图上。示例:

    List<GeoPoint> coordinates = new ArrayList<>();
    coordinates.add(map.createGeoPoint(53.4491, -1.9955));
    coordinates.add(map.createGeoPoint(53.6200, -1.9539));
    coordinates.add(map.createGeoPoint(53.4425, -1.6196));
    coordinates.add(map.createGeoPoint(53.1900, -1.4969));
    coordinates.add(map.createGeoPoint(53.1926, -1.6197));
    Polyline polyline = map.createPolyline(coordinates);
    polyline.setStrokeWeight(5);
    polyline.setStrokeOpacity(0.5);
    polyline.setStrokeColor("#7341f4");
    map.addPolyline(polyline);
    Polyline
    Figure 44. 折线
  • Polygon - 展示多边形的组件。可以使用 MapViewer 接口的 createPolygon()addPolygonOverlay() 方法来创建此对象并放置在地图上。示例:

    List<GeoPoint> coordinates = new ArrayList<>();
    coordinates.add(map.createGeoPoint(48.560579, 7.767876));
    coordinates.add(map.createGeoPoint(48.561386, 7.782791));
    coordinates.add(map.createGeoPoint(48.541940, 7.782861));
    coordinates.add(map.createGeoPoint(48.545641, 7.768749));
    Polygon p = map.createPolygon(coordinates, "#9CFBA9", 0.6, "#2CA860", 1.0, 2);
    map.addPolygonOverlay(p);
    Polygon
    Figure 45. 多边形
事件监听器

下列监听器都在 com.haulmont.charts.gui.map.model.listeners 包内。

  • CircleCenterChangeListener - 用户在地图编辑模式改变了圆的中心点位置。

  • CircleCompleteListener - 用户在地图编辑模式创建了一个圆。

  • CircleRadiusChangeListener - 用户在地图编辑模式改变了一个圆的半径。

  • InfoWindowClosedListener - 用户关闭了一个信息窗口。

  • MapInitListener - 地图初始化完成。在加载所有图层并且所有坐标可用时,第一次加载地图后调用此监听器一次。

  • MapMoveListener - 用户鼠标按下并且拖动了地图。

  • MarkerDragListener - 用户拖动了一个标记。

  • PolygonCompleteListener - 用户在地图编辑模式创建了一个多边形。

  • PolygonEditListener - 用户编辑了多边形(移动或者添加顶点)。

左键点击事件:

  • CircleClickListener - 用户点击圆。

  • MapClickListener - 用户点击地图。

  • MarkerClickListener - 用户点击标记。

  • PolygonClickListener - 用户点击多边形。

右键点击事件:

  • CircleRightClickListener - 用户右键点击圆。

  • MapRightCLickListener - 用户右键点击地图。

  • MarkerRightClickListener - 用户右键点击标记。

  • PolygonRightClickListener - 用户右键点击多边形。

双击事件监听器:

  • MarkerDoubleClickListener - 用户双击标记。

  • CircleDoubleClickListener - 用户双击圆。

想了解更多关于地图方法和参数的细节信息,请参阅相应的 JavaDoc 文档。

3. 透视表展示

PivotTable 是带有拖拽功能的表格组件,通过这个组件可以将数据集合转换成汇总表格,并且可以通过 2D 的拖拽 UI 来操作。在 CUBA Studio 组件库就可以使用该组件的全部功能。

PivotTable 基于外部 JavaScript 库 - https://github.com/nicolaskruchten/pivottable 。可以在作者的网站上找到更多关于 PivotTable 的例子: http://nicolas.kruchten.com/pivottable/examples/

该组件的 XML 名称: pivotTable

实现该组件的 block: Web Client

PivotTable 1
Figure 46. 透视表

在界面 XML 描述中定义组件的示例:

<chart:pivotTable id="tipsPivotTable"
                  dataContainer="tipsDc"
                  renderer="HEATMAP">
    <chart:properties>
        <chart:property name="row"/>
        <chart:property name="totalBill"/>
        <chart:property name="tip"/>
        <chart:property name="sex"/>
        <chart:property name="smoker"/>
        <chart:property name="day"/>
        <chart:property name="time"/>
        <chart:property name="size"/>
    </chart:properties>
    <chart:aggregation mode="SUM_OVER_SUM">
        <chart:property name="tip"/>
        <chart:property name="totalBill"/>
    </chart:aggregation>
    <chart:rows>
        <chart:row value="sex"/>
        <chart:row value="smoker"/>
    </chart:rows>
    <chart:columns>
        <chart:column value="day"/>
        <chart:column value="time"/>
    </chart:columns>
    <chart:sortersFunction>
        function(attr){
            if(attr=="Day"){
                return $.pivotUtilities.sortAs(["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]);
            }
        }
    </chart:sortersFunction>
</chart:pivotTable>
pivotTable 的 XML 元素
  • properties - 在 pivotTable 内使用的一组属性的键值对映射。键是 数据容器 中属性的名称,值是本地化语言的名称。

  • derivedProperties - 可以向原始数据容器添加新属性,这些属性派生自现有属性。此元素是键值映射,其中键是生成的属性的名称,值是生成此属性的 JavaScript 函数。

    • 元素内部的 derivedProperty 元素需要定义 caption 属性,此属性的值会被当作键值使用。

    • function 元素用作 derivedProperty 的值。

  • hiddenProperties - 不需要展示在 UI 的一组属性。该属性值可以设置为 properties 的键值或者生成属性的名称(比如 derivedProperties 的键值)。该属性只对可编辑的 pivotTable 有效。

  • hiddenFromAggregations - 不在聚合参数下拉框显示的一组属性。

  • hiddenFromDragDrop - 不在 UI 可拖拽部分显示的一组属性。

  • columns - 作为表格列使用的一组属性。该属性值可以设置为 properties 的键值或者生成属性的名称。

  • columnOrder - 渲染时列数据的展示顺序。

  • rows - 作为表格行使用的一组属性。该属性值可以设置为 properties 的键值或者生成属性的名称。

  • rowOrder - 渲染时行数据的展示顺序。

  • exclusions - 键值映射,其中键是属性的名称(properties 键值或生成的属性的名称),值是要在渲染时不包括的属性值列表。该属性只对可编辑的 pivotTable 有效。

  • inclusions - 键值映射,其中键是属性的名称(properties 键值或生成的属性的名称),值是要渲染的属性值列表。该属性只对可编辑的 pivotTable 有效。

  • filterFunction - JavaScript 函数,用来做过滤。

  • renderers - 定义渲染函数的集合,这个集合需要在 UI 中的可用渲染器列表中显示。

    • default 属性用来设置一个默认的渲染器。当组件加载时,选择的渲染器会当作默认的使用。

    • 内部的 renderer 元素可以使用 type 属性来设置一个预制的渲染器:AREA_CHARTBAR_CHARTCOL_HEATMAPHEATMAPHORIZONTAL_BAR_CHARTHORIZONTAL_STACKED_BAR_CHARTLINE_CHARTROW_HEATMAPSCATTER_CHARTSTACKED_BAR_CHARTTABLE_BAR_CHARTTABLETREEMAPTSV_EXPORT

      只对可编辑的 pivotTable 有效。

  • rendererOptions - 定义渲染器的参数。事实上只有两种渲染器能做自定义设置:

    • 全部的 heatmap 渲染器。可以通过 JavaScript 代码设置热力单元的颜色。

    • 全部的 chart 渲染器。参数可以用来设置图表的大小。

  • sortersFunction - JavaScript 函数,用来做行和列标题的排序。

pivotTable 的聚合(aggregation)属性
  1. aggregation - 设置聚合函数,用来对每个单元格的结果做聚合。

    aggregation 属性:

    • mode 属性可以用来设置一个预制的聚合函数。

    • caption 展示在 UI 的本地化语言的描述。

    • custom - 如果是 true,那么会忽略 mode 的值,而使用 内部的 function 元素指定的 JavaScript 函数。

      aggregation 元素:

    • function - 包含聚合函数的 JavaScript 代码。

    • property - 用来作为聚合函数输入参数的属性列表。该属性值可以设置为 properties 的键值或者生成属性的名称。只对非可编辑 pivotTable 有效。

      示例:

      <chart:aggregation mode="SUM_OVER_SUM" custom="true">
          <chart:property name="tip"/>
          <chart:property name="Total Bill"/>
      </chart:aggregation>
  1. aggregationProperties - 定义在聚合器的下拉列表中显示的属性列表。该属性值可以设置为 properties 的键值或者生成属性的名称。只对可编辑 pivotTable 有效。

    <chart:aggregationProperties>
        <chart:property name="tip"/>
        <chart:property name="totalBill"/>
    </chart:aggregationProperties>
  1. aggregations - 定义需要在 UI 中的可用聚合器下拉列表中显示的聚合器集合。

    aggregations 属性:

    • default 属性用来设置一个预制的聚合函数。当组件加载时,选择的函数会当作默认的使用。

    • 内部的 aggregation 元素跟 aggregation 的使用方法相同,除了内部的 property 元素。只对可编辑 pivotTable 有效。

      示例:

      <chart:aggregations default="COUNT">
          <chart:aggregation caption="Count"/>
          <chart:aggregation mode="SUM_OVER_SUM"/>
      </chart:aggregations>
pivotTable 属性
  • dataContainer - 设置在界面 XML 描述的 data 部分定义的一个数据容器。必须是 collectionDataContainer 类型。

  • editable - 如果是 true,UI 会显示用来操作数据的元素,否则只显示数据。

  • menuLimit - 设置双击显示菜单的最大条目数量。如果记录数大于这个值,则会显示相应的信息。只对可编辑 pivotTable 有效。

  • renderer - 启用设置一个预制的数据渲染器。只对非可编辑 pivotTable 有效。

  • showColTotals - 定义是否显示列总数。默认值是 true。只对表格渲染器有效。

  • showRowTotals - 定义是否显示行总数。默认值是 true。只对表格渲染器有效。

  • showUI - 控制是否在可编辑的透视表中显示 UI 元素。默认值是 true

  • autoSortUnusedProperties - 定义在 UI 是否要对没使用的属性进行排序。只对可编辑 pivotTable 有效。

  • unusedPropertiesVertical - 定义没使用的属性是要以垂直方式展示(true)还是以水平方式展示(false 或者默认情况)。如果设置为数字,那么当属性名称的字符组合长度超过此数字,则属性将垂直显示。

pivotTable 的监听器
  • addCellClickListener - 为 PivotTable 添加单元格点击事件监听器。CellClickEvent 事件只在表格渲染器会触发(TABLE, HEATMAP, TABLE_BAR_CHART, COL_HEATMAP, ROW_HEATMAP)。

    tipsPivotTableUI.addCellClickListener(event -> {
        showNotification("Value: " + event.getValue() + ",\n"
            + "Filters applied: " + event.getFilters());
    });


3.1. 透视表示例

3.1.1. 自定义聚合器和派生属性

下面是 pivotTable 的一个示例,与上面示例的不同之处在于使用了自定义的聚合器以及在界面的 Java 控制器中添加的派生属性。

<chart:pivotTable id="tipsCustomAggregatorPivotTable"
                  dataContainer="tipsDc">
    <chart:properties>
        <chart:property name="row"/>
        <chart:property name="totalBill"/>
        <chart:property name="tip"/>
        <chart:property name="sex"/>
        <chart:property name="smoker"/>
        <chart:property name="day"/>
        <chart:property name="time"/>
        <chart:property name="size"/>
    </chart:properties>
    <chart:aggregation mode="SUM_OVER_SUM" custom="true">
        <chart:property name="tip"/>
        <chart:property name="Total Bill"/>
    </chart:aggregation>
    <chart:rows>
        <chart:row value="sex"/>
        <chart:row value="Smokes"/>
    </chart:rows>
    <chart:columns>
        <chart:column value="day"/>
        <chart:column value="time"/>
    </chart:columns>
</chart:pivotTable>

可以在 XML 描述或 Java 控制器中设置排序和聚合函数。在此例中,JavaScript 函数作为参数传递给 JsFunction 类的构造器。

派生属性也可以在界面控制器中定义。

public class PivotSampleScreen extends Screen {

    @Inject
    private PivotTable tipsCustomAggregatorPivotTable;

    @Subscribe
    protected void onInit(InitEvent event) {
        tipsCustomAggregatorPivotTable.setSortersFunction(
                new JsFunction("function(attr){if(attr == \"Day\"){return $.pivotUtilities.sortAs([\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\",\"Sun\"]);}}"));

        tipsCustomAggregatorPivotTable.getAggregation().setFunction(
                new JsFunction("$.pivotUtilities.aggregators[\"Sum\"]([\"Tip\"])"));

        DerivedProperties derivedProperties = new DerivedProperties();
        derivedProperties.addAttribute("Smokes",
                new JsFunction("function(record) {return record.smoker == \"Yes\" ? \"True\" : \"False\";}"));
        tipsCustomAggregatorPivotTable.setDerivedProperties(derivedProperties);
    }
}

运行结果:

PivotTable 2
Figure 47. 使用了自定义聚合功能的透视表

3.1.2. 可编辑的透视表

在下面的示例中,让 pivotTable 可编辑并设置默认聚合函数。在可编辑模式下,可以直接在 UI 中更改外观(图表类型)和表格内容(行和列)。

<chart:pivotTable id="tipsPivotTableUI"
                  autoSortUnusedProperties="true"
                  dataContainer="tipsDc"
                  editable="true">
    <chart:properties>
        <chart:property name="row"/>
        <chart:property name="totalBill"/>
        <chart:property name="tip"/>
        <chart:property name="sex" localizedName="Sex"/>
        <chart:property name="smoker"/>
        <chart:property name="day"/>
        <chart:property name="time"/>
        <chart:property name="size"/>
    </chart:properties>
    <chart:hiddenProperties>
        <chart:property name="row"/>
    </chart:hiddenProperties>
    <chart:aggregationProperties>
        <chart:property name="tip"/>
        <chart:property name="totalBill"/>
    </chart:aggregationProperties>
    <chart:aggregations default="COUNT">
        <chart:aggregation caption="Count"/>
        <chart:aggregation mode="SUM_OVER_SUM"/>
    </chart:aggregations>
    <chart:renderers default="BAR_CHART"/>
    <chart:rows>
        <chart:row value="sex"/>
        <chart:row value="smoker"/>
    </chart:rows>
    <chart:columns>
        <chart:column value="day"/>
        <chart:column value="time"/>
    </chart:columns>
    <chart:sortersFunction>
        function(attr){
            if(attr=="Day"){
                return $.pivotUtilities.sortAs(["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]);
            }
        }
    </chart:sortersFunction>
</chart:pivotTable>
PivotTable 3
Figure 48. 可编辑的透视表

3.2. 导出透视表数据

PivotTableExtensionPivotTable 组件的扩展,提供带有聚合数据的表格下载 API,下载格式为 XLS。

需要使用类构造器(而不是 UiComponents bean)在界面控制器中创建此扩展的实例。示例:

@Inject
private PivotTable pivotTable;

private PivotTableExtension extension;

@Subscribe
private void onInit(InitEvent event) {
    extension = new WebPivotTableExtension(pivotTable)
}
Tip

该扩展仅适用于以下渲染器类型:TABLE,TABLE_BAR_CHART,HEATMAP,COL_HEATMAP,ROW_HEATMAP,并且不能取到单元格的颜色。

可以使用 exportTableToXls() 方法下载 XLS 格式的表格数据,比如,在点击按钮时:

extension.exportTableToXls();

默认情况下,下载文件名跟 PivotTable 数据容器中实体的本地化名称一致。也可以使用 setFileName() 方法定义文件名:

extension.setFileName("Orders of " + new Date());
Tip

XLS 格式的文件最多只能存 65536 行数据。如果 PivotTable 包含多于 65536 行,那么表格内容只能导出到限制的最后一行,系统也会弹出相应的警告信息。

另外,PivotTableExtension 提供了另外两个获取 PivotTable 数据的方法:

  • JSON 格式:

    extension.getPivotDataJSON();
  • 序列化的 PivotData 类对象:

    extension.getPivotData();

3.3. 使用 ShowPivotAction

ShowPivotAction 是一个特殊的操作,可以通过这个操作从继承了 ListComponent 的组件中导出数据,比如 TableTree 以及 DataGrid,甚至透视表。此操作不需要其它任何额外的应用程序组件就可以提供简单的 BI 分析方法。

这个操作需要在界面控制器中以编程的方式创建并使用,比如,在按钮中:

ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable);
exportButton.setAction(showPivotAction);

ShowPivotAction 有两种导出模式:所有行和选中行。如果没有选中行,默认会不进行确认导出所有行。

可编辑的 PivotTable 组件会显示在新标签页中。默认情况下,包含在组件数据容器视图的所有属性都会被展示,除了下面这些:

  • 集合(Collection)类型的属性;

  • 字节(byte)数组类型的属性;

  • UUID 属性;

  • 带有 @SystemLevel 注解的属性。

如果需要排除某些属性或者只包含部分属性,可以通过下面的流式 API 方法来实现:

  • withIncludedProperties(),示例:

    ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable)
            .withIncludedProperties(Arrays.asList("sex", "smoker", "day", "totalBill"));
  • withExcludedProperties(),示例:

    ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable)
            .withExcludedProperties(Arrays.asList("sex", "smoker"));

这些方法接收属性名称的列表作为输入参数,所有不正确的属性名称将被忽略。

可以使用 withNativeJson() 方法修改透视表的默认配置,此方法接收 JSON 字符串作为输入。注意要使用本地化的属性名称:

ShowPivotAction showPivotAction = new ShowPivotAction(tipsGroupTable)
        .withNativeJson("{" +
                " \"cols\": [\"Time\", \"Day\"]," +
                " \"rows\": [\"Sex\", \"Smoker\"]," +
                " \"editable\": false," +
                " \"renderer\": \"heatmap\"," +
                " \"aggregation\": {" +
                " \"id\": \"d8fc3fdf-730d-c94f-a0c8-72a9ce3dcb3a\"," +
                " \"mode\": \"count\"," +
                " \"properties\": [\"Sex\"]" +
                " }" +
                " }");

以下是不可编辑透视表的 JSON 结构:

{
        "cols": ["localized property", "localized property"],
        "rows": ["localized property"],
        "editable": false,
        "renderer": "heatmap",
        "aggregation": {
                "id": "d8fc3fdf-730d-c94f-a0c8-72a9ce3dcb3a",
                "mode": "sumOverSum",
                "properties": ["localized property", "localized property"]
        }
}

这是可编辑透视表的 JSON 结构:

{
        "cols": ["localized property"],
        "rows": ["localized property"],
        "editable": true,
        "renderers": {
                "selectedRenderer": "barChart"
        },
        "autoSortUnusedProperties": true,
        "aggregationProperties": ["localized property", "localized property"],
        "aggregations": {
                "selectedAggregation": "count",
                "aggregations": [{
                        "id": "647780f0-c6d0-6ade-a63a-542b5c8cdbd5",
                        "mode": "count",
                        "caption": "Count"
                }, {
                        "id": "c2663238-2654-67f0-2dec-add6962d867c",
                        "mode": "sumOverSum"
                }]
        }
}

显示的透视数据可以轻松导出到 Excel(如果支持当前渲染器)。默认情况下,在打开的标签页中将显示相应的导出按钮。

. . .