4

我正在使用 JavaFx 2.1 中的简单绑定在 MacOSX 上使用 java 1.7.0_04-b21 开发一个小型应用程序。实际上,我目前将 Mac OSX 上 Cocoa 的绑定机制与 JavaFx 进行比较,并面临几个问题:

该应用程序使用一个模型来保存一个 observableArrayList(称为 messageList),它被设置为 TableView 的项目。向列表中添加新条目有效。该条目出现在 TableView 中。

问题 1:删除所选项目不起作用。当我从可观察列表中删除一个项目时,它不会消失。

问题 2:我想用存储在 TableView 中选择的对象的一个​​字段中的值填充 textField。实际上,在 Cocoa 中,即使在绑定定义的时间点 this 为空(因此没有选择任何内容),也可以定义对选择的绑定。这实际上是一个非常有用的概念,我没有发现这在 JavaFX 中是如何实现的。

问题 3:只有当已经选择了一个对象时,才能建立绑定,以便我最终编写了一个 EventHandler 对不断变化的选择做出反应,并始终重新建立我的文本字段与模型字段的正确绑定。但是对于这种方法,我的应用程序出于某种我现在不明白的原因破坏了模型。

只需单击添加按钮三四次,然后选择条目并查看 textField 的更新。模型中的数据被破坏和覆盖 - 条目通常会变短......欢迎解释效果。

到目前为止,我在 TableView 上找不到绑定示例,因此欢迎任何输入。

我的演示项目的代码包含在以下文件中。所有这些都在同一个包 com.es.javaFxTest 中。

MainWindowController.java:

/*
 * Created on 19.05.2012
 */
package com.es.javaFxTest;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ResourceBundle;

import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;

public class MainWindowController implements Initializable
{

  public Model                         model;

  public TableView<Message>            messageListTableView;

  public TableColumn<Message, String>  messageTableMessageNameColumn;

  public Button                        addMessageButton;

  public Button                        deleteMessageButton;

  public TextField                     messageNameTextField;

  public SimpleObjectProperty<Message> selectedMessage;

  /* (non-Javadoc)
   * @see javafx.fxml.Initializable#initialize(java.net.URL, java.util.ResourceBundle)
   */
  @Override
  public void initialize(URL arg0, ResourceBundle arg1)
  {
    model = new Model();

    messageTableMessageNameColumn.setCellValueFactory(new PropertyValueFactory<Message, String>("messageName"));
    messageListTableView.setItems(model.getMessageList());

    addMessageButton.setOnAction(new EventHandler<ActionEvent>() {
      StringBuffer str = new StringBuffer("a"); // to fill some dummy data

      @Override
      public void handle(ActionEvent e)
      {
        // adding an object to the observed list works and the result is shown in the tableView
        model.getMessageList().add(new Message(str.toString()));
        str.append("x");
      }
    });
    deleteMessageButton.setOnAction(new EventHandler<ActionEvent>() {
      @Override
      public void handle(ActionEvent event)
      { // observation does not work here; also the following code does not work. 
        ObservableList<Integer> selectedIndices = messageListTableView.getSelectionModel().getSelectedIndices();
        ArrayList<Integer> selectedIndices2 = new ArrayList<Integer>(selectedIndices);
        Collections.sort(selectedIndices2);
        for (int i = selectedIndices2.size() - 1; i >= 0; i--)
        {
          model.getMessageList().remove(selectedIndices2.get(i));
        }
      }
    });

    selectedMessage = new SimpleObjectProperty<Message>();
    selectedMessage.bind(messageListTableView.getSelectionModel().selectedItemProperty());

    selectedMessage.addListener(new ChangeListener<Message>() {

      @Override
      public void changed(ObservableValue< ? extends Message> observable, Message oldValue, Message newValue)
      {
        System.out.format("ObservableValue %s, \n  oldValue %s\n  newValue %s\n\n", observable, oldValue, newValue);
        if (oldValue != null)
        {
          messageNameTextField.textProperty().unbind();
          oldValue.messageName.unbind();
        }
        if (newValue != null)
        {
          messageNameTextField.textProperty().set(newValue.getMessageName());
          newValue.messageName.bindBidirectional(messageNameTextField.textProperty());
        }
      }
    });
  }
}

MainWindowLayout.java

package com.es.javaFxTest;

/*
 * Created on 18.05.2012
 */
