禁用 JavaFX 中的 TableCell,在设置 setOnEditCommit 后。

huangapple go评论114阅读模式
英文:

disable TableCell after setOnEditCommit JavaFX

问题

我已经创建了一个可编辑的表格,其中有一列(quantityColumn)的单元格应该在执行setOnEditCommit后变为禁用状态,不再可编辑。换句话说,他们只允许输入一次。程序有一个添加行按钮,用于向表格中添加新行,并且setOnEditCommit会使用每个单元格中的新值更新数据库。

以下是代码示例:

public class TableViewController implements Initializable {

    // 初始化代码...

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // 初始化代码...

        quantityColumn.setOnEditCommit(event -> {
            Item user = event.getRowValue();
            user.setQuantity(event.getNewValue());
            updateQuantity("Quantity", event.getNewValue(), user.getID());

            // 在此禁用已提交的单元格
            TableCell<Item, String> cell = event.getTablePosition().getTableColumn().getCellFactory().call(quantityColumn);
            cell.setEditable(false);
        });

        // 其他的初始化代码...
    }

    // 更新数据库的方法...
}

public class JFXTextFieldCell extends TableCell<Item, String> {
    // JFXTextFieldCell 类的实现...
}

注意:在设置单元格不可编辑之后,可能需要一种方式来重新启用它们,具体根据你的业务需求来实现。

英文:

I have created an editable table that has a certain column (quantityColumn) that only this column's cells should be disabled and not editable anymore when setOnEditCommit is done. So in other words they are only allowed to enter once. The program has an add row button that adds new rows to the table and setOnEditCommit updates the database with the new values from each cell in each column.

An example of how my code looks like:

public class TableViewController implements Initializable {

/**
 * Initializes the controller class.
 */
private Connection c = ConnectDB.getInstance().getConnection(); // ConnectDB is a helper that connects to the database
@FXML
private TableView&lt;Item&gt; table;

@FXML
private TableColumn&lt;Item, LocalDate&gt; dateColumn;

@FXML
private TableColumn&lt;Item, LocalTime&gt; timeColumn;

@FXML
private TableColumn&lt;Item, String&gt; quantityColumn;

@Override
public void initialize(URL url, ResourceBundle rb) {


 //the following three are custom TableCell classes that allows me to make my cell have a JFX date and time picker.
    
    Callback&lt;TableColumn&lt;Item, LocalDate&gt;, TableCell&lt;Item, LocalDate&gt;&gt; cellFactoryDate = 
            (TableColumn&lt;Item, LocalDate&gt; param) -&gt; new DatePickerCell();
    Callback&lt;TableColumn&lt;Item, LocalTime&gt;, TableCell&lt;Item, LocalTime&gt;&gt; cellFactoryTime = 
            (TableColumn&lt;Item, LocalTime&gt; param) -&gt; new TimePickerCell();
    Callback&lt;TableColumn&lt;Item, String&gt;, TableCell&lt;Item, String&gt;&gt; cellFactoryText = 
            (TableColumn&lt;Item, String&gt; param) -&gt; new JFXTextFieldCell(); 


    dateColumn.setCellValueFactory(cellData -&gt; cellData.getValue().weekDateProperty());
    timeColumn.setCellValueFactory(cellData -&gt; cellData.getValue().timeProperty());
    quantityColumn.setCellValueFactory(cellData -&gt; cellData.getValue().quantityProperty());
    
    dateColumn.setCellFactory(cellFactoryDate);
    timeColumn.setCellFactory(cellFactoryTime);   
    quantityColumn.setCellFactory(cellFactoryText);   

    table.setEditable(true);

    dateColumn.setOnEditCommit(event -&gt; {
        Item user = event.getRowValue();
        user.setWeekDate(event.getNewValue());
        updateDate(&quot;WeekDate&quot;, event.getNewValue(), user.getID());
    });
    timeColumn.setOnEditCommit(event -&gt; {
        Item user = event.getRowValue();
        user.setTime(event.getNewValue());
        updateTime(&quot;Time&quot;, event.getNewValue(),  user.getID());
    });
    quantityColumn.setOnEditCommit(event -&gt; {
        Item user = event.getRowValue();
        user.setQuantity(event.getNewValue());
        updateQuantity(&quot;Quantity&quot;, event.getNewValue(),  user.getID());

        //I want to disable the cell that has been committed here
    });

}

private void updateDate(String column, LocalDate date, int id) {
    
    try {
        
        PreparedStatement stmt = c.prepareStatement(&quot;UPDATE Items SET &quot;+column+&quot; = ? WHERE ID = ? &quot;);
        stmt.setDate(1, java.sql.Date.valueOf(date));
        stmt.setInt(2, id);
        stmt.executeUpdate();
    } catch (SQLException ex) {
        System.err.println(&quot;Error&quot;);
        ex.printStackTrace(System.err);
    }
}

private void updateTime(String column, LocalTime time, int id) {
    
    try {
        
        PreparedStatement stmt = c.prepareStatement(&quot;UPDATE Items SET &quot;+column+&quot; = ? WHERE ID = ? &quot;);
        stmt.setTime(1, java.sql.Time.valueOf(time));
        stmt.setInt(2, id);
        stmt.executeUpdate();
    } catch (SQLException ex) {
        System.err.println(&quot;Error&quot;);
        ex.printStackTrace(System.err);
    }
}

private void updateQuantity(String column, String quantity, int id) {
    
    try {
        
        PreparedStatement stmt = c.prepareStatement(&quot;UPDATE Items SET &quot;+column+&quot; = ? WHERE ID = ? &quot;);
        stmt.setString(1, quantity);
        stmt.setInt(2, id);
        stmt.executeUpdate();
    } catch (SQLException ex) {
        System.err.println(&quot;Error&quot;);
        ex.printStackTrace(System.err);
    }
}
 @FXML
 private void addRow(ActionEvent event) {

    // get current position
    TablePosition pos = table.getFocusModel().getFocusedCell();
    
    // clear current selection
    table.getSelectionModel().clearSelection();

    // create new record and add it to the model
    Item data = new Item();
    table.getItems().add(data);
    // get last row
    int row = table.getItems().size() - 1;
    table.getSelectionModel().select( row, pos.getTableColumn());

    // scroll to new row
    table.scrollTo( data);

 }

}

