vaadin之UI组件

来源:互联网 发布:查错别字的软件 编辑:程序博客网 时间:2024/06/08 19:21

UI 组件的类层级关系图:
这里写图片描述
组合框ComboBox

// The locale in which we want to have the language// selection listLocale displayLocale = Locale.CHINA;// All known localesfinal Locale[] locales = Locale.getAvailableLocales();// Allow selecting a language. We are in a constructor of a// CustomComponent, so preselecting the current// language of the application can not be done before// this (and the selection) component are attached to// the application.final ComboBox select = new ComboBox("Select a language") {    @Override    public void attach() {        super.attach();        setValue(getLocale());    }};for (int i=0; i<locales.length; i++) {    select.addItem(locales[i]);    select.setItemCaption(locales[i], locales[i].getDisplayName(displayLocale));    // Automatically select the current locale    if (locales[i].equals(getLocale()))        select.setValue(locales[i]);}layout.addComponent(select);// Locale code of the selected localefinal Label localeCode = new Label("");layout.addComponent(localeCode);// A date field which language the selection will changefinal InlineDateField date =    new InlineDateField("Calendar in the selected language");date.setResolution(Resolution.DAY);layout.addComponent(date);// Handle language selectionselect.addValueChangeListener(new Property.ValueChangeListener() {    public void valueChange(ValueChangeEvent event) {        Locale locale = (Locale) select.getValue();        date.setLocale(locale);        localeCode.setValue("Locale code: " +                            locale.getLanguage() + "_" +                            locale.getCountry());    }});select.setImmediate(true);

效果如下:
这里写图片描述
文本域TextField

TextField readwrite = new TextField("Read-Write");        readwrite.setValue("You can change this");        readwrite.setReadOnly(false); // The default        layout.addComponent(readwrite);        TextField readonly = new TextField("Read-Only");        readonly.setValue("You can't touch this!");        readonly.setReadOnly(true);        layout.addComponent(readonly);文本变更事件// Text field with maximum lengthfinal TextField tf = new TextField("My Eventful Field");tf.setValue("Initial content");tf.setMaxLength(20);// Counter for input lengthfinal Label counter = new Label();counter.setValue(tf.getValue().length() +                 " of " + tf.getMaxLength());// Display the current length interactively in the countertf.addTextChangeListener(new TextChangeListener() {    public void textChange(TextChangeEvent event) {        int len = event.getText().length();        counter.setValue(len + " of " + tf.getMaxLength());    }});// The lazy mode is actually the defaulttf.setTextChangeEventMode(TextChangeEventMode.LAZY);

TextArea

TextArea area1 = new TextArea("Wrapping");area1.setWordwrap(true); // The defaultarea1.setValue("A quick brown fox jumps over the lazy dog");TextArea area2 = new TextArea("Nonwrapping");area2.setWordwrap(false);area2.setValue("Victor jagt zw&ouml;lf Boxk&auml;mpfer quer "+               "&uuml;ber den Sylter Deich");setWordwrap() 方法控制当行的长度达到表示区域最大宽度时, 长的行是否折行表示

PasswordField

PasswordField 是 TextField 的变体, 它会将输入的内容隐藏起来.PasswordField tf = new PasswordField("Keep it secret");

RichTextArea

RichTextArea 组件可用于输入和编辑格式化的文本. 它的工具条提供了所有基本的的编辑功能

使用 DateField 输入日期和时间

DateField 组件用于显示和输入日期/时间. 这个组件有两种不同的形式: PopupDateField, 它包括一个数字输入框, 和一个弹出式日历, InlineDateField, 直接显示日历. DateField 基类默认为弹出式输入方式// Create a DateField with the default styleDateField date = new DateField();// Set the date and time to presentdate.setValue(new Date());// Display only year, month, and day in ISO formatdate.setDateFormat("yyyy-MM-dd");InlineDateField 提供一个日期选择组件, 表现为月历形式. 用户可以点击适当的箭头按钮在不同的年份和月份间跳转. 与弹出式日历不同, 这个日历会永远显示在界面中.

