mynameisJEFF
mynameisJEFF

Reputation: 4239

Javafx: fail to display content properly in table cell

I have the following Trade object class.

public class Trade implements Comparable<Trade>{

      // attributes of each trade that go into the tableViewTransaction log

      // properties
      private StringProperty itemID;

      public Trade(int itemID){
          this.itemID = new SimpleStringProperty(String.format("%04d",itemID));
      }

      public String getItemID(){
          return this.itemID.get();
      }


      public StringProperty itemIDProperty(){
          return this.itemID;
      }

      public void setItemID(String itemID){
            int id = Integer.parseInt(itemID);
            this.itemID.set(String.format("%04d",id));
      }
}

Now in my Controller class, I have a tableView TransactionLog and a table column for itemID.

public TableView<Trade> fxTransactionLog;
public TableColumn<Trade, String> fxTransactionLogItemID;

The tableView is editable, so is the table column using the following code.

Here is where the problem is: The tableView is able to display itemID perfectly. For example say when I create a new Trade object with itemID = 1, the table cell will display 0001, then I decide to edit the itemID of a Trade object and type in to a new ID of 13, it will show up as 0013 like below.

0001 -> 0013 // This works fine, if I edit the cell and assign a DIFFERENT value to the cell.

However, if I click to edit the itemID and assign the same value it already has, which is 1 in this case. It displays 1, which is not what I want, as it is missing the leading zeros. I looked through my code and just couldn't figure out why this is happening. This is more of an aesthetic issue.

0001 -> 1 // edit and reassign same value to the cell

How can I make it display 0001 instead of just 1 even if I assign the SAME value to the cell ?

Secondly, what code and where should I write to prevent the user from typing in String for itemID ?

UPDATE: So I followed thislink Example 12-11 . I created a separate class for EditingItemIDCell.

import javafx.scene.control.TableCell; import javafx.scene.control.TextField; public class EditingItemIDCell extends TableCell{ private TextField textField;

public EditingItemIDCell() {
}

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

    if (empty) {
        setText(null);
        setGraphic(null);
    } else {
        if (isEditing()) {
            if (textField != null) {
                textField.setText(String.format("%04d",Integer.parseInt(getString())));
            }
            setText(null);
            setGraphic(textField);
        } else {
            setText(getString());
            setGraphic(null);
        }
    }
}

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

}

And in my Controller class, I made the following changes.

But I am getting an error saying:

The method setCellFactory(Callback<TableColumn<Trade,String>,TableCell<Trade,String>>) in the type TableColumn<Trade,String> is not applicable for the arguments (Callback<TableColumn,TableCell>).

Upvotes: 0

Views: 664

Answers (1)

Uluk Biy
Uluk Biy

Reputation: 49185

Define cellfactory as;

Callback<TableColumn<Trade, String>, TableCell<Trade, String>> cellFactory
    = new Callback<TableColumn<Trade, String>, TableCell<Trade, String>>()
    {
        public TableCell call( TableColumn<Trade, String> p )
        {
            return new EditingItemIDCell();
        }
    };

In EditingItemIDCell when you create textfield do

private void createTextField()
{
    NumberFormat nf = NumberFormat.getIntegerInstance();
    textField = new TextField();

    // add filter to allow for typing only integer
    textField.setTextFormatter( new TextFormatter<>( c ->
    {

        if (c.getControlNewText().isEmpty()) {
            return c;
        } // for the No.2 issue in the comment

        ParsePosition parsePosition = new ParsePosition( 0 );
        Object object = nf.parse( c.getControlNewText(), parsePosition );

        if ( object == null || parsePosition.getIndex() < c.getControlNewText().length() )
        {
            return null;
        }
        else
        {
            return c;
        }
    } ) );

    textField.setText( getString() );

    textField.setMinWidth( this.getWidth() - this.getGraphicTextGap() * 2 );

    // commit on Enter
    textField.setOnAction( new EventHandler<ActionEvent>()
    {
        @Override
        public void handle( ActionEvent event )
        {
            commitEdit( textField.getText() );
        }
    } );

    textField.focusedProperty().addListener( new ChangeListener<Boolean>()
    {
        @Override
        public void changed( ObservableValue<? extends Boolean> arg0,
                Boolean arg1, Boolean arg2 )
        {
            if ( !arg2 )
            {
                commitEdit( textField.getText() );
            }
        }
    } );

}

and finally, your actual problem, since you decided to format the input value in Trade class setters, when the user commits the same value, tableview determines the original value is not changed and do not update the rendered value in a cell, so when user commits "1" again, the underlying value of Trade instance is "0001", but the rendered value remains with "1". The dirty workaround may be to change the value to some arbitrary intermediate one:

public void setItemID( String itemID )
{
    this.itemID.set( "0" );
    int id = Integer.parseInt( itemID );
    this.itemID.set( String.format( "%04d", id ) );
}


Edit

1) I still want the existing textfield 0001 in the tableCell to be highlighted, when I double click on the cell to edit ?

Change the startEdit to this:

@Override
public void startEdit()
{
    if ( !isEmpty() )
    {
        super.startEdit();
        createTextField();
        setText( null );
        setGraphic( textField );
        textField.requestFocus();
        // textField.selectAll(); commenting out this because
        // JavaFX confuses the caret position described in the comment
        // as OP has observed. Seems to be a bug.
    }
}

2) Plus, with your existing code, i can never delete the integer on the first index when editing.

In the textfield filter, check for emptiness with:

if (c.getControlNewText().isEmpty()) {
    return c;
}

I also edited the filter code above.

Upvotes: 1

Related Questions