In Javafx how to access object of a component which is under an overridden method? Problem related with TableView

82 Views Asked by At

[This is my following code. Here i wants to access the textField from another class. textField is under overridden method named call().]

public class URLCellFactory {

    private static final URLCellFactory urlCellFactory = new URLCellFactory();
//  private final TextField textField = new TextField();

    public Callback<TableColumn<SQLManager, String>, TableCell<SQLManager, String>> cell() {
        Callback<TableColumn<SQLManager, String>, TableCell<SQLManager, String>> cellFactory =
                new Callback<TableColumn<SQLManager, String>, TableCell<SQLManager, String>>() {
                    @Override
                    public TableCell<SQLManager, String> call(TableColumn<SQLManager, String> elementsStringTableColumn) {
                        final TableCell<SQLManager, String> cell = new TableCell<SQLManager, String>() {
                            private final TextField textField = new TextField();
                            {
                                urlCellFactory.operateURL(textField);
                            }

                            @Override
                            public void updateItem(String item, boolean empty) {
                                super.updateItem(item, empty);
                                if (empty) {
                                    setGraphic(null);
                                } else {
                                    setGraphic(textField);
                                }
                            }
                        };

                        return cell;
                    }
                };
        return cellFactory;
    }

    public void operateURL(TextField textField) {
        textField.setPromptText("Enter value");
        textField.setOnAction((ActionEvent event) -> {
            System.out.println("Something spoken shit");
            textField.setEditable(false);
            //getURL(textField.getText());
        });
    }

// this function should get access from the textField component. Like: textField.getText();
//    public static String getURL() {
//        return ;
//    }
}                       
                    
  

[I tried making the textField global, static and also non-static but it corrupt my output.]

public class URLCellFactory {

    private static final URLCellFactory urlCellFactory = new URLCellFactory();
    private TextField textField = new TextField();

    public Callback<TableColumn<SQLManager, String>, TableCell<SQLManager, String>> cell() {
        Callback<TableColumn<SQLManager, String>, TableCell<SQLManager, String>> cellFactory =
                new Callback<TableColumn<SQLManager, String>, TableCell<SQLManager, String>>() {
                    @Override
                    public TableCell<SQLManager, String> call(TableColumn<SQLManager, String> elementsStringTableColumn) {
                        final TableCell<SQLManager, String> cell = new TableCell<SQLManager, String>() {
                            //private final TextField textField = new TextField();
                            {
                                urlCellFactory.operateURL(textField);
                            }
                            @Override
                            public void updateItem(String item, boolean empty) {
                                super.updateItem(item, empty);
                                if (empty) {
                                    setGraphic(null);
                                } else {
                                    setGraphic(textField);
                                }
                            }
                        };

                        return cell;
                    }
                };
        return cellFactory;
    }

    public void operateURL(TextField textField) {
        textField.setPromptText("Enter value");
        textField.setOnAction((ActionEvent event) -> {
            System.out.println("Something spoken shit");
            textField.setEditable(false);
            //getURL(textField.getText());
        });
    }

    public String getURL() {
        return this.textField.getText();
    }
}                    
                      
  

this corrupt my output

[The goal is to access the textField component without corrupting output. As i mentioned earlier i tried every possible ways to make it work but i failed. So, is there any right way to do that properly please provide it would be very helpful. Please also add comment about how your code works if anyone provide any.
Thanks in advance.]

1

There are 1 best solutions below

1
Slaw On BEST ANSWER

Trying to get the text directly from the TextField of the cell is the wrong approach. The TableView API does not provide a way to get the cell at a particular index. Also, there can be more items in the table than there are cells being displayed. Using the text fields to store application state will lead to information loss.

A TableView is backed by a "model". The model is a custom class that exposes properties. The class itself represents a single item in the table, displayed in a single row. The properties represent the values to display in each column. Editing the value of a cell should result in the new value being written back to the property of a model instance. You can get the new values via the items when needed.

