如何正确避免特定的表视图单元格可编辑?

时间:2014-05-19 14:32:48

标签: java properties javafx tableview editingstate

以下问题:我需要知道,在特定时刻,我的TableView目前正在编辑天气。我使用editingCellProperty并将特定的businesslogic绑定到它,以便在表格切换这些模式时获得通知。

不幸的是状态并不总是正确显示,因为我有特定的单元格(在同一列中)根本不应该编辑,但是当点击不可编辑的单元格时,TablePosition被设置到editingCellProperty中,因此我得到了我的TableView正在编辑的信息,尽管它已被告知,这个单元格是不可编辑的,编辑模式永远不会真正开始。

我创建了一个小例子,其中所有偶数行的ColumnName都是可编辑的,而奇数行则不是。

Small Example

实施例-代码:

public class TableViewEditableStateError extends Application
{
  public static void main( final String[] args )
  {
    launch( args );
  }

  @Override
  public void start( final Stage primaryStage )
  {
    Item item1 = new Item( 1, "Item1" );
    Item item2 = new Item( 2, "Item2" );
    Item item3 = new Item( 3, "Item3" );
    Item item4 = new Item( 4, "Item4" );

    ObservableList<Item> itemList = FXCollections.observableArrayList( item1, item2, item3, item4 );

    TableColumn<Item, Integer> columnId = new TableColumn<>( "ColumnId" );
    TableColumn<Item, String> columnName = new TableColumn<>( "ColumnName" );

    columnId.setCellValueFactory( new PropertyValueFactory<Item, Integer>( "id" ) );
    columnName.setCellValueFactory( new PropertyValueFactory<Item, String>( "name" ) );

    //    columnName.setCellFactory( TextFieldTableCell.forTableColumn() ); -> Using own implementation for editablestate-behaviour

    columnName.setCellFactory( new Callback<TableColumn<Item, String>, TableCell<Item, String>>()
    {
      @Override
      public TableCell<Item, String> call( final TableColumn<Item, String> column )
      {
        TableCell<Item, String> cell = new EditableTableCell<Item>()
        {
          @Override
          public void startEdit()//Business-Rule to deceide, which cell is editable.
          {
            if ( getTableRow().getIndex() % 2 != 0/* Only Odd Values are editable. -> Any Random Condition */)
            {
              System.out.println( "Do not initiate Editing." );
              return;
            }

            System.out.println( "initiate Editing." );
            super.startEdit();
          }
        };

        return cell;
      }
    } );

    columnName.setOnEditCommit( new EventHandler<CellEditEvent<Item, String>>()
    {
      @Override
      public void handle( final CellEditEvent<Item, String> event )
      {
        final Item item = event.getRowValue();

        System.out.println( "Change Item " + item + " from " + event.getOldValue() + " to new value "
            + event.getNewValue() );

        item.setName( event.getNewValue() );
      }
    } );

    final TableView<Item> tableView = new TableView<>( itemList );
    tableView.getColumns().add( columnId );
    tableView.getColumns().add( columnName );

    tableView.setColumnResizePolicy( TableView.CONSTRAINED_RESIZE_POLICY );

    tableView.setEditable( true );
    columnId.setEditable( false );

    Label label = new Label( "Editing-State: is not editing..." );

    //Listener to record editing behaviour:
    tableView.editingCellProperty().addListener( new ChangeListener<TablePosition<Item, ?>>()
    {
      @Override
      public void changed( final ObservableValue<? extends TablePosition<Item, ?>> observable,
                           final TablePosition<Item, ?> oldValue, final TablePosition<Item, ?> newValue )
      {
        System.out.println( "Editstate changed to: " + newValue );

        if ( newValue != null )
          label.setText( "Editing-State: is editing..." );
        else
          label.setText( "Editing-State: is not editing..." );
      }
    } );

    BorderPane layout = new BorderPane();
    layout.setCenter( tableView );
    layout.setTop( label );
    Scene scene = new Scene( layout, 400, 400 );
    scene.getStylesheets().add( getClass().getResource( "application.css" ).toExternalForm() );
    primaryStage.setScene( scene );
    primaryStage.show();
  }

  class EditableTableCell<S> extends TableCell<S, String>
  {
    protected TextField textField;

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

      if ( textField == null )
      {
        createTextField();
      }

