シナリオは、FX コンポーネントが埋め込まれた Swing UI です。Swing と FX 部分の両方を同じモデルで駆動する必要があります。モデルがバインドされたプロパティを持つ Bean であると仮定すると、次のように、Bean プロパティと fx プロパティを適応させるための fx サポートを使用して、両方に fx バインディングを使用できます。
// adapts a bean property to a fx property
protected Property createBeanAdapter(Object bean, String propertyName) {
try {
return JavaBeanObjectPropertyBuilder.create()
.bean(bean)
.name(propertyName)
.build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
モデル プロパティを swing と fx コンポーネントの両方にバインドするための使用法
// the model
person = new PersonBean("Philopator");
personProperty = createBeanAdapter(person, "lastName");
// bind to swing label's text
labelProperty = createBeanAdapter(label, "text");
labelProperty.bindBidirectional(personProperty);
// bind to fx textfield
fxField.textProperty().bindBidirectional(personProperty);
それはクールです...両方の部分でシングルスレッドルールに違反していることを除いて(完全な例は最後にあります)。
問題は、これらの違反をどのように解決するかです。うまくいけば、私は単に明らかなことを見落としているだけです:-)
これが遊ぶためのSSCCEです
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.embed.swing.JFXPanel;
import javafx.scene.SceneBuilder;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBoxBuilder;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import fx.property.PersonBean;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class MixWithBinding {
// model
private PersonBean person;
// bindings
private Property personProperty;
private Property labelProperty;
// swing components
private JComponent content;
private JButton resetButton;
private JLabel label;
// fx components
private TextField fxField;
public MixWithBinding() {
// init the view
JComponent swingPanel = createSwingPanel();
JComponent fxPanel = createFXPanel();
content = new JPanel(new GridLayout(0, 2));
content.add(fxPanel);
content.add(swingPanel);
// init/bind the model
person = new PersonBean("Philopator");
// adapt to fx property
personProperty = createBeanAdapter(person, "lastName");
// swing binding
labelProperty = createBeanAdapter(label, "text");
labelProperty.bindBidirectional(personProperty);
Action reset = new AbstractAction("Reset Text") {
@Override
public void actionPerformed(ActionEvent e) {
person.setLastName("Philator");
}
};
resetButton.setAction(reset);
// fx binding
//final Property threadWrapper = new PropertyWrapper(personProperty);
Platform.runLater(new Runnable() {
@Override
public void run() {
//fxField.textProperty().bindBidirectional(threadWrapper);
fxField.textProperty().bindBidirectional(personProperty);
}
});
debugThreadViolations();
}
protected Property createBeanAdapter(Object bean, String propertyName) {
try {
return JavaBeanObjectPropertyBuilder.create()
.bean(bean)
.name(propertyName)
.build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private JComponent createSwingPanel() {
label = new JLabel();
resetButton = new JButton();
JComponent panel = Box.createVerticalBox();
panel.setBorder(BorderFactory.createTitledBorder("Swing"));
panel.add(label);
panel.add(resetButton);
return panel;
}
private JComponent createFXPanel() {
final JFXPanel fxPanel = new JFXPanel();
Platform.runLater(new Runnable() {
@Override
public void run() {
fxField = new TextField();
fxPanel.setScene(SceneBuilder.create()
.root(VBoxBuilder.create()
.children(fxField)
.build())
.build());
}
});
JComponent panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder("FX"));
panel.add(fxPanel);
return panel;
}
protected void debugThreadViolations() {
PropertyChangeListener swingChange = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!SwingUtilities.isEventDispatchThread())
LOG.info("Violation of EDT rule");
}
};
label.addPropertyChangeListener("text", swingChange);
final ChangeListener fxChange = new ChangeListener() {
@Override
public void changed(ObservableValue arg0, Object arg1, Object arg2) {
if (!Platform.isFxApplicationThread())
LOG.info("Violation of FX-AT rule");
}
};
Platform.runLater(new Runnable() {
@Override
public void run() {
fxField.textProperty().addListener(fxChange);
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MixWithBinding().content);
frame.setLocationByPlatform(true);
frame.setSize(400, 200);
frame.setVisible(true);
}
});
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(MixWithBinding.class
.getName());
}
ダミービーン
public class PersonBean {
String lastName;
/**
* @param lastName
*/
public PersonBean(String lastName) {
super();
this.lastName = lastName;
}
/**
* @return the lastName
*/
public String getLastName() {
return lastName;
}
/**
* @param lastName the lastName to set
*/
public void setLastName(String lastName) {
Object oldValue = getLastName();
this.lastName = lastName;
firePropertyChange("lastName", oldValue, getLastName());
}
PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
protected void firePropertyChange(String name, Object oldValue,
Object newValue) {
support.firePropertyChange(name, oldValue, newValue);
}
}
編集
アイデアを試しました-完全には機能していないため、別の質問です;-)
編集 2
トンネルの終わりにいくらかの光があるかもしれません (そしておそらく mKorbel が彼のコメントで意味したこと): jdk8で EDT/FX-AT のシームレスな相互運用性がサポートされるかもしれません。一方、新しい SwingNode に関する最近のチュートリアル(2013 年 9 月) では、残念なことに両方を切り替えています。