Item:

public class Item {

private final IntegerProperty id;
private final ObjectProperty&lt;LocalDate&gt; weekDate;
private final ObjectProperty&lt;LocalTime&gt; time;
private final StringProperty quantity;


public Item() {
    this(0, null, null, null);
}
/**
 * Constructor with some initial data.
 * @param id
 * @param weekDate
 * @param time
 * @param quantity
 * 
 */
public Item(int id, LocalDate weekDate, LocalTime time, String quantity) {            
    this.id = new SimpleIntegerProperty(id);
    this.weekDate = new SimpleObjectProperty(weekDate);
    this.time = new SimpleObjectProperty(time);
    this.quantity = new SimpleStringProperty(quantity);

}
    

public int getID() {
    return id.get();
}

public void setID(int id) {
    this.id.set(id);
}

public IntegerProperty idProperty() {
    return id;
}

public LocalDate getWeekDate() {
    return weekDate.get();
}

public void setWeekDate(LocalDate weekDate) {
    this.weekDate.set(weekDate);
}

public ObjectProperty&lt;LocalDate&gt; weekDateProperty() {
    return weekDate;
}


public LocalTime getTime() {
    return time.get();
}

public void setTime(LocalTime time) {
    this.time.set(time);
}

public ObjectProperty&lt;LocalTime&gt; timeProperty() {
    return time;
}

public String getQuantity() {
    return quantity.get();
}

public void setQuantity(String quantity) {
    this.quantity.set(quantity);
}

public StringProperty quantityProperty() {
    return quantity;
}
}

TableView FXML

&lt;AnchorPane id=&quot;AnchorPane&quot; prefHeight=&quot;400.0&quot; prefWidth=&quot;600.0&quot; xmlns=&quot;http://javafx.com/javafx/8.0.171&quot; xmlns:fx=&quot;http://javafx.com/fxml/1&quot; fx:controller=&quot;disablecelltest.TableViewController&quot;&gt;
   &lt;children&gt;
      &lt;TableView fx:id=&quot;table&quot; editable=&quot;true&quot; layoutX=&quot;61.0&quot; layoutY=&quot;70.0&quot; prefHeight=&quot;292.0&quot; prefWidth=&quot;479.0&quot;&gt;
        &lt;columns&gt;
          &lt;TableColumn fx:id=&quot;dateColumn&quot; prefWidth=&quot;75.0&quot; text=&quot;Date&quot; /&gt;
          &lt;TableColumn fx:id=&quot;timeColumn&quot; prefWidth=&quot;75.0&quot; text=&quot;Time&quot; /&gt;
            &lt;TableColumn fx:id=&quot;quantityColumn&quot; prefWidth=&quot;75.0&quot; text=&quot;Quantity&quot; /&gt;
        &lt;/columns&gt;
      &lt;/TableView&gt;
      &lt;JFXButton buttonType=&quot;RAISED&quot; contentDisplay=&quot;TEXT_ONLY&quot; graphicTextGap=&quot;10.0&quot; layoutX=&quot;10.0&quot; layoutY=&quot;10.0&quot; onAction=&quot;#addRow&quot; text=&quot;ADD RECORD&quot; textAlignment=&quot;CENTER&quot;&gt;
         &lt;font&gt;
            &lt;Font name=&quot;Dosis SemiBold&quot; size=&quot;18.0&quot; /&gt;
         &lt;/font&gt;
      &lt;/JFXButton&gt;
   &lt;/children&gt;