CheckBox

CheckBox checkbox1 = new CheckBox("Box with no Check");CheckBox checkbox2 = new CheckBox("Box with a Check");checkbox2.setValue(true);checkbox1.addValueChangeListener(event -> // Java 8    checkbox2.setValue(! checkbox1.getValue()));checkbox2.addValueChangeListener(event -> // Java 8    checkbox1.setValue(! checkbox2.getValue()));

OptionGroup

Option group 默认为单选模式. 使用 setMultiSelect() 方法可以激活多选模式// A single-select radio button groupOptionGroup single = new OptionGroup("Single Selection");single.addItems("Single", "Sola", "Yksi");// A multi-select check box groupOptionGroup multi = new OptionGroup("Multiple Selection");multi.setMultiSelect(true);multi.addItems("Many", "Muchos", "Monta");你可以使用 setItemEnabled() 方法禁用 OptionGroup 内的单个项目. 在多选模式下, 对于被禁用的项目, 用户不能选中它, 也不能解除选中, 但在单选模式下, 用户可以将当前选项从一个被禁用的项目变为另一个有效的项目. 选中项可以通过程序来变更, 无论项目是有效还是禁用. 你可以使用 isItemEnabled() 方法检查某个项目是有效还是禁用.setItemEnabled() 使用项目的 ID 来指定它将要禁用或允许的项目.// Have an option group with some itemsOptionGroup group = new OptionGroup("My Disabled Group");group.addItems("One", "Two", "Three");// Disable one item by its item IDgroup.setItemEnabled("Two", false);

TwinColSelect

TwinColSelect 是一个多选组件, 它并列显示两个列表框, 左侧是未选择的项目, 右侧是已选择的项目. 用户可以从左侧列表选择项目, 然后点击 ">>" 按钮将它移动到右侧列表中. 取消选择一个项目, 可以在右侧列表选中它, 然后点击 "<<" 按钮TwinColSelect select = new TwinColSelect("Select Targets");// Put some items in the selectselect.addItems("Mercury", "Venus", "Earth", "Mars",        "Jupiter", "Saturn", "Uranus", "Neptune");// Few items, so we can set rows to match item countselect.setRows(select.size());// Preselect a few items by creating a setselect.setValue(new HashSet<String>(    Arrays.asList("Venus", "Earth", "Mars")));// Handle value changesselect.addValueChangeListener(event -> // Java 8    layout.addComponent(new Label("Selected: " +            event.getProperty().getValue())));

这里写图片描述

Table

Table 组件的目的是以行和列的形式表现表格式的数据Table 的行就是容器内的 项目(item) , Table 的列则是项目的 属性(property). Table 的各行(项目) 使用 项目 ID(item identifier) (IID) 来区分, 各列(property) 使用 属性 ID(property identifier) (PID) 来区分创建 Table 时, 你首先需要使用addContainerProperty() 方法定义列Table table = new Table("The Brightest Stars");// Define two columns for the built-in containertable.addContainerProperty("Name", String.class, null);table.addContainerProperty("Mag",  Float.class, null);// Add a row the hard wayObject newItemId = table.addItem();Item row1 = table.getItem(newItemId);row1.getItemProperty("Name").setValue("Sirius");row1.getItemProperty("Mag").setValue(-1.46f);// Add a few other rows using shorthand addItem()table.addItem(new Object[]{"Canopus",        -0.72f}, 2);table.addItem(new Object[]{"Arcturus",       -0.04f}, 3);table.addItem(new Object[]{"Alpha Centauri", -0.01f}, 4);// Show exactly the currently contained rows (items)table.setPageLength(table.size());

在 Table 内选择项目

在 Table 中可以通过鼠标点击来选择一个或多个项目. 当用户选择一个项目时, 项目的 IID 会被设置为 Table 的属性值, 同时触发 ValueChangeEvent 事件. 你需要将 Table 设置为 可选择, 才能允许选择 Table 内的项目. 大多数情况下, 你还需要将 Table 设置为 立即模式, 如下例所示, 因为如果不是立即模式, 属性的变化不会立即发送到服务器端.下面的例子演示如何允许在 Table 内选择项目, 以及如何处理由选择项变化造成的 ValueChangeEvent 事件. 你需要使用 Property.ValueChangeListener 接口的 valueChange() 方法来处理这个事件.// Allow selecting items from the table.table.setSelectable(true);// Send changes in selection immediately to server.table.setImmediate(true);// Shows feedback from selection.final Label current = new Label("Selected: -");// Handle selection change.table.addValueChangeListener(new Property.ValueChangeListener() {    public void valueChange(ValueChangeEvent event) {        current.setValue("Selected: " + table.getValue());    }});

这里写图片描述
多选模式
Table 也可以处于 多选 模式, 这时用户可以按住 Ctrl 键(或 Meta 键)不放, 然后鼠标点击多个项目来选择它们. 如果 Ctrl 键不按下, 点击一个项目会选中它, 但其他被选中的项目会被取消选中. 用户可以选择一个范围内的所有项目, 方法是先选中一个项目, 按住 Shift 键不放, 然后点击另一个项目, 这两个项目之间的所有项目都会被选中. 还可以选择多个范围, 方法是先选中一个范围, 然后按住 Ctrl 键不放, 再同时按住 Ctrl 和 Shift 键不放, 选择另一个项目.
使用 Table 的基类 AbstractSelect 中的 setMultiSelect() 方法可以打开多选模式. 将 Table 设为多选模式不会隐含地将它设置为 可选择, 因此还必须设置 Table 为可选择.
setMultiSelectMode() 方法决定多选的控制模式: MultiSelectMode.DEFAULT 是默认模式, 这种模式下选择项目需要按住 Ctrl (或 Meta) 键不放, MultiSelectMode.SIMPLE 模式下则不需要按住 Ctrl 键. 在 simple 模式下, 要取消一个已选中的项目, 必须再次点击它.
页长与滚动条
Table 的默认风格是一个带滚动条的表格. 滚动条在表格的右侧, 只在 Table 内项目的数量超过页长时才显示, 页长也就是可见项目的数量. 你可以使用 setPageLength() 方法设置页长.
将页长设置为 0 , 会显示 Table 内所有项目, 无论项目数量有多大. 注意, 这样的设置也会导致缓冲功能失效, 因为 Tabel 内所有数据都必须一次性装载到浏览器端. 使用这样的 Table 来生成报表不适用于大量数据的情况, 因为它会在使用 Ajax 描绘 Table 时出现大量的数据传输. 对于大量数据的报表, 直接生成 HTML 内容会更高效一些.
拖动列的宽度
你可以在服务器端程序中使用setColumnWidth() 方法设置列的宽度 . 列通过对应的属性 ID 来标识, 宽度单位为像素.
用户也可以拖动两列之间的 resize 条来调整 Table 的列宽. 重新调整列宽会激发 ColumnResizeEvent 事件, 这个事件可以通过 Table.ColumnResizeListener 监听器来处理. 通常你会希望立即收到列宽变化的事件, 这时 Table 必须设置为立即模式.

table.addColumnResizeListener(new Table.ColumnResizeListener(){    public void columnResize(ColumnResizeEvent event) {        // Get the new width of the resized column        int width = event.getCurrentWidth();        // Get the property ID of the resized column        String column = (String) event.getPropertyId();        // Do something with the information        table.setColumnFooter(column, String.valueOf(width) + "px");    }});// Must be immediate to send the resize events immediatelytable.setImmediate(true);

拖动列的顺序
如果设置了 setColumnReorderingAllowed(true), 用户可以调整 Table 内列的顺序, 方法是用鼠标拖动列头部分
将列收起
果设置了 setColumnCollapsingAllowed(true), Table 头部的右侧会显示一个下拉列表框, 在这个列表框中可以选择显示哪些列. 将列收起, 与使用 setVisibleColumns() 方法将列隐藏起来是不同的, 后一种方法会导致列完全被隐藏, 用户不能通过 UI 操作将这个列显示出来.
Table 内的组件
Table 内的单元格不仅可以显示字符串, 而且可以包含任意的 UI 组件. 如果行的高度超过默认 theme 定义的行高, 那么你就需要使用自定义 theme 来定义适当的行高.
对于 Table 内的组件, 比如下例中的 Button, 当你处理它的事件时, 你需要知道这个组件所属的项目. 组件本身是不知道 Table 的, 也不知道组件所属的项目. 因此, 事件处理代码需要使用其他手段来确定当前项目 的 ID. 可能的方法有几种. 通常最简单的办法是使用 setData() 方法将任意对象绑定到组件上. 你也可以继承组件, 然后在子类中存放项目 ID 信息. 你也可以简单地在整个 Table 内搜索组件所属的项目, 不过这种方案的可扩展性可能不太好.
下联的例子在 Table 的行内嵌入一个 HTML 内容模式的 Label, 一个多行的 TextField, 一个 CheckBox, 以及一个显示为链接风格的 Button.

final Table table = new Table();table.addStyleName("components-inside");/* Define the names and data types of columns. * The "default value" parameter is meaningless here. */table.addContainerProperty("Sum",            Label.class,     null);table.addContainerProperty("Is Transferred", CheckBox.class,  null);table.addContainerProperty("Comments",       TextField.class, null);table.addContainerProperty("Details",        Button.class,    null);/* Add a few items in the table. */for (int i=0; i<100; i++) {    // Create the fields for the current table row    Label sumField = new Label(String.format(                   "Sum is <b>$%04.2f</b><br/><i>(VAT incl.)</i>",                   new Object[] {new Double(Math.random()*1000)}),                               ContentMode.HTML);    CheckBox transferredField = new CheckBox("is transferred");    // Multiline text field. This required modifying the     // height of the table row.    TextField commentsField = new TextField();    commentsField.setRows(3);    // The Table item identifier for the row.    Integer itemId = new Integer(i);    // Create a button and handle its click. A Button does not    // know the item it is contained in, so we have to store the    // item ID as user-defined data.    Button detailsField = new Button("show details");    detailsField.setData(itemId);    detailsField.addClickListener(new Button.ClickListener() {        public void buttonClick(ClickEvent event) {            // Get the item identifier from the user-defined data.            Integer iid = (Integer)event.getButton().getData();            Notification.show("Link " +                              iid.intValue() + " clicked.");        }     });    detailsField.addStyleName("link");    // Create the table row.    table.addItem(new Object[] {sumField, transferredField,                                commentsField, detailsField},                  itemId);}// Show just three rows because they are so high.table.setPageLength(3);

这里写图片描述
遍历一个 Table
由于 Table 内的项目是无下标索引的, 因此要遍历所有的项目必须使用 iterator. Table 类的 Container 接口的 getItemIds() 方法返回一个 Collection, 其中的内容是项目 ID, 你可以使用 Iterator 来遍历这个 Collection. 注意, 在遍历过程中你不能修改 Table 内容, 也就是说, 不能增加或删除项目. 但修改项目内的数据是允许的.
在 Table 内编辑数据值
Table 通常只是简单地以文本的方式显示项目中的各字段. 如果你希望允许用户编辑数据值, 你可以象前面的例子一样将各字段放在组件内, 也可以简单地调用 setEditable(true) 方法, 这时各单元格就会自动地变为可编辑的字段.
我们首先来看看一个通常的 Table, 其中包含几个列, 类型为通常的 Java 类型, 一个 Date, 一个 Boolean, 以及一个 String.

// Create a table. It is by default not editable.final Table table = new Table();// Define the names and data types of columns.table.addContainerProperty("Date",     Date.class,  null);table.addContainerProperty("Work",     Boolean.class, null);table.addContainerProperty("Comments", String.class,  null);// Add a few items in the table.for (int i=0; i<100; i++) {    Calendar calendar = new GregorianCalendar(2008,0,1);    calendar.add(Calendar.DAY_OF_YEAR, i);    // Create the table row.    table.addItem(new Object[] {calendar.getTime(),                                new Boolean(false),                                ""},                  new Integer(i)); // Item identifier}table.setPageLength(8);layout.addComponent(table);如果需要的话, 你立刻就可以将 Table 设置为可编辑模式. 我们下面扩展一下这个例子, 为它增加某种机制, 可以将 Table 在可编辑/不可编辑模式之间切换.final CheckBox switchEditable = new CheckBox("Editable");switchEditable.addValueChangeListener(        new Property.ValueChangeListener() {    public void valueChange(ValueChangeEvent event) {        table.setEditable(((Boolean)event.getProperty()                             .getValue()).booleanValue());    }});switchEditable.setImmediate(true);layout.addComponent(switchEditable);

这里写图片描述
这里写图片描述
在可编辑模式下跳转
在可编辑模式下, 编辑器 Field 组件可以拥有输入焦点. 按下 Tab 键可以将焦点移动到下一列, 如果目前已经是最后一列, 那么会移动到下一个项目的第一列. 与此对应, 按下 Shift+Tab 键会将焦点反向移动. 如果焦点在最后一个可见项目的最后一列, 按下 Tab 贱会将焦点移动到 Table 之外. 从第一个项目的第一列反向移动焦点, 会将焦点移动到 Table 自身. 对 Table 的某些变更, 比如变更表头, 表脚, 恢复一个列, 都可以将焦点从编辑器组件移动到 Table 自身.
很多情况下 Table 的默认行为可能不适应你的需求. 比如, 焦点还会跳转到只读的编辑器组件上, 还会不适当地移出 Table 之外. 你可以实现更好的焦点跳转, 方法是用事件监听器来处理某些快捷键, 比如 Tab, Arrow Up,Arrow Down, 以及 Enter.

// Keyboard navigationclass KbdHandler implements Handler {    Action tab_next = new ShortcutAction("Tab",            ShortcutAction.KeyCode.TAB, null);    Action tab_prev = new ShortcutAction("Shift+Tab",            ShortcutAction.KeyCode.TAB,            new int[] {ShortcutAction.ModifierKey.SHIFT});    Action cur_down = new ShortcutAction("Down",            ShortcutAction.KeyCode.ARROW_DOWN, null);    Action cur_up   = new ShortcutAction("Up",            ShortcutAction.KeyCode.ARROW_UP,   null);    Action enter   = new ShortcutAction("Enter",            ShortcutAction.KeyCode.ENTER,      null);    public Action[] getActions(Object target, Object sender) {        return new Action[] {tab_next, tab_prev, cur_down,                             cur_up, enter};    }    public void handleAction(Action action, Object sender,                             Object target) {        if (target instanceof TextField) {            // Move according to keypress            int itemid = (Integer) ((TextField) target).getData();            if (action == tab_next || action == cur_down)                itemid++;            else if (action == tab_prev || action == cur_up)                itemid--;            // On enter, just stay where you were. If we did            // not catch the enter action, the focus would be            // moved to wrong place.            if (itemid >= 0 && itemid < table.size()) {                TextField newTF = valueFields.get(itemid);                if (newTF != null)                    newTF.focus();            }        }    }}// Panel that handles keyboard navigationPanel navigator = new Panel();navigator.addStyleName(Reindeer.PANEL_LIGHT);navigator.addComponent(table);navigator.addActionHandler(new KbdHandler());

列头和列脚
列头
列头显示在 Table 的最顶端. 前面已经介绍过, 你可以使用列头来拖动列, 或改变列的宽度. 默认情况下, 除非使用 setColumnHeader() 方法明确地指定列头内容, 否则列头内容为列对应的属性 ID.

// Define the propertiestable.addContainerProperty("lastname", String.class, null);table.addContainerProperty("born", Integer.class, null);table.addContainerProperty("died", Integer.class, null);// Set nicer header namestable.setColumnHeader("lastname", "Name");table.setColumnHeader("born", "Born");table.setColumnHeader("died", "Died");列头的文字以及列头是否可见 取决于 列头模式. 列头默认是可见的, 但你可以使用setColumnHeaderMode(Table.COLUMN_HEADER_MODE_HIDDEN) 方法禁用它.

列脚
Table 的列脚可用来显示某列的合计或平均值, 等等信息. 列脚默认是不可见的; 你可以使用setFooterVisible(true) 方法显示它. 与列头不同, 列头(译注: 原文如此, 应为”列脚”)默认是空的. 你可以使用 setColumnFooter() 方法为列脚设置值. 这个方法中, 列通过对应的属性 ID 来指定.
下例演示如何计算一个列的平均值:

// Have a table with a numeric columnTable table = new Table("Custom Table Footer");table.addContainerProperty("Name", String.class, null);table.addContainerProperty("Died At Age", Integer.class, null);// Insert some dataObject people[][] = {{"Galileo",  77},                     {"Monnier",  83},                     {"Vaisala",  79},                     {"Oterma",   86}};for (int i=0; i<people.length; i++)    table.addItem(people[i], new Integer(i));// Calculate the average of the numeric columndouble avgAge = 0;for (int i=0; i<people.length; i++)    avgAge += (Integer) people[i][1];avgAge /= people.length;// Set the footerstable.setFooterVisible(true);table.setColumnFooter("Name", "Average");table.setColumnFooter("Died At Age", String.valueOf(avgAge));// Adjust the table height a bittable.setPageLength(table.size());

处理列头和列脚的鼠标点击事件
通常, 如果数据源是 Sortable, 并且排序功能未被禁止, 当用户点击列头时, Table 将按照这个列进行排序. 某些情况下, 你可能希望在用户点击列头时实现一些别的功能, 比如以某种方式选中整个列.
点击列头会激发 HeaderClickEvent 事件, 你可以使用 Table.HeaderClickListener 监听器来处理. 列头(以及列脚)上的点击事件, 与按钮的点击一样, 会立即发送到服务器端, 因此不必使用 setImmediate() 方法将 Table 设置为立即模式.

// Handle the header clickstable.addHeaderClickListener(new Table.HeaderClickListener() {    public void headerClick(HeaderClickEvent event) {        String column = (String) event.getPropertyId();        Notification.show("Clicked " + column +                "with " + event.getButtonName());    }});// Disable the default sorting behaviortable.setSortDisabled(true);设置一个点击事件监听器不会自动地将列头的排序动作禁止掉; 你需要调用 setSortDisabled(true) 来明确地禁止它. 当用户点击列头来调整列宽, 或拖动列的顺序时, 不会触发列头点击事件.HeaderClickEvent 对象通过 getPropertyId() 方法提供被点击的列的 ID. getButton() 方法报告点击时按下的鼠标键是哪一个: BUTTON_LEFT, BUTTON_RIGHT, 或者 BUTTON_MIDDLE. getButtonName() 方法返回一个易读的鼠标键英文名: "left", "right", 或者 "middle". isShiftKey(), isCtrlKey(), 等方法判断Shift, Ctrl, Alt 等辅助案件在鼠标点击时是否有被按下.点击列脚会激发 FooterClickEvent 事件, 你可以使用 Table.FooterClickListener 监听器处理. 列头点击后默认会排序, 但列脚点击后没有默认的处理动作. 除了这一点之外, 列脚点击事件的处理与列头点击事件是完全一样的

动态生成的列
你可能会希望某个列的内容由其他列计算而来. 或者你也可能希望用某种方式格式化某个列, 比如, 如果某些列显示货币值的情况. ColumnGenerator 接口可以用来为这样的列创建自定义的列生成器.
你可以使用addGeneratedColumn() 方法将新生成的列添加到 Table 中. 这个方法接受的参数是列 ID. 通常你会希望列头内容更加用户友好一些, 最好能国际化. 在添加新生成的列之前, 你可以使用addContainerProperty()方法设置列头和图标.

// Define table columns. table.addContainerProperty(    "date",     Date.class,   null, "Date",         null, null);table.addContainerProperty(    "quantity", Double.class, null, "Quantity (l)", null, null);table.addContainerProperty(    "price",    Double.class, null, "Price (e/l)",  null, null);table.addContainerProperty(    "total",    Double.class, null, "Total (e)",    null, null);// Define the generated columns and their generators.table.addGeneratedColumn("date",                         new DateColumnGenerator());table.addGeneratedColumn("quantity",                         new ValueColumnGenerator("%.2f l"));table.addGeneratedColumn("price",                         new PriceColumnGenerator());table.addGeneratedColumn("total",                         new ValueColumnGenerator("%.2f e"));注意, 即使你事先定义了列的顺序, addGeneratedColumn() 方法永远会将动态创建的列添加为最末列. 你必须使用 setVisibleColumns() 方法来设置适当的列顺序.table.setVisibleColumns(new Object[] {"date", "quantity", "price", "total"});列生成器是实现了 Table.ColumnGenerator 接口及其中的generateCell() 方法的对象. 这个方法接受的参数是项目 ID, 列 ID, 以及 Table 对象. 方法必须返回一个组件对象.下例创建了一个列生成器, 它使用一个格式化字符串来格式化一个 Double 值的 Field (与java.util.Formatter 一样)./** Formats the value in a column containing Double objects. */class ValueColumnGenerator implements Table.ColumnGenerator {    String format; /* Format string for the Double values. */    /**     * Creates double value column formatter with the given     * format string.     */    public ValueColumnGenerator(String format) {        this.format = format;    }    /**     * Generates the cell containing the Double value.     * The column is irrelevant in this use case.     */    public Component generateCell(Table source, Object itemId,                                  Object columnId) {        // Get the object stored in the cell as a property        Property prop =            source.getItem(itemId).getItemProperty(columnId);        if (prop.getType().equals(Double.class)) {            Label label = new Label(String.format(format,                    new Object[] { (Double) prop.getValue() }));            // Set styles for the column: one indicating that it's            // a value and a more specific one with the column            // name in it. This assumes that the column name            // is proper for CSS.            label.addStyleName("column-type-value");            label.addStyleName("column-" + (String) columnId);            return label;        }        return null;    }}对于 Table 内的所有可见项目(更准确的说, 应该是所有被缓存的项目), 列生成器都会被调用. 如果用户在 Table 内滚动到另一个位置, 对于新的可见行, 会动态生成列内容. 当某个项目的值发生变更时, 在可见(缓存)行中的列也会被自动生成. 因此使用不同的行(项目)中的值来计算某个动态生成的单元格的值, 通常是安全的.当你将 Table 设置为 可编辑, 通常的 Field 会变为可编辑的 Field. 当用户修改了 Field 内的值, 动态生成的列会被自动更新. 将一个包含动态生成的列的 Table 设置为可编辑模式会有点怪异. Table 的可编辑模式不会影响到动态生成的列. 你有两个选择: 要么在列生成器中生成可编辑的 Field 组件, 或者, 对于使用列生成器来格式化列内容的情况, 可以在可编辑模式下删除这个生成器. 下例使用后一种方案.// Have a check box that allows the user// to make the quantity and total columns editable.final CheckBox editable = new CheckBox(    "Edit the input values - calculated columns are regenerated");editable.setImmediate(true);editable.addClickListener(new ClickListener() {    public void buttonClick(ClickEvent event) {        table.setEditable(editable.booleanValue());        // The columns may not be generated when we want to        // have them editable.        if (editable.booleanValue()) {            table.removeGeneratedColumn("quantity");            table.removeGeneratedColumn("total");        } else { // Not editable            // Show the formatted values.            table.addGeneratedColumn("quantity",                new ValueColumnGenerator("%.2f l"));            table.addGeneratedColumn("total",                new ValueColumnGenerator("%.2f e"));        }        // The visible columns are affected by removal        // and addition of generated columns so we have        // to redefine them.        table.setVisibleColumns(new Object[] {"date", "quantity",                 "price", "total", "consumption", "dailycost"});    }});你还需要将 Field 编辑组件设置为 立即 模式, 这样才能在 Field 编辑组件失去焦点时, 让数据值立即更新. 你可以使用定制的 TableFieldFactory, 来将Field 编辑组件设置为 立即 模式, 如下例所示, 我们在这里只是简单地继承默认实现, 然后设置了编辑组件的模式:public class ImmediateFieldFactory extends DefaultFieldFactory {    public Field createField(Container container,                             Object itemId,                             Object propertyId,                             Component uiContext) {        // Let the DefaultFieldFactory create the fields...        Field field = super.createField(container, itemId,                                        propertyId, uiContext);        // ...and just set them as immediate.        ((AbstractField)field).setImmediate(true);        return field;    }}...table.setTableFieldFactory(new ImmediateFieldFactory());如果你使用列生成器来生成 Field 编辑组件, 你就没必要使用上面那样的 Field 工厂了, 但是当然, 你必须为正常模式和可编辑模式两种情况生成 Field.

Tree

Tree 组件以一种自然的方式来表现层级式关系的数据,final Object[][] planets = new Object[][]{        new Object[]{"Mercury"},         new Object[]{"Venus"},        new Object[]{"Earth", "The Moon"},            new Object[]{"Mars", "Phobos", "Deimos"},        new Object[]{"Jupiter", "Io", "Europa", "Ganymedes",                                "Callisto"},        new Object[]{"Saturn",  "Titan", "Tethys", "Dione",                                "Rhea", "Iapetus"},        new Object[]{"Uranus",  "Miranda", "Ariel", "Umbriel",                                "Titania", "Oberon"},        new Object[]{"Neptune", "Triton", "Proteus", "Nereid",                                "Larissa"}};Tree tree = new Tree("The Planets and Major Moons");/* Add planets as root items in the tree. */for (int i=0; i<planets.length; i++) {    String planet = (String) (planets[i][0]);    tree.addItem(planet);    if (planets[i].length == 1) {        // The planet has no moons so make it a leaf.        tree.setChildrenAllowed(planet, false);    } else {        // Add children (moons) under the planets.        for (int j=1; j<planets[i].length; j++) {            String moon = (String) planets[i][j];            // Add the item as a regular item.            tree.addItem(moon);            // Set it to be a child.            tree.setParent(moon, planet);            // Make the moons look like leaves.            tree.setChildrenAllowed(moon, false);        }        // Expand the subtree.        tree.expandItemsRecursively(planet);    }}main.addComponent(tree);你可以使用Tree 组件的属性值来获得或设定当前选中的项目, 也就是使用 getValue() 和 setValue() 方法. 当用户点击 Tree 内某个项目时, Tree 会收到 ValueChangeEvent 事件, 你可以使用 ValueChangeListener监听器来处理这个事件. 如果要在用户点击后立即接收这个事件, 你需要对 Tree 设置 setImmediate(true)
0 0