TableView,自定义单元格编辑行为

Posted

技术标签:

【中文标题】TableView,自定义单元格编辑行为【英文标题】:TableView, custom cell editing behaviour 【发布时间】:2015-02-04 21:55:14 【问题描述】:

我有这个适用于 TableView 的应用程序。它添加/删除新的行或列并为每个单元格存储数据。

public class MyTable extends Application 
private BorderPane root;
private HBox hBox;
private TableView<MyData> tableView;    
private Button buttAddColumn, buttDeleteColumn;
private Button buttAddRow, buttDeleteRow;
private int numberOfColumns = 0;
private ObservableList<MyData> dataList = FXCollections.observableArrayList();
@Override
public void init() throws Exception 
    root = new BorderPane();
    hBox = new HBox();
    buttAddColumn = new Button("Add columns");
    buttDeleteColumn = new Button("Delete columns");
    buttAddRow = new Button("Add rows");
    buttDeleteRow = new Button("Delete rows");
    tableView = new TableView<>();

    tableView.setEditable(true);
    tableView.setItems(dataList);
    hBox.getChildren().addAll(buttAddColumn,buttDeleteColumn,buttAddRow,buttDeleteRow);

    root.setTop(hBox);
    root.setCenter(tableView);


    //----------------------------------------------------------------
    buttAddColumn.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent event) 
            buttAddColumnAction(event);
        
    );
    buttDeleteColumn.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent event) 
            buttDeleteColumnAction(event);
        
    );
    buttAddRow.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent event) 
            buttAddRowAction(event);
        
    );
    buttDeleteRow.setOnAction(new EventHandler<ActionEvent>() 
        @Override
        public void handle(ActionEvent event) 
            buttDeleteRowAction(event);
        
    );


private void buttAddColumnAction(ActionEvent event)        
    int i = numberOfColumns;// thats the key for lambda expression. Unicate number for column to access its variable;

    if(dataList.size() > 0)//resizing each data object with new variable
        for(MyData x: dataList)
            x.addNew(i);
    TableColumn<MyData, Integer> newColumn = new TableColumn<>("#" + String.valueOf(++numberOfColumns));    
    newColumn.setCellValueFactory(cellData -> cellData.getValue().getCellValue(i).asObject());
    newColumn.setCellFactory(TextFieldTableCell.<MyData, Integer>forTableColumn(new IntegerStringConverter()));

    tableView.getColumns().add(newColumn);

private void buttDeleteColumnAction(ActionEvent event)
    if(numberOfColumns > 0)
        tableView.getColumns().remove(numberOfColumns-1);
        --numberOfColumns;

        if(dataList.size() > 0)
            for(MyData x: dataList) //deleting unnecesary variable
                x.deleteLast();
    

private void buttAddRowAction(ActionEvent event)
    dataList.add(new MyData(numberOfColumns)); 

private void buttDeleteRowAction(ActionEvent event)
    if(dataList.size() > 0)//resizing each data object with new variable
        dataList.remove(dataList.size()-1);
    


//*******************************************************************
public class MyData //dont forget about public because you wont get acces to properties
    private ObservableList<SimpleIntegerProperty> cellValue = FXCollections.observableArrayList();

    public MyData(int howManyColumns) 
        for(int i=0; i<howManyColumns; ++i)
           this.cellValue.add(new SimpleIntegerProperty(new Random().nextInt(10)));
    

    public SimpleIntegerProperty getCellValue(int whichOne) 
        return cellValue.get(whichOne);
    

    public void setCellValue(int cellValue, int whichOne) 
        this.cellValue.set(whichOne, new SimpleIntegerProperty(cellValue));
    

    public void addNew(int numberOfNewElement) //ads another variable for another column
        cellValue.add(new SimpleIntegerProperty(new Random().nextInt(10)));
    
    public void deleteLast() //deletes last variable when column is deleted
        cellValue.remove(cellValue.size()-1);
    


//*******************************************************************
@Override
public void start(Stage primaryStage) throws Exception 
    try 
        Scene scene = new Scene(root,400,400);
        primaryStage.setScene(scene);
        primaryStage.show();
     catch(Exception e) 
        e.printStackTrace();
    

public static void main(String[] args) 
    launch(args);