&lt;/AnchorPane&gt;

I have tried looking for answers here. This is one answer I tried and modified it without condition it disabled the whole column not that certain cell as I don't have a condition for the item I just want to disable when its been entered only once. I tried this as well.

EDIT:
This is the custom JFXTextField I'm using for quantity:

public class JFXTextFieldCell extends TableCell&lt;Item, String&gt; {

        private JFXTextField textField;

        public JFXTextFieldCell() {
        }

        @Override
        public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                createTextField();
                setText(null);
                setGraphic(textField);
                textField.selectAll();
            }
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();

            setText((String) getItem());
            setGraphic(null);
        }

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if (empty) {
                setText(item);
                setGraphic(null);
            } else {
                if (isEditing()) {
                    if (textField != null) {
                        textField.setText(getString());
//                        setGraphic(null);
                    }
                    setText(null);
                    setGraphic(textField);
                } else {
                    setText(getString());
                    setGraphic(null);
                }
            }
        }

        private void createTextField() {
            textField = new JFXTextField(getString());
            textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
            textField.setOnAction((e) -&gt; commitEdit(textField.getText()));
            textField.focusedProperty().addListener((ObservableValue&lt;? extends Boolean&gt; observable, Boolean oldValue, Boolean newValue) -&gt; {
                if (!newValue) {
                    commitEdit(textField.getText());
                }
            });
        }

        private String getString() {
            return getItem() == null ? &quot;&quot; : getItem();
        }
}

答案1

得分: 2

仅提供翻译内容,不包含其他额外的信息。

只是简单地禁用(或使当前单元格无法编辑)是行不通的;如果用户在表格中滚动,单元格将被重新分配,因此错误的单元格最终会变为不可编辑。

您需要向模型添加一些属性(或将某些属性存储在其他地方,可以通过模型实例访问),并实现一个自定义单元格,该单元格观察这些属性,适当地更新可编辑状态。

类似以下内容应该可以工作:

ObservableSet<Item> quantityEditedItems = FXCollections.observableSet();
    
quantityColumn.setCellFactory(tc -> new TextFieldTableCell<>(new IntegerStringConverter()) {
    @Override
    public void updateItem(Integer quantity, boolean empty) {
        super.updateItem(quantity, empty) ;
        editableProperty().unbind();
        if (empty) {
            setEditable(false);
        } else {
            editableProperty().bind(Bindings.createBooleanBinding(() ->
                ! quantityEditedItems.contains(getTableView().getItems().get(getIndex())),
                quantityEditedItems));
        }
    }
});

然后您可以执行以下操作