Cell implementations that allow editing take one of two forms.

  1. The cell can enter and exit the editing state. When editing, the UI changes to a control that allows user input. The implementation works via the startEdit, cancelEdit, and commitEdit API of the cell and outside code reacts to the appropriate events.

  2. The cell is "always editing". This bypasses the normal editing API and typically makes use of bidirectional bindings.

Your current implementation seems to be an attempt at the second form. But given you also appear to make the text field non-editable in the on-action handler, it seems the first form would be a better fit. And in that case, you can use the built-in TextFieldTableCell class instead of writing your own.

Here is an example with a TableView that displays Person instances which have a first name property and a last name property.

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToolBar;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Main extends Application {

  /*
   * The TableView is set to editable and its two columns have cell factories
   * that return TextFieldTableCell. Cell selection is enabled.
   * 
   * To start editing the property of a person, select the appropriate cell and
   * hit ENTER or click on an already-selected cell. To commit an edit, press
   * ENTER while the TextField has focused. To cancel, press ESC or deselect the
   * cell.
   * 
   * Press the "Add person" button to add a new row to the (initially empty) 
   * table. You can then edit the first and last name of this person.
   * 
   * Press the "Print people" button to print the people in the table's items 
   * list. This will show you that the properties of the Person objects change
   * when you commit edits via the UI.
   */

  private TableView<Person> table;
  private int printCount;

  @Override
  public void start(Stage primaryStage) {
    table = createPersonTable();

    var addPersonBtn = new Button("Add person");
    addPersonBtn.setOnAction(this::handleAddPerson);

    var printPeopleBtn = new Button("Print people");
    printPeopleBtn.setOnAction(this::handlePrintPeople);

    var root = new BorderPane(table);
    root.setTop(new ToolBar(addPersonBtn, printPeopleBtn));

    primaryStage.setScene(new Scene(root, 600, 400));
    primaryStage.show();
  }

  private TableView<Person> createPersonTable() {
    var table = new TableView<Person>();
    table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN);
    table.getSelectionModel().setCellSelectionEnabled(true);
    table.setEditable(true);

    /*
     * No need to set the onEditCommit handler of the columns because the default handler
     * will set the property for us when the observable returned by the cell-value factory
     * is writable. We are returning StringProperty, which is writable, in both factories below.
     */

    var firstNameCol = new TableColumn<Person, String>("First Name");
    firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
    firstNameCol.setCellValueFactory(cdf -> cdf.getValue().firstNameProperty());
    table.getColumns().add(firstNameCol);

    var lastNameCol = new TableColumn<Person, String>("Last Name");
    lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
    lastNameCol.setCellValueFactory(cdf -> cdf.getValue().lastNameProperty());
    table.getColumns().add(lastNameCol);

    return table;
  }

  private void handleAddPerson(ActionEvent e) {
    e.consume();

    table.getItems().add(new Person("FIRSTNAME", "LASTNAME"));

    int row = table.getItems().size() - 1;
    var col = table.getColumns().get(0);
    table.getSelectionModel().select(row, col);
    table.requestFocus();
  }

  private void handlePrintPeople(ActionEvent e) {
    e.consume();

    /*
     * Demonstrates getting the items of the table and querying the properties
     * of those items. Edits committed via the table will be reflected in these items.
     */

    System.out.printf("########## PRINT %02d ##########%n", printCount);
    for (int i = 0; i < table.getItems().size(); i++) {
      var person = table.getItems().get(i);
      System.out.printf(
          "Person %d: First name = '%s', Last name = '%s'%n",
          i, person.getFirstName(), person.getLastName());
    }
    System.out.println();

    printCount++;
  }

  // The model class
  public static class Person {

    private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
    public final void setFirstName(String firstName) { this.firstName.set(firstName); }
    public final String getFirstName() { return firstName.get(); }
    public final StringProperty firstNameProperty() { return firstName; }

    private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
    public final void setLastName(String lastName) { this.lastName.set(lastName); }
    public final String getLastName() { return lastName.get(); }
    public final StringProperty lastNameProperty() { return lastName; }

    public Person() {}

    public Person(String firstName, String lastName) {
      setFirstName(firstName);
      setLastName(lastName);
    }
  }
}