JavaFx2.0Binding

1.首先阅读文档,了解Javafx2.0中的属性和绑定:Using JavaFX Properties and Binding

2.简单总结:

(1)JavaBean不再是以前的pojo了,Javafx添加了一系列的封装类,进一步封装了Java中的基本类型的封装类,使得它可以被绑定或者绑定,也就是它实现了Observable接口,具体请看API。

(2)上面的以Simple开头的是相应的property的简单实现类,所以在类(Javabean)中一般是使用初始化为simple...

(3)property都有一些方法用于绑定特定的对象,例如,绑定其他的property,或者其他的property组合而成的,例如,StringProperty

(4)除了使用bind方法绑定其他的property之外,它还可以添加listener,比如,当这个属性发生变化的时候就可以通知给其他的对象!

下面显示了StringProperty的实现的接口

3.实践

编写一个JavaBean,符合JavaFx2.0的规范

LightButton.java

package light;

import util.ProtocolUtil;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class LightButton {

    private IntegerProperty id = new SimpleIntegerProperty();// init here

    private StringProperty name = new SimpleStringProperty();

    private IntegerProperty state = new SimpleIntegerProperty(ProtocolUtil.STATE_OFF);

//    public LightButton(){//可以没有
//        
//    }

    public LightButton(int id, String name) {
        setId(id);
        setName(name);
    }

    public void setState(int value) {
        state.set(value);
    }

    public int getState() {
        return state.get();
    }

    public IntegerProperty stateProperty() {
        return state;
    }

    public void setId(int value) {
        id.set(value);
    }

    public int getId() {
        return id.get();
    }

    public IntegerProperty idProperty() {
        return id;
    }

    public void setName(String value) {
        name.set(value);
    }

    public String getName() {
        return name.get();
    }

    public StringProperty nameProperty() {
        return name;
    }

}

LightView.fxml

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

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<?import light.Light?>

<AnchorPane id="AnchorPane" fx:id="lightAnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="150.0" xmlns:fx="http://javafx.com/fxml" fx:controller="light.LightViewController">
  <children>
    <StackPane id="StackPane" fx:id="lightStackPane" onMouseEntered="#handleShowButtons" onMouseExited="#handleHideButtons" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
      <children>
        <ImageView fx:id="lightImageView" onMouseClicked="#handleLight" style="-fx-cursor:hand;" StackPane.alignment="TOP_CENTER">
          <effect>
            <Reflection bottomOpacity="0.2" fraction="0.5" />
          </effect>
          <image>
            <Image url="@light_off.png" preserveRatio="true" smooth="true" />
          </image>
          <StackPane.margin>
            <Insets top="20.0" />
          </StackPane.margin>
        </ImageView>
        <Text id="text1" fx:id="textLightName" boundsType="VISUAL" scaleX="1.3438942202036481" scaleY="1.5467894807101856" stroke="#0c9900" strokeLineCap="ROUND" strokeLineJoin="ROUND" text="LightName" StackPane.alignment="TOP_CENTER">
          <effect>
            <Reflection bottomOpacity="0.2" fraction="0.6" />
          </effect>
          <StackPane.margin>
            <Insets top="120.0" />
          </StackPane.margin>
        </Text>
        <Button fx:id="btnOpen" alignment="CENTER" contentDisplay="CENTER" defaultButton="true" onAction="#handleOpenLight" text="*Open" StackPane.alignment="CENTER_LEFT">
          <StackPane.margin>
            <Insets left="4.0" />
          </StackPane.margin>
        </Button>
        <Button fx:id="btnClose" onAction="#handleCloseLight" text="*Close" StackPane.alignment="CENTER_RIGHT">
          <StackPane.margin>
            <Insets right="4.0" />
          </StackPane.margin>
        </Button>
      </children>
    </StackPane>
  </children>
</AnchorPane>

LightViewController.java

package light;

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

import util.ProtocolUtil;

import javafx.animation.FadeTransition;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.util.Duration;
import app.AppControllers2;

public class LightViewController implements Initializable {

    private LightButton lightButton;

    private Image lightOnImage;
    private Image lightOffImage;

    @FXML
    private AnchorPane lightAnchorPane;
    @FXML
    private StackPane lightStackPane;
    @FXML
    private ImageView lightImageView;
    @FXML
    private Button btnOpen;
    @FXML
    private Button btnClose;
    @FXML
    private Text textLightName;

    private List<Button> buttons = new ArrayList<Button>();

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        lightOnImage = new Image(getClass().getResourceAsStream("light_on.png"));
        lightOffImage = new Image(getClass().getResourceAsStream("light_off.png"));
        lightButton = AppControllers2.getNewLightButton();
        changeImage();
        textLightName.setText(lightButton.getName());
//        lightButton.nameProperty().bind(textLightName.textProperty());//(1)fails!
        // java.lang.RuntimeException: A bound value cannot be set.
//        textLightName.textProperty().bind(lightButton.nameProperty());//(2)ok

