I am trying to add a ComboBox into the TableView but for some reason I cannot make the conversion. Behind the scenes, I want to make the conversation if the value is
0 then it should display 'free' if the value is 1 then it will say 'taken' in the ComboBox, and once the user changes the value in the ComboBox
it will save its integer value.
I am not sure how to add the converter and it gives the following error at event.getNewValue():
cant convert int to string.
Any help where I am doing wrong?
private final IntegerProperty mode;
public int getMode() {
return mode.get();
}
public void setMode(int mode) {
this.mode.set(mode);
}
public IntegerProperty modeProperty() {
return mode;
}
Set<String> modeList = new HashSet<>();
modeList.add("Free");
modeList.add("Taken");
var converter=modeConverter();
TableColumn<Review, String> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter); //How to apply the converter.
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(FXCollections.observableList(modeList))));
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue()); //Method cannot be applied java.lang.String. But I already make the conversatin via modeConverter
});
...
private StringConverter modeConverter() {
return new StringConverter<Integer>() {
#Override
public String toString(Integer object) {
if (object == 0) {
return "FREE";
} else {
return "Taken";
}
}
#Override
public Integer fromString(String string) {
if (string.equalsIgnoreCase("free")) {
return 0;
} else {
return 1;
}
}
};
}
You need to use the overloaded method:
forTableColumn​(StringConverter converter,
ObservableList items)
Please note that ComboBox backing list should contain 0, 1 instead of Free, Taken. The converter is responsible for displaying 0 as Free and 1 as Taken.
Also, the TableColumn should be of type <Review, Integer> instead of <Review, String>.
In your code, you can do something as follows:
ObservableList<Integer> modeList = FXCollections.observableList(0, 1);
TableColumn<Review, Integer> modeCombo = new TableColumn("Mode");
modeCombo.setCellValueFactory(new PropertyValueFactory("mode"));
modeCombo.setCellFactory(ComboBoxTableCell.forTableColumn(converter, modeList)));
Once your basic type is fixed, the following should work:
modeCombo.setOnEditCommit(event -> {
mode.setOperationMode(event.getNewValue());
});
Related
In my table I have one cell that does not update without interaction with the table.
I found the reason already here Java: setCellValuefactory; Lambda vs. PropertyValueFactory; advantages/disadvantages
My problem is, the default value of the cells item is LocalDate.MIN and I want my cell to contain "---" as long as the item has this default value. When I update the item, I want the cell to contain the current date string.
Item Class:
public class ItemEv {
private final ObjectProperty<LocalDate> openedAt;
#XmlJavaTypeAdapter(LocalDateAdapter.class)
public final LocalDate getOpenedAt() {
return openedAt.get();
}
public final ObjectProperty<LocalDate> openedAtProperty() {
return this.openedAt;
}
public final void setOpenedAt(LocalDate openedAt) {
this.openedAt.set(openedAt);
}
}
in another CellFactory I set the new value: i.setOpenedAt(LocalDate.now());
this is working but not wanted:
openedAtColumnEv.setCellValueFactory(cellData -> cellData.getValue().openedAtProperty().asString());
and this is what I tried so far:
openedAtColumnEv.setCellValueFactory(new Callback<CellDataFeatures<ItemEv, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<ItemEv, String> i) {
if (i.getValue().getOpenedAt().equals(LocalDate.MIN)) {
return new SimpleStringProperty("---");
}
return i.getValue().openedAtProperty().asString();
}
});
and this:
openedAtColumnEv.setCellValueFactory(cellData -> {
if(cellData.getValue().openedAtProperty().getValue().equals(LocalDate.MIN)) {
return new SimpleStringProperty("---");
}
return cellData.getValue().openedAtProperty().asString();
});
Both of my tests return either SimpleStringProperty or StringBinding which should be fine.
In my tests I made a mistake where the first return in the IF statement does never return true, then the cell values show the standard string for LocalDate.MIN and get updated immediately when the item property changes.
Im a bit lost on this. Please forgive my bad english, Im not a native speaker.
If the property in the model class is an ObjectProperty<LocalDate>, then the column should be a TableColumn<ItemEv, LocalDate>, not a TableColumn<ItemEv, String>.
Implementing the cellValueFactory directly (typically with a lambda expression) is always preferable to using the legacy PropertyValueFactory class. You never "need to use" a PropertyValueFactory (and never should).
The cellValueFactory is only used to determine what data to display. It is not used to determine how to display the data. For the latter, you should use a cellFactory.
So:
private TableColumn<ItemEv, LocalDate> opendAtColumnEv ;
// ...
openedAtColumnEv.setCellValueFactory(cellData -> cellData.getValue().openedAtProperty());
openedAtColumnEv.setCellFactory(column -> new TableCell<ItemEv, LocalDate>() {
#Override
protected void updateItem(LocalDate openedAt, boolean empty) {
super.updateItem(openedAt, empty);
if (openedAt == null || empty) {
setText("");
} else {
if (openedAt.equals(LocalDate.MIN)) {
setText("---");
} else {
// Note you can use a different DateTimeFormatter as needed
setText(openedAt.format(DateTimeFormatter.ISO_LOCAL_DATE));
}
}
}
});
i have a data model "Rule"
A Rule consists of 1-x String parts saved as a List and an boolean value weather the rule is active or not.
To show this in my UI i want to add a TableView with 2 Columns.
Column 1 should display the Rule Text as a whole, but heavily customized. In the cell i add a textfield for each rule part which then get binded to the StrinProperty (Thats why i need a List of String Properties.
The 2. column should display a checkbox to activate or deactivate the rule (this is no problem an works fine)
Before my rule Model had the boolean isActive flag i used a Listview which had the whole Rule model class as Object. I made my own ListCell implementation and overrode updateItem(Object item, boolean isEmpty) to customize the cell to look like this:
I want the tablecell in column 1 to look exactly how the listcell in my listview looked.
Because ListCell and Tablecell both inherit from IndexedCell i saw no problem in my way of changing the visual of the cell.
My problem is to bind the new datamodel to the table:
private TableView<Rule> tvRules;
this.tvRules = new TableView<Rule>();
this.tvRules.setPrefSize(GuiCore.prefWidth * 0.32, GuiCore.prefHeight * 0.32);
this.tvRules.setEditable(true);
headerBoxLbl = new Label("Active");
headerBox = new CheckBox();
headerBoxLbl.setGraphic(headerBox);
headerBoxLbl.setContentDisplay(ContentDisplay.RIGHT);
headerBox.setOnAction(e -> this.changeAllActiveBoxes());
rulePartsColumn = new TableColumn<Rule, List<SimpleStringProperty>>("Rule");
rulePartsColumn.setCellFactory((callback) -> new RuleTableCell());
rulePartsColumn.setCellValueFactory(cellData -> cellData.getValue().getRulePartsProperty());
rulePartsColumn.setResizable(false);
rulePartsColumn.prefWidthProperty().bind(this.widthProperty().multiply(0.8));
isActiveColumn = new TableColumn<Rule, Boolean>();
isActiveColumn.setCellValueFactory(cellData -> cellData.getValue().getIsActiveProperty());
isActiveColumn.setCellFactory(cellData -> new CheckBoxTableCell<>());
isActiveColumn.setResizable(false);
isActiveColumn.prefWidthProperty().bind(this.widthProperty().multiply(0.2));
isActiveColumn.setStyle( "-fx-alignment: CENTER;");
isActiveColumn.setGraphic(headerBoxLbl);
this.tvRules.getColumns().addAll(rulePartsColumn, isActiveColumn);
As you see i create 2 Columns with the TableDataType Rule, one with Boolean type and one with the List as Data type.
The problem ist that i dont get the binding of the rulePartsColumn to the rule Model to work:
I really dont know how to bind this so in the cell i can work with a List of StringProperties (or SimpleStringProperties).
For reference my Model class Rule:
public class Rule {
private SimpleListProperty<SimpleStringProperty> ruleParts;
private SimpleBooleanProperty isActive;
public Rule() {
this(true, Arrays.asList("", "=", ""));
}
public Rule(final boolean isActive, final List<String> ruleParts) {
this.isActive = new SimpleBooleanProperty(isActive);
this.ruleParts = new SimpleListProperty<SimpleStringProperty>(FXCollections.observableArrayList());
for(int i = 0; i < ruleParts.size(); i++) {
this.ruleParts.add(new SimpleStringProperty(ruleParts.get(i)));
}
}
public SimpleListProperty<SimpleStringProperty> getRulePartsProperty() {
return this.ruleParts;
}
public List<SimpleStringProperty> getRulePartsProperties() {
return (List<SimpleStringProperty>)this.ruleParts;
}
public List<String> getRuleParts() {
List<String> parts = new ArrayList<String>();
for(int i = 0; i < this.ruleParts.size(); i++) {
parts.add(this.ruleParts.get(i).get());
}
return parts;
}
public SimpleBooleanProperty getIsActiveProperty() {
return this.isActive;
}
public boolean isActive() {
return isActive.get();
}
public void setActive(boolean isActive) {
this.isActive.set(isActive);
}
}
Thanks in advance
I have an Item object having 4 String fields and 3 boolean fields.
I have to construct this object based on the 3 boolean variables.
The target is whenever any one of the boolean variable is true we have to create the object having that/those boolean variable set.
If for any situation none of the boolean variables are true, we wont create the object.
I am using a COR to check whether any of the boolean fields will be set or not based on some business logic.
I was trying this with builder, but then I have to construct so many objects and later discard them when none of the boolean variables found true.
Can anyone have any better idea, to solve this kind of problem ?
Well thanks for the 2 delete flag for this question. Thank for the thoughts on this question as well.
I did something to achieve what I want. Which is quite flexible I believe. Only part if there is a dependency on If loop, but that is acceptable since Report class can have extra boolean so when that class is changed, it's builder should be touched to cater that change. Rest this is flexible which I wanted.
public class Report {
private String acftNo;
private Date plannedDate;
private String plannedStn;
private Integer mntncId;
private Set<String> capableStations;
private String routedStn;
private boolean isRoutedNEQPlannedStn; //Inconsistency type 1
private boolean isCapableAtPlannedStn; //Inconsistency type 2
private boolean isPlannedOrRoutedStationExists; //Inconsistency type 3/5
public Report(String acftNo, Integer mntncId) {
super();
this.acftNo = acftNo;
this.mntncId = mntncId;
}
public Report(String acftNo, Date plannedDate, String plannedStn,
Integer mntncId) {
super();
this.acftNo = acftNo;
this.plannedDate = plannedDate;
this.plannedStn = plannedStn;
this.mntncId = mntncId;
}
//setters and getters. Removed for space.
public static Report buildReport(Maintenance<?> task, Set<InconsistencyReport> enumSet) {
Report temp = new Report(task.getAssignment().getAircraftNumber(),task.getAssignment().getMntncScheduleDate(),
task.getAssignment().getStationCode(),task.getAssignment().getMntncId());
temp.setCapableStations(InconsistencyReport.getCapableStations(task));
for(InconsistencyReport ir : enumSet)
{
if(ir.compareTo(InconsistencyReport.ROUTED_STN_NEQ_PLANNED_STN)==0)
temp.setRoutedNEQPlannedStn(true);
if(ir.compareTo(InconsistencyReport.ITEM_NT_CAPABLE_AT_PLANNED_STN)==0)
temp.setCapableAtPlannedStn(true);
if(ir.compareTo(InconsistencyReport.NO_ROUTD_STN_ON_A_DATE)==0)
temp.setPlannedOrRoutedStationExists(true);
}
return temp;
}
}
calculateInconsitencyReport() method which will decide whether to create object or not.
public class InconsistencyReportChain {
public enum InconsistencyReport implements InconsistencyReportIface {
ROUTED_STN_NEQ_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task ) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
ITEM_NT_CAPABLE_AT_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
NO_ROUTD_STN_ON_A_DATE {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true
return false;
}
};
#Override
public boolean validate(Maintenance<?> task) {
return !(null == task.getAssignment());
}
static Set<String> getCapableStations(Maintenance<?> task)
{
Set<String> capableStations = newHashSet();
if(task.getCapStationList() != null)
{
capableStations.addAll(Arrays.asList(task.getCapStationList().split(StringConstants.COMMA_SPLIT_REGEX)));
}
if(task.getCapStationClassList() != null)
{
Map<String, List<String>> stationClassMap = CacheManager.get(STN_CLASS.name());
List<String> stationClass = Arrays.asList(task.getCapStationClassList().split(StringConstants.COMMA_SPLIT_REGEX));
for(String stnClass : stationClass)
{
capableStations.addAll(stationClassMap.get(stnClass));
}
}
return capableStations;
}
}
public static Report calculateInconsitencyReport(Maintenance<?> task) {
Set<InconsistencyReport> enumSet = null;
for(InconsistencyReport iReport : InconsistencyReport.values())
{
if(iReport.findInconsistency(task))
{
if(null==enumSet)
enumSet = EnumSet.of(iReport);
else
enumSet.add(iReport);
}
}
if(null!= enumSet && enumSet.size() > 0)
return Report.buildReport(task,enumSet);
return null;
}
}
Helper Interface:
public interface InconsistencyReportIface {
public boolean findInconsistency(Maintenance<?> task );
public boolean validate(Maintenance<?> task );
}
Details of class logic is teared off because of security.
What is the problem? Just create your object when one of your booleans is true.
if(bool1 || bool2 || bool3) {
item = new Item(str1, str2, str3, str4, bool1, bool2, bool3);
}
From what I understand of your description:
a) you will have some bools that will determine wether you create a certain object or not.
b) you may have to include some more bools into the "check protocol"
c) you have to do this checking in a loop where
i/ you check for the bool variable
ii/ you check if the object had been created previously
I still don't quite get it yet, but.. that looks pretty straight forward to me. Let's say your bools are stored in a boolean array boolean[] bools and your strings in a string array String[] strings (which, btw, I don't know what they are used for). You are saying to check if every bool is true and then create an object based on that result.
boolean[] bools = new boolean[] { ... };
String[] strings = new String[] { ... };
boolean checks = false;
for(int i = 0; i<bools.length && !checks; i++)
checks = bools[i];
//so far we will have processed if any of the bools was false, which was your condition
if(checks)
Object object = new Object(); //create your desired object
I don't understand why you would need to check if the object has been constructed previously, though, so I didn't include it in my suggestion :P
I've an enum like this:
public enum ChartType
{
TABLE(0, false), BAR(1, false), COLUMN(3, false)
private int type;
private boolean stacked;
ChartType(int type, boolean stacked)
{
this.type = type;
this.stacked = stacked;
}
public int getType()
{
return type;
}
public boolean isStacked()
{
return this.stacked;
}
}
I get a charttype (int values like 0,1,3) from the request and want the matching input
Something along these lines. Not sure if syntax is 100 percent, but it demos the idea.
public ChartType getChartypeForValue(int value)
for(ChartType type : ChartType.values()){
if(type.getType() == value){
return type;
}
}
return null;
}
You can add map in your enum to store relationship between type and specific enum value. You can fill it once in static block after all values will be created like:
private static Map<Integer, ChartType> typeMap = new HashMap<>();
static{
for (ChartType chartType: values()){
typeMap.put(chartType.type, chartType);
}
}
then you can add method which will use this map to get value you want, or null if there isn't any
public static ChartType getByType(int type) {
return typeMap.get(type);
}
You can use it like
ChartType element = ChartType.getByType(1);
You should create a static method to retrieve a type by number. With just a few charts like this, it's simplest to do that by just running through all the options. For larger enums, you could create a map for quick lookup. The only thing to watch for here is that any static initializers aren't run until after the values have been created.
Simple approach:
public static ChartType fromType(int type) {
// Or for (ChartType chart : ChartType.getValues())
for (ChartType chart : EnumSet.allOf(ChartType.class)) {
if (chart.type == type) {
return chart;
}
}
return null; // Or throw an exception
}
If you use java 8, use stream() and filter()
int value =2;
Optional<ChartType> chartType = Arrays.asList(ChartType.values()).stream().
filter(c -> c.type == value).findFirst();
if(chartType.isPresent()){
ChartType chartType =chartType.get();
//
}
Define new method:
public ChartType valueOf(int id) {
switch(id) {
case 1:
return TABLE;
case 2:
return BAR;
case 3:
return COLUMN;
}
return null;
}
Example:
ChartType.valueOf(1) // TABLE
Is there any way to add clickHandlers (or any type of handler) to the headers of the columns in a CellTable? I want to add some sorting functionality to my CellTable and I dont see any methods in the Column or Header classes that will allow this. I used this post to figure out how to use the CellTable.
Workaround for click events:
Header<String> columnHeader = new Header<String>(new ClickableTextCell()) {
#Override
public String getValue() {
return columnName;
}
};
columnHeader.setUpdater(new ValueUpdater<String>() {
#Override
public void update(String value) {
Window.alert("Header clicked!");
}
});
table.addColumn(column, columnHeader);
There is no out of the box way of supporting sort as yet on the CellTable. However there is a manual workaround involving a lot of code drudgery. Refer the classes SortableHeader and SortableColumn in the bike shed under expenses sample. You will find the usage in com.google.gwt.sample.expenses.gwt.client.ExpenseDetails. You can use this until something concrete comes out in the next release.
check out directory: http://google-web-toolkit.googlecode.com/svn/trunk/bikeshed
With the final release of GWT 2.1, has there been any support for sortable columns added to the CellTable? Or is it still a roll your own solution after looking at the bikeshed example?
CellTable<Contact> table = new CellTable<Contact>();
// Create name column.
final TextColumn<Contact> nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.name;
}
};
// Create a data provider.
ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
// Connect the table to the data provider.
dataProvider.addDataDisplay(table);
final List<Contact> list = dataProvider.getList();
for (Contact contact : CONTACTS) {
list.add(contact);
}
final ListHandler<Contact> columnSortHandler = new ListHandler<Contact>(
list);
Header<String> columnHeader = new Header<String>(new ClickableTextCell()) {
#Override
public String getValue() {
return "Name";
}
};
columnHeader.setUpdater(new ValueUpdater<String>() {
#Override
public void update(String value) {
if (Window.confirm("Want to do?")){
nameColumn.setSortable(true);
columnSortHandler.setComparator(nameColumn,
new Comparator<Contact>() {
public int compare(Contact o1, Contact o2) {
if (o1 == o2) {
return 0;
}
// Compare the name columns.
if (o1 != null) {
return (o2 != null) ? o1.name.compareTo(o2.name) : 1;
}
return -1;
}
});
} else nameColumn.setSortable(false);
}
});
// Make the name column sortable.
nameColumn.setSortable(false);
// Create address column.
TextColumn<Contact> addressColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.address;
}
};
// Add the columns.
table.addColumn(nameColumn, columnHeader);
table.addColumn(addressColumn, "Address");
// Add the data to the data provider, which automatically pushes it to the
// widget.
// Add a ColumnSortEvent.ListHandler to connect sorting to the
// java.util.List.
//------------------ Code to add --------------------------------//
VerticalPanel vp = new VerticalPanel();
table.addColumnSortHandler(columnSortHandler);
//------------------ Code end --------------------------------//
// We know that the data is sorted alphabetically by default.
table.getColumnSortList().push(nameColumn);
// Add it to the root panel.
vp.add(table);
RootPanel.get().add(vp);