我创建了一个单元工厂包装器,以启用JavaFX表单元的自定义配置。请参见下面的代码。
package idmas.controller.modules.tableview; import idmas.model.Cml; import idmas.model.CmlDAO; import idmas.model.Damageloop; import idmas.model.modules.general.dateMethods; import java.util.Date; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.util.Callback; /** * Generic cell factory that can be used to override specific properties of cells, such as formatting (text size, color etc.) and even values. * @author Joshua Adams * @param <S> = Data type of table view to be modified. E.g. Damageloop, Equipment, CML, Inspection etc. * @param <T> = Data type of specific cell being overridden. E.g. Date, Integer, Double etc. */ public class CellFactoryCustom<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> { //Represents the name of the column for the specific tableview, i.e. Last Internal Inspection Date etc. String colname; //Constructors for class wide objects CmlDAO cmldao = new CmlDAO(); /** * Class constructor requires a column name to be passed so the specific formatting for that column's cells can be applied. * @param colname = Name of column to apply formatting to. */ public CellFactoryCustom (String colname) { this.colname = colname; } /** * Main method to override the properties of the cells for a table column. * @param arg * @return */ @Override public TableCell<S, T> call(TableColumn<S, T> arg) { TableCell<S, T> cell = new TableCell<S, T>() { @Override protected void updateItem(T item, boolean empty) { //super allows reffering to methods in superclass super.updateItem(item, empty); //recommended syntax for overridding the updateItem method - Refer to update item javadoc. if (empty || item == null) { setText(null); setGraphic(null); getStyleClass().removeAll("highlightCellBlack"); getStyleClass().add(getTableRow().getStyleClass().toString()); } else { setConditionalFormatting(this,item); } } }; return cell; } /** * Specific formatting rules which are applied to a columns cells based on its column name. * @param cell * @param item */ public void setConditionalFormatting (TableCell<S,T> cell, T item) { //Constructors for reference classes dateMethods datemethods = new dateMethods(); Date currentdateplus60 = new Date(); Date date; Damageloop damageloop; Cml cml; ComboBox combobox; //Current styles need to be removed to ensure all specific styles for each cell are applied correctly cell.getStyleClass().removeAll("highlightCellBlack"); //Switch statement selected over if statement to improve performance of code and readibility switch (colname) { case "colNextExternalInspectionDate": damageloop = (Damageloop) cell.getTableRow().getItem(); cell.setText(datemethods.getDateToString((Date) item,"dd/MM/yyyy")); currentdateplus60 = datemethods.getDatePlusDays(currentdateplus60, 60); date = (Date) item; if(date.before(currentdateplus60) && damageloop.getErrorcode() != 2 & damageloop.getErrorcode() != 3) { cell.getStyleClass().add("highlightCellBlack"); } else { cell.getStyleClass().add(cell.getTableRow().getStyleClass().toString()); } break; case "colNextInternalInspectionDate": damageloop = (Damageloop) cell.getTableRow().getItem(); cell.setText(datemethods.getDateToString((Date) item,"dd/MM/yyyy")); currentdateplus60 = datemethods.getDatePlusDays(currentdateplus60, 60); date = (Date) item; if(date.before(currentdateplus60) && damageloop.getErrorcode() != 1 && damageloop.getErrorcode() != 3) { cell.getStyleClass().add("highlightCellBlack"); } else { cell.getStyleClass().add(cell.getTableRow().getStyleClass().toString()); } break; case "colCmlStatus": cml = (Cml) cell.getTableRow().getItem(); String[] fieldsArray = new String[]{"C = Continue to Monitor", "S = Scoped", "X = Redundant"}; String[] disabledFieldsArray = new String[]{"S = Scoped"}; String SQLString = "UPDATE IDMAS.CML SET CMLSTATUS = '<NEW_STATUS>' WHERE EQUIPMENT_ID = '" + cml.getEquipmentId() + "' AND CML_NO = " + cml.getCmlNo(); String replacestring = "<NEW_STATUS>"; String defaultvalue = item.toString(); ComboBoxCustom comboboxcustom = new ComboBoxCustom (fieldsArray, disabledFieldsArray, SQLString, replacestring, defaultvalue); comboboxcustom.setPrefWidth(10); cell.setGraphic(comboboxcustom); break; case "colCmlStatusRemediation": ObservableList<String> options = FXCollections.observableArrayList( "A = Approved for Remediation", "C = Continue to Monitor", "F = Fit for Service", "R = Recommend Remediation" ); cml = (Cml) cell.getTableRow().getItem(); combobox = new ComboBox(options); combobox.setValue(item.toString()); combobox.setPrefWidth(10); combobox.valueProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue ov, String oldvalue, String newvalue) { cml.setCmlstatusremediation(newvalue.charAt(0)); cmldao.updateCml(cml); //Platform runlater required to update the combox with the new value Platform.runLater(() -> { combobox.setValue(String.valueOf(newvalue.charAt(0))); }); } }); cell.setGraphic(combobox); break; case "colTemporaryrepairinstalled": cml = (Cml) cell.getTableRow().getItem(); CheckBox checkbox = new CheckBox(); checkbox.setSelected(Boolean.valueOf(item.toString())); checkbox.selectedProperty().addListener(new ChangeListener<Boolean>() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { cml.setTemporaryrepairinstalled(newValue); cmldao.updateCml(cml); checkbox.setSelected(newValue); } }); cell.setGraphic(checkbox); break; default: break; } } }
这个cellfactory包装器通常可以在我所有的表视图中导航。但是,一旦我在系统中执行其他功能,例如添加新记录,编辑记录等,就会引发以下错误。
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException at idmas.controller.modules.tableview.CellFactoryCustom.setConditionalFormatting(CellFactoryCustom.java:110) at idmas.controller.modules.tableview.CellFactoryCustom$1.updateItem(CellFactoryCustom.java:60) at javafx.scene.control.TableCell.updateItem(TableCell.java:663) at javafx.scene.control.TableCell.indexChanged(TableCell.java:468) at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:116) at com.sun.javafx.scene.control.skin.TableRowSkinBase.requestCellUpdate(TableRowSkinBase.java:659) at com.sun.javafx.scene.control.skin.TableRowSkinBase.handleControlPropertyChanged(TableRowSkinBase.java:234) at com.sun.javafx.scene.control.skin.TableRowSkin.handleControlPropertyChanged(TableRowSkin.java:70) at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197) at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55) at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89) at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182) at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81) at javafx.beans.property.ReadOnlyIntegerPropertyBase.fireValueChangedEvent(ReadOnlyIntegerPropertyBase.java:72) at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:102) at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:113) at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:147) at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:115) at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1957) at com.sun.javafx.scene.control.skin.VirtualFlow.addTrailingCells(VirtualFlow.java:1344) at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1197) at javafx.scene.Parent.layout(Parent.java:1087) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Parent.layout(Parent.java:1093) at javafx.scene.Scene.doLayoutPass(Scene.java:552) at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2397) at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Toolkit.java:355) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:354) at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:381) at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510) at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490) at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191) at java.lang.Thread.run(Thread.java:745)
我似乎无法弄清楚为什么这个包装器认为我的CML类中包含空值。任何人对于为什么会抛出此Null Pointer异常有任何想法?
不能保证在第一次调用该方法时,该TableCell变量已经与a关联TableRow或TableRow已经被填充updateItem。
TableCell
TableRow
updateItem
在您的情况下,可能会getTableRow()返回null(但这意味着cml = (Cml) cell.getTableRow().getItem();抛出异常的那一行,而不是String SQLString = "UPDATE IDMAS.CML SET CMLSTATUS = '<NEW_STATUS>' WHERE EQUIPMENT_ID = '" + cml.getEquipmentId() + "' AND CML_NO = " + cml.getCmlNo(););至少在我尝试重现该错误时发生了。
getTableRow()
null
cml = (Cml) cell.getTableRow().getItem();
String SQLString = "UPDATE IDMAS.CML SET CMLSTATUS = '<NEW_STATUS>' WHERE EQUIPMENT_ID = '" + cml.getEquipmentId() + "' AND CML_NO = " + cml.getCmlNo();
但是如果
确实是导致错误的行,那么导致这种情况的唯一方法就是if cmlis null。
cml
解决此问题的一种方法是根据索引从表中获取项目:
cml = (Cml) cell.getTableView().getItems().get(getIndex());