      setGraphic( textField );
      setContentDisplay( ContentDisplay.GRAPHIC_ONLY );
      Platform.runLater( new Runnable()
      {
        @Override
        public void run()
        {
          textField.selectAll();
          textField.requestFocus();
        }
      } );
    }

    @Override
    public void cancelEdit()
    {
      super.cancelEdit();
      setText( getItem() == null ? "" : getItem().toString() );
      setContentDisplay( ContentDisplay.TEXT_ONLY );
      textField = null;
    }

    @Override
    public void updateItem( final String item, final boolean empty )
    {
      super.updateItem( item, empty );
      if ( empty )
      {
        setText( null );
        setGraphic( null );
      }
      else
      {
        if ( isEditing() )
        {
          if ( textField != null )
          {
            textField.setText( getItem() == null ? "" : getItem().toString() );
          }
          setGraphic( textField );
          setContentDisplay( ContentDisplay.GRAPHIC_ONLY );
        }
        else
        {
          setText( getItem() == null ? "" : getItem().toString() );
          setContentDisplay( ContentDisplay.TEXT_ONLY );
        }
      }
    }

    private void createTextField()
    {
      textField = new TextField( getItem() == null ? "" : getItem().toString() );

      textField.setOnKeyPressed( new EventHandler<KeyEvent>()
      {
        @Override
        public void handle( final KeyEvent keyEvent )
        {
          if ( keyEvent.getCode() == KeyCode.ENTER )
          {
            commitEdit( textField.getText() );
          }
          else if ( keyEvent.getCode() == KeyCode.ESCAPE )
          {
            cancelEdit();
          }
        }
      } );

      textField.focusedProperty().addListener( new ChangeListener<Boolean>()
      {
        @Override
        public void changed( final ObservableValue<? extends Boolean> observable, final Boolean oldValue,
                             final Boolean newValue )
        {
          if ( !newValue && textField != null )
          {
            final String text = textField.getText();
            commitEdit( text );
          }
        }
      } );
    }
  }
}

DataClass项目:

public class Item
{
  private final IntegerProperty id   = new SimpleIntegerProperty();
  private final StringProperty  name = new SimpleStringProperty();

  public Item( final int id, final String name )
  {
    this.id.set( id );
    this.name.set( name );
  }

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

  public String getName()
  {
    return name.get();
  }

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

  public void setName( final String name )
  {
    this.name.set( name );
  }

  public IntegerProperty idProperty()
  {
    return id;
  }

  public StringProperty nameProperty()
  {
    return name;
  }
}

我将问题缩小到TableView的editCell方法,因为它没有对特定于细胞的可编辑状态做出反应:

/**
 * Causes the cell at the given row/column view indexes to switch into
 * its editing state, if it is not already in it, and assuming that the 
 * TableView and column are also editable.
 */
public void edit(int row, TableColumn<S,?> column) {
    if (!isEditable() || (column != null && ! column.isEditable())) {
        return;
    }

    if (row < 0 && column == null) {
        setEditingCell(null);
    } else {
        setEditingCell(new TablePosition<>(this, row, column));
    }
}

但是因为setEditingCell()是一个私有方法,所以我无法以我想要的方式覆盖edit-Method。所以我很好奇:有人能告诉我在同一列中实现动态可编辑表格单元的正确方法,而TableView的editingCell-State总是保持正确吗?

1 个答案:

答案 0 :(得分:0)

在侦听器中有这样的内容,只是忽略编辑您不想要的单元格更改

    editingCellProperty().addListener((obs,ov,nv) -> {
        if (nv!=null && nv.getRow() % 2 != 0/* Only Odd Values are editable. -> Any Random Condition */)
        {
          System.out.println( "Pretend this never happened" );
        }
        else
        System.out.println("Editstate changed to: " + nv);
    });

即使I setEditable(false)对于EditableCell构造函数中的某些单元格,侦听器仍然会触发。它不认为它发生在TableCell编辑方法中,因为它检查!isEditable()。正在发生的是editCell更改,从正在编辑的最后一个单元格到未编辑的新单元格。我想这有点意义,但是你没有得到null,但是没有编辑单元格的TablePosition。即使没有编辑单元格,尝试编​​辑不可编辑的单元格仍然会激活侦听器。

我认为这需要一个错误报告。