```java
quantityColumn.setOnEditCommit(event -> {
    Item item = event.getRowValue();
    item.setQuantity(event.getNewValue());
    updateQuantity("Quantity", event.getNewValue(), item.getID());

    quantityEditedItems.add(event.getRowValue());
});

这里是一个完整的工作示例:

import java.util.Random;
// ...(其他导入)

public class EditOnceTable extends Application {
    // ...(之前的代码)

    public static class Item {
        // ...(之前的代码)
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

以上是您提供的代码的翻译内容。如果您有任何其他问题或需要进一步的帮助,请随时提问。

英文:

Simply disabling (or making uneditable) the current cell won't work; cells will be reassigned e.g. if the user scrolls around the table, so the wrong cells would end up not editable.

You will need to add some property to your model (or store some properties elsewhere which can be accessed via the model instances) and implement a custom cell which observes those properties, updating the editable state appropriately.

Something like the following should work:

ObservableSet&lt;Item&gt; quantityEditedItems = FXCollections.observableSet();

quantityColumn.setCellFactory(tc -&gt; new TextFieldTableCell&lt;&gt;(new IntegerStringConverter()) {
    @Override
    public void updateItem(Integer quantity, boolean empty) {
        super.updateItem(quantity, empty) ;
        editableProperty().unbind();
        if (empty) {
            setEditable(false);
        } else {
            editableProperty().bind(Bindings.createBooleanBinding(() -&gt;
                ! quantityEditedItems.contains(getTableView().getItems().get(getIndex())),
                quantityEditedItems));
        }
    }
});

and then you can do

quantityColumn.setOnEditCommit(event -&gt; {
    StudentPresc user = event.getRowValue(); // should this be Item?
    user.setQuantity(event.getNewValue());
    updateQuantity(&quot;Quantity&quot;, event.getNewValue(),  user.getID());

    quantityEditedItems.add(event.getRowValue());
});

Here's a complete working example:

import java.util.Random;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener.Change;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.IntegerStringConverter;

public class EditOnceTable extends Application {

	@Override
	public void start(Stage primaryStage) throws Exception {
		
		TableView&lt;Item&gt; table = new TableView&lt;&gt;();
		table.setEditable(true);
		TableColumn&lt;Item, String&gt; itemColumn = new TableColumn&lt;&gt;(&quot;Item&quot;);
		itemColumn.setCellValueFactory(cellData -&gt; cellData.getValue().nameProperty());
		
		TableColumn&lt;Item, Integer&gt; quantityColumn = new TableColumn&lt;&gt;(&quot;Quantity&quot;);
		quantityColumn.setCellValueFactory(cellData -&gt; cellData.getValue().quantityProperty().asObject());
		quantityColumn.setEditable(true);
		
		table.getColumns().add(itemColumn);
		table.getColumns().add(quantityColumn);
		
		ObservableSet&lt;Item&gt; quantityEditedItems = FXCollections.observableSet();
		
		quantityColumn.setCellFactory(tc -&gt; new TextFieldTableCell&lt;&gt;(new IntegerStringConverter()) {
		    @Override
		    public void updateItem(Integer quantity, boolean empty) {
		        super.updateItem(quantity, empty) ;
		        editableProperty().unbind();
		        if (empty) {
		            setEditable(false);
		        } else {
		            editableProperty().bind(Bindings.createBooleanBinding(() -&gt;
		                ! quantityEditedItems.contains(getTableView().getItems().get(getIndex())),
		                quantityEditedItems));
		        }
		    }
		});
		
		quantityColumn.setOnEditCommit(event -&gt; {
		    Item item = event.getRowValue(); // should this be Item?
		    item.setQuantity(event.getNewValue());
//		    updateQuantity(&quot;Quantity&quot;, event.getNewValue(),  user.getID());

		    quantityEditedItems.add(event.getRowValue()) ;
		});
		
		ListView&lt;Item&gt; editedItemsView = new ListView&lt;&gt;();
		quantityEditedItems.addListener((Change&lt;? extends Item&gt; change) -&gt; 
			editedItemsView.getItems().setAll(quantityEditedItems)
		);
		editedItemsView.setCellFactory(lv -&gt; new ListCell&lt;&gt;() {
			@Override
			protected void updateItem(Item item, boolean empty) {
				super.updateItem(item, empty);
				if (empty || item==null) {
					setText(&quot;&quot;);
				} else {
					setText(item.getName());
				}
			}
		});
	    Button clear = new Button(&quot;Clear edited&quot;);
	    clear.setOnAction(e -&gt; quantityEditedItems.clear());
		
		Random rng = new Random();
		for (int i = 1 ; i &lt;= 40 ; i++) {
			table.getItems().add(new Item(&quot;Item &quot;+i, rng.nextInt(100)));
		}
		
		BorderPane root = new BorderPane(table);
		root.setRight(new VBox(5, new Label(&quot;Edited:&quot;), editedItemsView, clear));
		
		Scene scene = new Scene(root);
		primaryStage.setScene(scene);
		primaryStage.show();
	}
	
	
	public static class Item {
		private final StringProperty name = new SimpleStringProperty() ;
		private final IntegerProperty quantity = new SimpleIntegerProperty();
		
		public Item(String name, int quantity) {
			setName(name);
			setQuantity(quantity);
		}
		
		public StringProperty nameProperty() {
			return name ;
		}
		
		public final String getName() {
			return nameProperty().get();
		}
		
		public final void setName(String name) {
			nameProperty().set(name);
		}
		
		public IntegerProperty quantityProperty() {
			return quantity ;
		}
		
		public final int getQuantity() {
			return quantityProperty().get();
		}
		
		public final void setQuantity(int quantity) {
			quantityProperty().set(quantity);
		}
	}

	public static void main(String[] args) {
		Application.launch(args);
	}

}

huangapple
  • 本文由 发表于 2020年5月19日 19:41:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/61890166.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定