import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class MainWindowLayout extends Application
{

  @Override
  public void start(Stage stage)
  {

    try
    {
      FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MainWindowLayout.fxml"));
      Pane root = (Pane) fxmlLoader.load();
      MainWindowController controller = (MainWindowController) fxmlLoader.getController();

      Scene scene = new Scene(root);
      stage.setTitle("Config");
      stage.setScene(scene);
      stage.show();

    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
  }

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

}

消息.java

/*
 * Created on 19.05.2012
 *
 */
package com.es.javaFxTest;

import javafx.beans.property.SimpleStringProperty;

public class Message 
{



  /**
   * @param canId
   * @param messageName
   */
  public Message(String messageName)
  {
    this.messageName = new SimpleStringProperty(messageName);
  }


  SimpleStringProperty messageName;
  /**
   * @return the messageName
   */
  public String getMessageName()
  {
    return messageName.getValue();
  }
  /**
   * @param messageName the messageName to set
   */
  public void setMessageName(String messageName)
  {
    this.messageName.set(messageName);
  }

  public String toString ()
  {
    return String.format("Name:%s",messageName); 
  }

}

模型.java

/*
 * Created on 20.05.2012
 */
package com.es.javaFxTest;

import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Model
{
  ObservableList<Message>     messageList;

  SimpleListProperty<Message> messageListProperty;

  public Model()
  {
    messageList = FXCollections.observableArrayList();
    messageListProperty = new SimpleListProperty<Message>(this,"messageList",messageList);
  }

  public final SimpleListProperty<Message> messageListProperty()
  {
    return messageListProperty;
  }

  public ObservableList<Message> getMessageList()
  {
    return messageListProperty.get();
  }

  public void setMessageList(ObservableList<Message> l)
  {
    messageListProperty.set(l);
  }
}

MainWindowLayout.xml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.collections.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="500.0" xmlns:fx="http://javafx.com/fxml" fx:controller="com.es.javaFxTest.MainWindowController">
  <children>
    <SplitPane id="splitPaneHorizontal1" dividerPositions="0.3614457831325301" focusTraversable="true" prefHeight="600.0" prefWidth="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
      <items>
        <AnchorPane id="anchorPane1" minHeight="0.0" minWidth="0.0" prefHeight="598.0" prefWidth="390.0">
          <children>
            <VBox id="VBox" alignment="CENTER" prefHeight="598.0" prefWidth="177.0" spacing="5.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
              <children>
                <TableView id="tableView1" fx:id="messageListTableView" editable="true" prefHeight="598.0" prefWidth="406.0">
                  <columns>
                    <TableColumn prefWidth="75.0" text="Name" fx:id="messageTableMessageNameColumn" />
                  </columns>
                </TableView>
                <HBox id="HBox" alignment="CENTER" spacing="5.0">
                  <children>
                    <Button id="button2" fx:id="addMessageButton" text="Add">
                      <HBox.margin>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                      </HBox.margin>
                    </Button>
                    <Button id="button1" fx:id="deleteMessageButton" text="Delete">
                      <HBox.margin>
                        <Insets bottom="5.0" right="5.0" top="5.0" />
                      </HBox.margin>
                    </Button>
                  </children>
                </HBox>
              </children>
            </VBox>
          </children>
        </AnchorPane>
        <AnchorPane id="anchorPane2" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
          <children>
            <HBox id="HBox" alignment="CENTER" layoutX="44.0" layoutY="177.0" spacing="5.0">
              <children>
                <Label id="label2" text="Name:" />
                <TextField id="textField2" fx:id="messageNameTextField" prefWidth="200.0" text="TextField" />
              </children>
            </HBox>
          </children>
        </AnchorPane>
      </items>
    </SplitPane>
  </children>
</AnchorPane>
4

1 回答 1

5

第 1 期——您踏入了著名的 Java 自动装箱陷阱之一。

model.getMessageList().remove(selectedIndices2.get(i)); // get(i) returns Integer

呼叫List.remove(Object o)而不是List.remove(int idx)您预期的呼叫。

采用

model.getMessageList().remove(selectedIndices2.get(i).intValue());

反而。

问题2-3

只需将侦听器附加到selectedItem更改并重新绑定:

    messageListTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Message>() {

        @Override
        public void changed(ObservableValue<? extends Message> ov, Message old, Message newM) {
            messageNameTextField.textProperty().unbindBidirectional(old.messageName);
            messageNameTextField.textProperty().bindBidirectional(newM.messageName);
        }
    });

并更新您的 Message 类。要正确使用 TableView,它需要完整的 FX“bean”并通过方法公开属性:

public StringProperty messageNameProperty() {
    return messageName;
}
于 2012-06-13T14:47:34.420 回答