        textLightName.textProperty().bindBidirectional(lightButton.nameProperty());// (3)ok
        lightButton.nameProperty().bindBidirectional(textLightName.textProperty());// (4)ok
        // (3)+(4) ok
//        lightButton.nameProperty().bind(textLightName.textProperty());//(1)+(2) fails
        // can not do this!StackOverflowError
        lightButton.stateProperty().addListener(new ChangeListener<Number>() {
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                changeImage();
            }
        });
        buttons.add(btnOpen);
        buttons.add(btnClose);
        handleHideButtons(null);
    }

    private void changeImage() {
        if (lightButton.getState() == ProtocolUtil.STATE_OFF) {
            lightImageView.setImage(lightOffImage);
        } else {
            lightImageView.setImage(lightOnImage);
        }
    }

    @FXML
    private void handleOpenLight(ActionEvent event) {
        System.out.println("handle open");
        lightButton.setName("open");
        lightButton.setState(ProtocolUtil.STATE_ON);
    }

    @FXML
    private void handleCloseLight(ActionEvent event) {
        System.out.println("handle close");
        lightButton.setName("close");
        lightButton.setState(ProtocolUtil.STATE_OFF);
    }

    @FXML
    private void handleLight(MouseEvent event) {
        System.out.println("handle light");
        lightButton.setName("name");
        lightButton.setState(1 - lightButton.getState());
    }

    @FXML
    private void handleShowButtons(MouseEvent me) {
        System.out.println("handle show buttons");
        for (Button button : buttons) {
            FadeTransition f = new FadeTransition(Duration.millis(250), button);
            f.setFromValue(0.2);
            f.setToValue(0.8);
            f.play();
        }
    }

    @FXML
    // TODO:bug here!if the mouse moves very fast,the buttons will not disappear!
    private void handleHideButtons(MouseEvent me) {
        System.out.println("handle hide buttons");
        for (Button button : buttons) {
            button.setOpacity(0);
        }
    }

}

然后用FXMLLoader加载fxml文件,显示在stage中,即可看到效果:

文字会发生变化,图片也会发生变化!其中文字使用的是bind,而图片使用的是addListener

注意:请注意Controller类中的注释代码,这个很重要,绑定是有方向性的,一般是UI控件的值绑定到了bean的某个属性对象上。

并且要注意,不要以为 a.bind(b) 再加上 b.bind(a) 就可以是想双向绑定,这个是错误的,会发生栈溢出错误!例如(1)+(2)

代码片段反映了不同的情况下的情况,注意两个不同的方法:bind 和 bindBidirectional 方法,后者是由StringProperty提供的,可以实现双向绑定(貌似是,没有看过源码)

所以,使用(3)和使用(4)是一样的,写上一个就可以了。

4.但是,实际开发中,我们的bean对象中不仅仅只是int,float,double,string等等这些基本类型吧!那如果是一些比较复杂的Java类怎么进行绑定呢?

仔细查看API,不能发现还有一个ObjectProperty,它对应一个简单的实现类SimpleObjectProperty,看看它怎么使用吧,其实和其他的XXXProperty是一样的

下面是一个例子:目的是让rectangle的color和bill中的color属性一致!

package demo;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

public class BindingDemo {

    public static void main(String[] args) {
        Bill bill1 = new Bill();
        Bill bill2 = new Bill();

        NumberBinding total = Bindings.add(bill1.dpProperty(), bill2.dpProperty());
        total.addListener(new InvalidationListener() {
            public void invalidated(Observable arg0) {
                System.out.println("now invalid!");
            }
        });

        bill1.setDp(12);//valid->invalid
//        System.out.println(total.getValue());// (1)//invalid->valid
        bill2.setDp(13);//valid->invalid
        System.out.println(total.getValue());// (2)//invalid->valid

        // if (1)+(2)
//        now invalid!
//        12.0
//        now invalid!
//        25.0
        
        //if (1) commented
//        now invalid!
//        25.0

        Rectangle rectangle = new Rectangle(20, 20, Color.RED);
        System.out.println(rectangle.getFill());// Color[red=255,green=0,blue=0,opacity=1.0]
        rectangle.fillProperty().bind(bill1.colorProperty());
        bill1.setColor(Color.GREEN);
        System.out.println(rectangle.getFill());// Color[red=0,green=128,blue=0,opacity=1.0]

        // ChangeListener<Object> right and ChangeListener<Color> wrong
//        rectangle.fillProperty().addListener(new ChangeListener<Object>() {
//            public void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {
//                //when the rectangle color changes,this method will be invoked
//            }
//        });
    }

}

class Bill {

    private DoubleProperty dp = new SimpleDoubleProperty();
    private ObjectProperty<Color> color = new SimpleObjectProperty<Color>();

    public final Color getColor() {
        return color.get();
    }

    public final void setColor(Color color) {
        this.color.set(color);
    }

    public ObjectProperty<Color> colorProperty() {
        return color;
    }

    public final double getDp() {
        return dp.get();
    }

    public final void setDp(double dp) {
        this.dp.set(dp);
    }

    public DoubleProperty dpProperty() {
        return dp;
    }

}
原文地址:https://www.cnblogs.com/yinger/p/2459803.html