0

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>).
mynameisJEFF
  • 4,073
  • 9
  • 50
  • 96
  • You need to define custom tablecell in your own setCellFactory. In this cell you will control how to show the text content and will use textfield which you can apply TextFormatter on it to allow only some formatted text. – Uluk Biy Aug 23 '15 at 07:09
  • Can you provide a code example please ? Thanks! – mynameisJEFF Aug 23 '15 at 07:11
  • See [this](https://docs.oracle.com/javafx/2/ui_controls/table-view.htm) section: Example 12-11 Alternative Solution Of Cell Editing. Then [this](http://stackoverflow.com/questions/31039449/java-8-u40-textformatter-javafx-to-restrict-user-input-only-for-decimal-number) – Uluk Biy Aug 23 '15 at 07:17
  • Hi, I have updated the code but I am very unsure whether I did the right thing or not. And I am getting errors. Please help me check what mistakes I have made. Thanks! – mynameisJEFF Aug 23 '15 at 07:47

1 Answers1

1

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.

Uluk Biy
  • 48,655
  • 13
  • 146
  • 153
  • This works! But I lost the ability to edit cell now. How can inherit the standard startEdit(), cancelEdit() from the superclass ? I tried super.startEdit() but it said this is incorrect. – mynameisJEFF Aug 23 '15 at 08:45
  • You misunderstood me. I need to know what is the standard startEdit(), cancelEdit() and other standard instance methods from TableCell , in order to understand how I should edit my code. The code in the link provided does not help me understand what the original TableCell code is like. – mynameisJEFF Aug 23 '15 at 09:18
  • @mynameisJEFF, well you can download the source code of JavaFX, attach it to your fav IDE and read it. But you need not to do that. See the example in above link, the EditingCell extends from TableCell, and overrides startEdit and cancelEdit methods. Have I still misunderstood? – Uluk Biy Aug 23 '15 at 10:29
  • Thanks! I used the same `startEdit()`, `cancelEdit` and `updateItem` instance method. I have no idea how to commit edit by Enter key because this is what the example doesn't teach and I cannot find code on the page that teach me this. Second, I am still getting 1 instead of `0001` after using the `EditingItemIDCell`. This is not solving the problem stated in the question. – mynameisJEFF Aug 23 '15 at 10:39
  • Sorry , I am new to javafx. There are lot of things I am not familiar with. – mynameisJEFF Aug 23 '15 at 11:02
  • Just want to ask, there is a second part in the question. How can I prevent user from entering a String when I am expecting a number in a table cell ? Thanks! – mynameisJEFF Aug 23 '15 at 12:53
  • Please see the createTextField() method in my answer again. There is a comment. – Uluk Biy Aug 23 '15 at 13:11
  • Thanks , I will edit and implement the code when I get home – mynameisJEFF Aug 23 '15 at 13:21
  • Thanks!!! Your code is excellent. However, I am not fully understand how the code within `textField.setTextFormatter` and `ObservableValue extends Boolean> arg0, Boolean arg1, Boolean arg2 ` works . Can you kindly explain what they do ? Cos I really learn more about javafx. Thanks !!!! – mynameisJEFF Aug 23 '15 at 16:28
  • Hi, there are several issues. 1) I still want the existing textfield `0001` in the tableCell to be highlighted, when I double click on the cell to edit ? 2) Plus, with your existing code, i can never delete the integer on the first index when editing. – mynameisJEFF Aug 23 '15 at 16:42
  • Sorry, I realized your code doesn't allow me to navigate around textfield in the first 10 seconds. It does the highlighting, and editing the first digit well. But it does not allow me to navigate cursor around different digits in the first 10 seconds – mynameisJEFF Aug 24 '15 at 15:36
  • @mynameisJEFF, I couldn't observe that. Maybe you are making other heavy tasks (network or db access) on property change? There is no reason to wait for 10 sec in above code. – Uluk Biy Aug 24 '15 at 15:44
  • I mean when I double click the cell to edit, I press the right arrow key, I end up on the first index / first digit and I couldn't move... – mynameisJEFF Aug 24 '15 at 15:45
  • Thank you so much. I have learnt a lot from this exercise. By the way, just out of curiosity, if I were to parse double only and ignore string, is there an equivalent of `NumberFormat.getIntegerInstance()` for double ? – mynameisJEFF Aug 24 '15 at 16:11
  • @mynameisJEFF yes DecimalFormat df = new DecimalFormat(); see javadoc for more details like setting format pattern, decimal places, rounding, grouping, locale etc. – Uluk Biy Aug 24 '15 at 16:14
  • Thanks ! In this case how can i make `EditingCell` class needs return `double` instead of `String` ? – mynameisJEFF Aug 24 '15 at 17:30