Make an OptionGroup (radio buttons) from an enum in Vaadin 7?
我有一个Java枚举,带有用于所需显示文本的getter。 如何使用它来填充Vaadin 7中的OptionGroup?
在Vaadin 7中,可以通过以下三种方法进行此操作:
好。
好。
这是我编写的
好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | package com.basilbourque; import com.vaadin.ui.OptionGroup; import java.util.Arrays; import java.util.Collection; import java.util.function.Function; import org.slf4j.LoggerFactory; /** * A subclass of the Vaadin 7 OptionGroup (radio buttons or bunch of checkboxes) widget, taking as its set of options * the instances of an Enum. * * In canonical usage, pass the class of your Enum and a reference to the method to be called for obtaining a textual * label for display to the user. * * Alternatively, if your Enum overrides the `toString` method, you may pass only the class of the Enum without a * Function. This approach is not recommended per the class documentation which explains `toString` should only be used * for debugging message. Nevertheless, some people override `toString` to provide a user-readable label, so we support * this. * * Even if your Enum does not override `toString` you may choose to omit passing the Function argument. As a default, * the Enum’s built-in `toString` method will be called, returning the"name" of the Enum’s instance. This is handy for * quick-and-dirty prototyping. Again, neither I nor the class doc recommend this approach for serious work. * * If you want to display a subset of your enum’s instances rather than all, pass a Collection. * * This source code available under terms of ISC License. https://en.wikipedia.org/wiki/ISC_license * * Copyright (c) 2015, Basil Bourque * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby * granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS * PROVIDED"AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * @author Basil Bourque * @version 2015-08-27T21:00:00Z * @since 2015-08-27T21:00:00Z */ public class EnumBackedOptionGroup<T extends Enum> extends OptionGroup { final org.slf4j.Logger logger = LoggerFactory.getLogger( this.getClass() ); /** * Constructor. The usual constructor for automatically detecting all the instances of an enum for use as the * options in a Vaadin 7 OptionGroup. Pass a function to be called for providing each option’s displayed labeling. * * Example usage: * * myRadios = new EnumBackedOptionGroup<DogBreed>("Choose breed:" , DogBreed.class , DogBreed :: getTitle ); * * @param caption * @param enumClass * @param f */ public EnumBackedOptionGroup ( final String caption , final Class< T > enumClass , final Function<T , String> f ) { super( caption ); Function<T , String> func = f; // If passed a null for the Function, fallback to using 'toString'. if ( func == null ) { func = T -> T.toString(); } this.buildAndAssignCaptions( enumClass , func ); } /** * Constructor. Similar to usual constructor, but here you may additionally pass a Collection of the subset of Enum * instances. * * For use where business logic dictates that you give only some of the Enum values an options rather than all of * them. The omitted options are effectively hidden from the user. * * @param caption * @param enumClass * @param enumValues * @param f */ public EnumBackedOptionGroup ( final String caption , final Class< T > enumClass , final Collection< T > enumValues , final Function<T , String> f ) { super( caption ); Function<T , String> func = f; // If passed a null for the Function, fallback to using 'toString'. if ( func == null ) { func = T -> T.toString(); } Collection< T > ev = enumValues; // Handle where calling method passed us a null or empty collection. if ( ( ev == null ) || ev.isEmpty() ) { this.buildAndAssignCaptions( enumClass , f ); // Fallback to assiging all the instances of enum as options in our OptionGroup. } else { this.addItems( enumValues ); // Add the passed subset of instances of the enum as items backing our OptionGroup. this.assignCaptions( enumValues , f ); } } /** * Constructor. Similar to the usual constructor, but omits the method for providing on-screen labeling. Instead * uses the 'toString' method defined either explicitly in the Enum subclass or implicitly calls to the Enum class’ * own 'toString'. * * Not recommended, as the Enum documentation strongly suggests the 'toString' method on an Enum be used only for * debugging. Nevertheless this is handy for quick-and-dirty prototyping. * * @param caption * @param enumClass */ public EnumBackedOptionGroup ( final String caption , final Class< T > enumClass ) { super( caption ); // User passed no Function to call for getting the title. So fallback to using 'toString'. this.buildAndAssignCaptions( enumClass , T -> T.toString() ); } // Helper method. (sub-routine) // Extracts all the instances of the enum, and uses them as options in our OptionGroup. // Also assigns each option a labeling using String returned by passed method to be called for each instance of enum. private void buildAndAssignCaptions ( final Class< T > enumClass , final Function<T , String> f ) { if ( enumClass.isEnum() ) { // This check may be unnecessary with Generics code"<T extends Enum>" at top of this class. Collection< T > enumValues = Arrays.asList( enumClass.getEnumConstants() ); this.addItems( enumValues ); // Add all the instances of the enum as items backing our OptionGroup. this.assignCaptions( enumValues , f ); } else { // Else the passed class is not an enum. // This case should not be possible because of the Generics marked on this class"<T extends Enum>". logger.error("Passed a class that is not a subclass of Enum. Message # f2098672-ab47-47fe-b720-fd411411052e." ); throw new IllegalArgumentException("Passed a class that is not a subclass of Enum." ); } } // Helper method. (sub-routine) // Assigns each option a labeling using String returned by passed method to be called for each instance of enum private void assignCaptions ( Collection< T > enumValues , final Function<T , String> f ) { for ( T option : enumValues ) { // For each option in our OptionGroup, determine and set its title, the label displayed for the user next to each radio button or checkbox. // To determine the label (the second argument), we invoke the passed method which must return a String. Using Lambda syntax. this.setItemCaption( option , f.apply( option ) ); } } } |
我希望您将使用像这样的枚举
好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.example; /** * Bogus example Enum. */ public enum DogBreed { AUSSIE("Australian Shepherd") , BORDER_COLLIE("Border Collie"), BLACK_LAB("Labrador, Black"), MUTT("Mixed Breed"); private String title = null; DogBreed ( final String titleArg) { this.title = titleArg; } public String getTitle() { return this.title; } } |
好。
由于WillShackleford对我的Question的Lambda语法的传递和调用方法引用,我只能完成该类。
好。
要使用此
好。
1 | OptionGroup optionGroup = new EnumBackedOptionGroup<DogBreed>("Choose Breed:" , DogBreed.class , DogBreed :: getTitle ); |
对于快速创建原型,可以定义一个没有此类构造函数和getter的简单枚举。在这种情况下,只传递您的标题和枚举类。
好。
1 2 3 4 5 6 7 8 9 10 11 | package com.example; /** * Bogus example Enum. */ public enum SaySo { YES, NO, MAYBE; } OptionGroup optionGroup = new EnumBackedOptionGroup<SaySo>("Says you:" , SaySo.class ); |
有时,您可能不希望在OptionGroup中使用所有枚举的实例值。如果是这样,请使用本课题中说明的隐式方法
好。
您可以将null作为最后一个参数传递,以使用
好。
您也许可以使用
好。
1 2 3 | Collection< T > enumValues = new ArrayList( Arrays.asList( SaySo.values() ) ); enumValues.remove( SaySo.MAYBE ); OptionGroup optionGroup = new EnumBackedOptionGroup<SaySo>("Says you:" , SaySo.class , null ); |
自己动手
想象一下
好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public enum CRITTER_FILTER { CANINE ("Dogs" ), // Pass the text to be displayed to user as the radio button’s Caption (label). FELINE ("Cats" ), COCKATIEL ("Cockatiel birds" ); private String title; CRITTER_FILTER ( String t ) { this.title = t; } // Add this method for the more flexible approach. // JavaBeans"getter" for use in BeanItemContainer. public String getTitle () { return this.title; } // Add this method for the short simple approach. @Override public String toString () { return this.title; } } |
添加构造函数使我们能够将所需的显示文本传递到每个枚举实例,然后将该文本存储在私有成员String变量中。
好。
简短的简单方法
如果确定显示文本没有花哨的工作,只需重写
好。
我不推荐这种方法。文档建议仅在要创建一个特殊值以在调试工作中显示给程序员时才建议覆盖
好。
公共字符串toString()
好。
…尽管通常不需要或不希望使用此方法,但该方法可能会被覆盖。当存在更"程序员友好"的字符串形式时,枚举类型应覆盖此方法。
好。
blockquote>
1
2 this.filterRadios = new OptionGroup("Filter:" , Arrays.asList( SomeClass.CRITTER_FILTER.values() ) ); // Convert plain array of the enum instances (the values) into a `Collection` object by calling utility method `Arrays.asList`.
this.filterRadios.setMultiSelect( false ); // Radio buttons are single-select.
toString 方法的完整示例
Person 类,带有嵌套枚举。好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 package com.example.vaadinradiobuttons;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Basil Bourque
*/
public class Person {
// Members
String name;
Person.VITAL_STATUS vitalStatus;
public enum VITAL_STATUS {
LIVING("Alive and Kicking" ),
DECEASED("Dead" ),
UNKNOWN("DUNNO" );
private String captionText;
VITAL_STATUS ( String t ) {
this.captionText = t;
}
@Override
public String toString () {
return this.captionText;
}
}
// Constructor
public Person ( String nameArg , VITAL_STATUS vitalStatusArg ) {
this.name = nameArg;
this.vitalStatus = vitalStatusArg;
}
}还有一个很小的Vaadin 7.4.3小应用程序,使用该嵌套枚举填充选项组。查找注释
// Core of example. 以查看重要的行。好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package com.example.vaadinradiobuttons;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.annotations.Widgetset;
import com.vaadin.data.Property;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.util.Arrays;
import java.util.Collection;
/**
*
*/
@Theme ("mytheme" )
@Widgetset ("com.example.vaadinradiobuttons.MyAppWidgetset" )
public class MyUI extends UI {
@Override
protected void init ( VaadinRequest vaadinRequest ) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin( true );
setContent( layout );
// Core of example.
Collection<Person.VITAL_STATUS> v = Arrays.asList( Person.VITAL_STATUS.values() );
OptionGroup radios = new OptionGroup("Vital Status :" , v );
radios.setImmediate( true );
radios.addValueChangeListener( ( Property.ValueChangeEvent event ) -> {
Person.VITAL_STATUS vitalStatus = ( Person.VITAL_STATUS ) event.getProperty().getValue();
System.out.println("User selected a vital status name:" + vitalStatus.name() +", labeled:" + vitalStatus.toString() );
} );
layout.addComponent( radios );
}
@WebServlet ( urlPatterns ="/*" , name ="MyUIServlet" , asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class , productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}更灵活的方法
请注意,在上面的枚举中添加了
getTitle 方法。您可以使用任何所需的方法名称,但getName 或name 已在Java中被定义为枚举的一部分。好。
创建一个BeanItemContainer,填充枚举的实例,并告诉Vaadin提供显示文本的"属性"(用于反射地找到匹配的getter方法)的名称。
好。
考虑到文档有关覆盖
toString 的警告,这种方法除了更灵活之外,可能更明智。好。
1
2
3
4
5
6 BeanItemContainer<SomeClass.CRITTER_FILTER> radiosBic = new BeanItemContainer<SomeClass.CRITTER_FILTER>( SomeClass.CRITTER_FILTER.class );
radiosBic.addAll( Arrays.asList( SomeClass.CRITTER_FILTER.values() ) ); // Convert array of values to a `Collection` object.
this.filterRadios = new OptionGroup("Critter Filter:" , radiosBic );
this.filterRadios.setMultiSelect( false ); // Radio buttons are single-select.
this.filterRadios.setItemCaptionMode( AbstractSelect.ItemCaptionMode.PROPERTY );
this.filterRadios.setItemCaptionPropertyId("title" ); // Matches the getter method defined as part of the enum.这样可行。我希望它可以在Vaadin 6和7中使用。
好。
BeanItemContainer 方法的完整示例让我们调整上一节中显示的示例
Person 和Vaadin应用。好。
在
Person 类中,将toString 方法替换为JavaBeans属性gettergetCaptionText 。该方法的名称可以是任何名称,只要它与下面进一步在Vaadin应用程序中看到的对setItemCaptionPropertyId 的调用匹配即可。好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 package com.example.vaadinradiobuttons;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Basil Bourque
*/
public class Person {
// Members
String name;
Person.VITAL_STATUS vitalStatus;
public enum VITAL_STATUS {
LIVING("Alive and Kicking" ),
DECEASED("Dead" ),
UNKNOWN("DUNNO" );
private String captionText;
static public String CAPTION_TEXT_PROPERTY_NAME ="captionText"; //
VITAL_STATUS ( String t ) {
this.captionText = t;
}
// JavaBeans Property getter.
public String getCaptionText () {
return this.captionText;
}
}
// Constructor
public Person ( String nameArg , VITAL_STATUS vitalStatusArg ) {
this.name = nameArg;
this.vitalStatus = vitalStatusArg;
}
}Vaadin应用程序已更改为使用
BeanItemContainer 。通过调用setItemCaptionPropertyId ,您可以指定该容器中的哪个属性用作显示的文本。好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 package com.example.vaadinradiobuttons;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.annotations.Widgetset;
import com.vaadin.data.Property;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.util.Arrays;
import java.util.Collection;
/**
*
*/
@Theme ("mytheme" )
@Widgetset ("com.example.vaadinradiobuttons.MyAppWidgetset" )
public class MyUI extends UI {
@Override
protected void init ( VaadinRequest vaadinRequest ) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin( true );
setContent( layout );
// Core of example.
Collection<Person.VITAL_STATUS> v = Arrays.asList( Person.VITAL_STATUS.values() );
BeanItemContainer<Person.VITAL_STATUS> bic = new BeanItemContainer<>( Person.VITAL_STATUS.class , v );
OptionGroup radios = new OptionGroup("Vital Status :" , bic );
radios.setItemCaptionPropertyId(Person.VITAL_STATUS.CAPTION_TEXT_PROPERTY_NAME ); // …or… ("captionText" );
radios.setImmediate( true );
radios.addValueChangeListener( ( Property.ValueChangeEvent event ) -> {
Person.VITAL_STATUS vitalStatus = ( Person.VITAL_STATUS ) event.getProperty().getValue();
System.out.println("User selected a vital status name:" + vitalStatus.name() +", labeled:" + vitalStatus.toString() );
} );
layout.addComponent( radios );
}
@WebServlet ( urlPatterns ="/*" , name ="MyUIServlet" , asyncSupported = true )
@VaadinServletConfiguration ( ui = MyUI.class , productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}好。
病毒素Viritin是Vaadin的附加组件,它具有一个非常方便的字段,称为
EnumSelect 。 它可以从已编辑的属性中自动检测可用属性。 您还可以通过一个策略来显示用户界面上的标题。基本用法
1
2
3
4
5
6
7
8
9
10
11
12 EnumSelect<AddressType> select = new EnumSelect<AddressType>()
.withSelectType(OptionGroup.class);
select.setStyleName(ValoTheme.OPTIONGROUP_HORIZONTAL);
// The Enum type is detected when the edited property is bound to select
// This typically happens via basic bean binding, but here done manually.
ObjectProperty objectProperty = new ObjectProperty(AddressType.Home);
select.setPropertyDataSource(objectProperty);
// Alternatively, if not using databinding at all, you could just use
// basic TypedSelect, or the method from it
// select.setOptions(AddressType.values());请注意,当前发行版的打字方式受到限制。 我刚刚修复了该问题,显示的类型化api将在下一个版本中发布。