我怎样才能改变它,所以当用户双击单元格时,程序会等到他按下 0-9 的单个数字,然后自动接受它并打开下一个单元格来放置另一个数字(所以他不需要手动打开/关闭它们)?

我目前不知道如何做到这一点。甚至如何使空单元格等待数据(也许数据类应该由char 变量而不是Integer 组成,所以我可以将值"" 放入单元格???)

【问题讨论】:

【参考方案1】:

6 小时左右,我成功了……

我必须在每一列中添加类似的内容,这是处理单元格编辑事件的类。

Callback<TableColumn<MyData, Integer>, TableCell<MyData, Integer>> cellFactoryInt = (TableColumn<MyData, Integer> p) -> new EditingCellNumbers(tableView);
newColumn.setCellFactory(cellFactoryInt);

这里实现这个类:

package application;

import application.MyTable.MyData;
import javafx.beans.value.ObservableValue;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

//Klasa ta pozwala na definiowania zachowania komórek, które edytuje użytkownik
public class EditingCellNumbers extends TableCell<MyData, Integer> 
    private TextField textField;
    private TableView<MyData> parentTableView;
    public static int numberOfColumns;

    public EditingCellNumbers(TableView<MyData> parent) 
        this.parentTableView = parent;
        numberOfColumns = parent.getColumns().size();
    

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

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

        setText(String.valueOf(getItem()));
        setGraphic(null);
    

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

        if (empty) 
            setText(null);
            setGraphic(null);
         else 
            if (isEditing()) 
                if (textField != null) 
                    textField.setText(getString());

                
                setText(null);
                setGraphic(textField);
             else 
                setText(getString());
                setGraphic(null);
            
        
    

    private void createTextField() 
        textField = new TextField(getString());
        textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
        textField.focusedProperty().addListener(
            (ObservableValue<? extends Boolean> arg0, 
            Boolean arg1, Boolean arg2) -> 
                if (!arg2) 
                    commitEdit(Integer.valueOf(textField.getText()));
                
        );
        textField.setOnKeyReleased(new EventHandler<Event>() 
            @Override
            public void handle(Event event) 
                try
                    int i = Integer.valueOf(textField.getText());
                    //digit given...
                    if( (i>=0) && (i<10) )//making sure cell is filled with just one digit
                       commitEdit(Integer.valueOf(textField.getText()));
                       int selectedColumn = parentTableView.getSelectionModel().getSelectedCells().get(0).getColumn(); // gets the number of selected column
                       int selectedRow = parentTableView.getSelectionModel().getSelectedCells().get(0).getRow();
                       if(selectedColumn < numberOfColumns-1)
                           parentTableView.getSelectionModel().selectNext();
                           parentTableView.edit(selectedRow, parentTableView.getColumns().get(selectedColumn+1));
                       else
                           parentTableView.getSelectionModel().select(selectedRow+1, parentTableView.getColumns().get(0));
                           parentTableView.edit(selectedRow+1, parentTableView.getColumns().get(0));

                       

                    else
                       textField.clear();
                catch(NumberFormatException e)
                    textField.clear();
                
            
        );
    

    private String getString() 
        return getItem() == null ? "" : getItem().toString();
    

这里是一点点编辑的应用程序类:

public class MyTable extends Application 
private BorderPane root;
private HBox hBox;
private TableView<MyData> tableView;    
private Button buttAddColumn, buttDeleteColumn;
private Button buttAddRow, buttDeleteRow;
private int numberOfColumns = 0;
private ObservableList<MyData> dataList = FXCollections.observableArrayList();

    @Override
    public void init() throws Exception 
        root = new BorderPane();
        buttAddColumn = new Button("Add columns");
        buttDeleteColumn = new Button("Delete columns");
        buttAddRow = new Button("Add rows");
        buttDeleteRow = new Button("Delete rows");
        tableView = new TableView<>();
        hBox = new HBox();
        hBox.getChildren().addAll(buttAddColumn,buttDeleteColumn,buttAddRow,buttDeleteRow);

        tableView.setEditable(true);
        tableView.getSelectionModel().setCellSelectionEnabled(true);
        tableView.setItems(dataList);

        root.setTop(hBox);
        root.setCenter(tableView);

        //----------------------------------------------------------------
        buttAddColumn.setOnAction(new EventHandler<ActionEvent>() 
            @Override
            public void handle(ActionEvent event) 
                buttAddColumnAction(event);
            
        );
        buttDeleteColumn.setOnAction(new EventHandler<ActionEvent>() 
            @Override
            public void handle(ActionEvent event) 
                buttDeleteColumnAction(event);
            
        );
        buttAddRow.setOnAction(new EventHandler<ActionEvent>() 
            @Override
            public void handle(ActionEvent event) 
                buttAddRowAction(event);
            
        );
        buttDeleteRow.setOnAction(new EventHandler<ActionEvent>() 
            @Override
            public void handle(ActionEvent event) 
                buttDeleteRowAction(event);
            
        );

    

    private void buttAddColumnAction(ActionEvent event)        
        int i = numberOfColumns;// thats the key for lambda expression. Unicate number for column to access its variable;

        if(dataList.size() > 0)//resizing each data object with new variable
            for(MyData x: dataList)
                x.addNew(i);

        TableColumn<MyData, Integer> newColumn = new TableColumn<>("#" + String.valueOf(++numberOfColumns));

        newColumn.setCellValueFactory(cellData -> cellData.getValue().getCellValue(i).asObject());
        newColumn.setCellFactory(TextFieldTableCell.<MyData, Integer>forTableColumn(new IntegerStringConverter()));

        Callback<TableColumn<MyData, Integer>, TableCell<MyData, Integer>> cellFactoryInt = (TableColumn<MyData, Integer> p) -> new EditingCellNumbers(tableView);
        newColumn.setCellFactory(cellFactoryInt);
        tableView.getColumns().add(newColumn);
    
    private void buttDeleteColumnAction(ActionEvent event)
        if(numberOfColumns > 0)
            tableView.getColumns().remove(numberOfColumns-1);
            --numberOfColumns;
            --EditingCellNumbers.numberOfColumns;
            if(dataList.size() > 0)
                for(MyData x: dataList) //deleting unnecesary variable
                    x.deleteLast();
        
    
    private void buttAddRowAction(ActionEvent event)
        dataList.add(new MyData(numberOfColumns)); 
    
    private void buttDeleteRowAction(ActionEvent event)
        if(dataList.size() > 0)//resizing each data object with new variable
            dataList.remove(dataList.size()-1);
        
    
    //*******************************************************************
    public class MyData //dont forget about public because you wont get acces to properties
        private ObservableList<SimpleIntegerProperty> cellValue = FXCollections.observableArrayList();

        public MyData(int howManyColumns) 
            for(int i=0; i<howManyColumns; ++i)
               this.cellValue.add(new SimpleIntegerProperty(new Random().nextInt(10)));
        

        public SimpleIntegerProperty getCellValue(int whichOne) 
            return cellValue.get(whichOne);
        

        public void setCellValue(int cellValue, int whichOne) 
            this.cellValue.set(whichOne, new SimpleIntegerProperty(cellValue));
        

        public void addNew(int numberOfNewElement) //ads another variable for another column
            cellValue.add(new SimpleIntegerProperty(new Random().nextInt(10)));
        
        public void deleteLast() //deletes last variable when column is deleted
            cellValue.remove(cellValue.size()-1);
        
    

    //*******************************************************************
    @Override
    public void start(Stage primaryStage) throws Exception 
        try 
            Scene scene = new Scene(root,400,400);
            primaryStage.setScene(scene);
            primaryStage.show();
         catch(Exception e) 
            e.printStackTrace();
        
    
    public static void main(String[] args) 
        launch(args);
    


很多代码,但也许将来有人会使用它:P

【讨论】:

以上是关于TableView,自定义单元格编辑行为的主要内容,如果未能解决你的问题,请参考以下文章

在单元格内编辑textview时,将另一个单元格添加到tableview

添加另一个部分时将编辑的文本保留在自定义单元格中([tableView 刷新数据])

使用 UITextField 在 tableView 中显示 100 个自定义单元格

带有自定义 UITextView 单元格的 TableView 将导航栏滚动到屏幕外

如何使用自定义按钮重新排序表格视图单元格,但不使用 ios 中的移动编辑图标

didEndEditingRow未使用自定义tableview单元调用