Javafx双向绑定SimpleDoubleProperty SimpleStringProperty数组超出范围

时间:2015-06-24 19:14:53

标签: java javafx javafx-2 javafx-8

当我运行以下代码时,我得到一个超出范围的数组索引异常。我的目标是绑定Text obj和ProgressBar对象,以便它们同时更新。我试图使用双向绑定。我必须踩到某个地方。我已经运行了很多次并且看到它成功运行了一次或两次。通常在观看进度条几分钟后发生异常。有时我在各自的进度条上看到了一个Text obj或两个退出更新,但进度条继续前进直到异常。 JavaFX绑定专家请帮忙。

Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.elementData(ArrayList.java:418)
at java.util.ArrayList.get(ArrayList.java:431)
at javafx.scene.Parent.updateCachedBounds(Parent.java:1580)
at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1377)
at javafx.scene.Node.updateGeomBounds(Node.java:3556)
at javafx.scene.Node.getGeomBounds(Node.java:3509)
at javafx.scene.Node.getLocalBounds(Node.java:3457)
at javafx.scene.Node.updateTxBounds(Node.java:3620)
at javafx.scene.Node.getTransformedBounds(Node.java:3403)
at javafx.scene.Node.updateBounds(Node.java:538)
at javafx.scene.Parent.updateBounds(Parent.java:1708)
at javafx.scene.Parent.updateBounds(Parent.java:1706)
at javafx.scene.Parent.updateBounds(Parent.java:1706)
at javafx.scene.Parent.updateBounds(Parent.java:1706)
at javafx.scene.Parent.updateBounds(Parent.java:1706)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2404)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Toolkit.java:314)
at com.sun.javafx.tk.Toolkit$$Lambda$163/2039336538.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:313)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:340)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:525)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:505)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$400(QuantumToolkit.java:334)
at com.sun.javafx.tk.quantum.QuantumToolkit$$Lambda$40/1271950976.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$145(WinApplication.java:101)
at com.sun.glass.ui.win.WinApplication$$Lambda$36/1963387170.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)

这是完整的来源。

    package javafxapplication2;

import java.io.BufferedReader;
import java.net.URL;
import java.security.Provider.Service;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.ResourceBundle;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextBoundsType;
import javafx.util.StringConverter;
import javafx.util.converter.DoubleStringConverter;
import javafx.util.converter.NumberStringConverter;
import org.controlsfx.control.HyperlinkLabel;


public class FXMLDocumentController implements Initializable {

    @FXML
    private TreeView treeView;


    List<JobProgress> jobProgressInfos = Arrays.<JobProgress>asList(
            new JobProgress("JJ&P_POP_DAT", "Running Jobs"),
            new JobProgress("Des_master_33334", "Running Jobs"),
            new JobProgress("FS_BUSS", "Running Jobs"),
            new JobProgress("CONER_DWN_MIX", "Running Jobs"),
            new JobProgress("newjb", "Running Jobs"),
            new JobProgress("jb name", "Running Jobs"),
            new JobProgress("tst jb", "Pending Jobs"),
            new JobProgress("ult--ob", "Pending Jobs"),
            new JobProgress("inl jb", "Pending Jobs"),
            new JobProgress("fst jb", "Completed Jobs"),
            new JobProgress("VoCt_Nme_Jb", "Completed Jobs"));
       TreeItem<String> rootNode = new TreeItem<>("Job Details");



    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }    

    void setup() 
    {
        rootNode.setExpanded(true);
        for (JobProgress jobProgressInfo : jobProgressInfos) 
        {   
            Text text = new Text();

            text.setBoundsType(TextBoundsType.VISUAL);
            text.setFill(Color.BLACK);
            ProgressBar bar = new ProgressBar(0.0);

            //Bind bar and text:
            bar.progressProperty().bind(jobProgressInfo.getJobPercentDouble());
            text.textProperty().bind(jobProgressInfo.getJobPercentString());

            StackPane stack = new StackPane();
            stack.getChildren().addAll(bar,text);
            Node icon = stack;

            text.textProperty().addListener(new ChangeListener<String>() 
            {
                public void changed(ObservableValue ov, String t, String t1) {
                        System.out.println(ov);
                        System.out.println(t);
                        System.out.println(t1);
                        System.out.println("");
                }    
            });
           TreeItem<String> empLeaf = new TreeItem<>(jobProgressInfo.getJobName(),icon);

            boolean found = false;
            for (TreeItem<String> depNode : rootNode.getChildren()) 
            {
                if (depNode.getValue().contentEquals(jobProgressInfo.getJobStatus()))
                { 
                    depNode.getChildren().add(empLeaf);
                    found = true;
                    break;
                }
            }
            if (!found) {
                TreeItem<String> depNode = new TreeItem<>(
                    jobProgressInfo.getJobStatus() 
                );
                rootNode.getChildren().add(depNode);
                depNode.getChildren().add(empLeaf);
            }

        }

        treeView.setRoot(rootNode);

        treeView.addEventHandler(MouseEvent.MOUSE_CLICKED,new EventHandler<MouseEvent>() 
        {
            @Override public void handle(MouseEvent mouseEvent) 
            {
                if ( mouseEvent.getButton().equals(MouseButton.PRIMARY))
                {
                    if (treeView.getSelectionModel().getSelectedItem() instanceof TreeItem)
                    {
                        TreeItem item = (TreeItem)treeView.getSelectionModel().getSelectedItem();
                        if (item.getParent() != null && item.getParent().getParent() != null)
                        {
                            System.out.println("You primary clicked a job!");
                        }
                    }
                }
                else if ( mouseEvent.getButton().equals(MouseButton.SECONDARY))
                {
                    if (treeView.getSelectionModel().getSelectedItem() instanceof TreeItem)
                    {
                        TreeItem item = (TreeItem)treeView.getSelectionModel().getSelectedItem();
                        if (item.getParent() != null && item.getParent().getParent() != null)
                        {
                            System.out.println("You scondary clicked a job!");
                        }
                    }
                }
            }
        });

        Service svc = new Service();
        svc.createTask().run();
    }

public class Service extends javafx.concurrent.Service<Void>
{

    @Override
    protected Task<Void> createTask() 
    {
        return new TestTask();
    }

  private class TestTask extends Task<Void>
  {

    public TestTask()
    {

    }

    @Override
    protected Void call() throws Exception
    {
        for (JobProgress jp : jobProgressInfos)
        {
            new Thread(){
                 @Override
                 public void run(){                
                      try {

                           for(double i=0; i<=1; i+=0.01)
                           {
                                DecimalFormat df = new DecimalFormat("#.####"); 
                                jp.setJobPercentDouble(Double.parseDouble( df.format(i))); 
                                Thread.sleep(500);                       
                           }

                      } catch (InterruptedException ex) {
                           System.err.println("Error on Thread Sleep");
                      }
                 }

            }.start();
        }
        return null;
    }

    @Override
    protected void succeeded()
    { 
    }



    @Override
    protected void cancelled()
    {
    }
  }
}


    public static class JobProgress 
    {
        private final SimpleDoubleProperty jobPercentDouble;
        private final SimpleStringProperty jobPercentString;
        private final SimpleStringProperty jobName;
        private final SimpleStringProperty jobStatus;
        private final HyperlinkLabel hyperLink;

        private JobProgress(String aJobName, String aJobStatus) {
            this.jobName = new SimpleStringProperty(aJobName);
            this.jobStatus = new SimpleStringProperty(aJobStatus);
            this.hyperLink = new HyperlinkLabel("test");
            this.jobPercentDouble = new SimpleDoubleProperty();
            this.jobPercentString = new SimpleStringProperty();


            Bindings.bindBidirectional(jobPercentString,jobPercentDouble, new NumberStringConverter());
           // StringConverter<? extends Number> converter =  new NumberStringConverter();

//            Bindings.bindBidirectional(stringProp, doubleProp, new Format() 
//            {
//
//                @Override
//                public StringBuffer format(Object obj, StringBuffer toAppendTo,FieldPosition pos) 
//                {
//                    System.out.println(obj.toString());
//                    return toAppendTo.append(obj.toString());
//                }
//
//                @Override
//                public Object parseObject(String source, ParsePosition pos) 
//                {
//                    System.out.println("parseObj " + source);
//                    return Integer.parseInt(source);
//                }
//
//            });
        }

        public String getJobName() {
            return jobName.get();
        }

        public void setJobName(String value) {
            jobName.set(value);
        }

        public String getJobStatus() {
            return jobStatus.get();
        }

        public void setJobStatus(String value) {
            jobStatus.set(value);
        }

        public SimpleStringProperty getJobPercentString() {
            return jobPercentString;
        }

        public void setJobPercentString(String value) {
            jobPercentString.set(value);
        }

        public SimpleDoubleProperty getJobPercentDouble() {
            return jobPercentDouble;
        }

        public void setJobPercentDouble(double value) {
            jobPercentDouble.set(value);
        }
    }

}

这里是FXML

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

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

<AnchorPane id="AnchorPane" prefHeight="509.0" prefWidth="222.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40" fx:controller="javafxapplication2.FXMLDocumentController">
   <children>
      <TreeView fx:id="treeView" layoutX="11.0" layoutY="5.0" prefHeight="496.0" prefWidth="200.0" AnchorPane.bottomAnchor="8.0" AnchorPane.leftAnchor="11.0" AnchorPane.rightAnchor="11.0" AnchorPane.topAnchor="5.0" />
   </children>
</AnchorPane>

最后但并非最不重要的主要内容:

package javafxapplication2;

import java.net.URL;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class JavaFXApplication2 extends Application {

    @Override
    public void start(Stage stage) throws Exception 
    {
        URL resource = getClass().getResource("FXMLDocument.fxml");

         FXMLLoader loader = new FXMLLoader(resource);
         Parent root = (Parent) loader.load();
         FXMLDocumentController controller = loader.getController();
         controller.setup();

         Scene scene = new Scene(root);

         stage.setScene(scene);
         stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 
    {
        launch(args);
    }

}

1 个答案:

答案 0 :(得分:3)

你把很多部分混合在一起,比如Service,Task,Thread。而且您的命名约定不是JavaFX的命名约定,就像您的属性一样。

在我清理了一些代码并创建了一个自己的JobProgress类文件之后,你现在应该准备好了。

您错过了在JobProgress对象上设置初始进度。那么如果没有数字,bibinding如何将数字转换为字符串?!

此外,我已删除了一些侦听器和操作处理程序。您应该可以再次添加它们。

FXMLDocumentController.java

import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextBoundsType;

public class FXMLDocumentController implements Initializable {

  @FXML
  private TreeView treeView;

  private final List<JobProgress> jobList = Arrays.<JobProgress>asList(
          new JobProgress("JJ&P_POP_DAT", "Running Jobs"),
          new JobProgress("Des_master_33334", "Running Jobs"),
          new JobProgress("FS_BUSS", "Running Jobs"),
          new JobProgress("CONER_DWN_MIX", "Running Jobs"),
          new JobProgress("newjb", "Running Jobs"),
          new JobProgress("jb name", "Running Jobs"),
          new JobProgress("tst jb", "Pending Jobs"),
          new JobProgress("ult--ob", "Pending Jobs"),
          new JobProgress("inl jb", "Pending Jobs"),
          new JobProgress("fst jb", "Completed Jobs"),
          new JobProgress("VoCt_Nme_Jb", "Completed Jobs"));

  @Override
  public void initialize(URL url, ResourceBundle rb) {
    TreeItem<String> rootNode = new TreeItem<>("Job Details");

    rootNode.setExpanded(true);

    for (JobProgress jb : jobList) {
      Text text = new Text();
      ProgressBar bar = new ProgressBar(0.0);
      bar.progressProperty().bind(jb.jobProgressProperty());

      text.setBoundsType(TextBoundsType.VISUAL);
      text.setFill(Color.BLACK);
      text.textProperty().bind(jb.jobProgressStringProperty());

      StackPane stack = new StackPane();
      stack.getChildren().addAll(bar, text);
      Node icon = stack;

      TreeItem<String> empLeaf = new TreeItem<>(jb.getJobName(), icon);
      boolean found = false;
      for (TreeItem<String> depNode : rootNode.getChildren()) {
        if (depNode.getValue().contentEquals(jb.getJobStatus())) {
          depNode.getChildren().add(empLeaf);
          found = true;
          break;
        }
      }
      if (!found) {
        TreeItem<String> depNode = new TreeItem<>(jb.getJobStatus());
        rootNode.getChildren().add(depNode);
        depNode.getChildren().add(empLeaf);
      }
    }
    treeView.setRoot(rootNode);
    new Thread(simulateProgress()).start();

  }

  private Task<Void> simulateProgress() {
    Task<Void> task = new Task<Void>() {

      @Override
      protected Void call() throws Exception {
        for (double i = 0.0; i <= 1.0; i += 0.1) {
          for (JobProgress job : jobList) {
            job.setJobProgress(i);
          }
          Thread.sleep(1000);
        }
        return null;
      }
    };
    return task;
  }
}

JavaFXApplication2.java

import java.net.URL;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class JavaFXApplication2 extends Application {

  @Override
  public void start(Stage stage) throws Exception {
    URL resource = getClass().getResource("FXMLDocument.fxml");
    FXMLLoader loader = new FXMLLoader(resource);
    Parent root = (Parent) loader.load();
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
  }

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

JobProgress.java

import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.util.converter.NumberStringConverter;

public class JobProgress {

  private final SimpleDoubleProperty jobProgress;

  public double getJobProgress() {
    return jobProgress.get();
  }

  public void setJobProgress(double value) {
    jobProgress.set(value);
  }

  public SimpleDoubleProperty jobProgressProperty() {
    return jobProgress;
  }

  private final SimpleStringProperty jobProgressString;

  public void setJobProgressString(String value) {
    jobProgressString.set(value);
  }

  public String getJobProgressString() {
    return jobProgressString.get();
  }

  public SimpleStringProperty jobProgressStringProperty() {
    return jobProgressString;
  }

  private final SimpleStringProperty jobName;

  public String getJobStatus() {
    return jobStatus.get();
  }

  public void setJobStatus(String value) {
    jobStatus.set(value);
  }

  private final SimpleStringProperty jobStatus;

  public String getJobName() {
    return jobName.get();
  }

  public void setJobName(String value) {
    jobName.set(value);
  }

  public JobProgress(String jobName, String jobStatus) {
    this.jobName = new SimpleStringProperty(jobName);
    this.jobStatus = new SimpleStringProperty(jobStatus);
    this.jobProgress = new SimpleDoubleProperty(0.0);
    this.jobProgressString = new SimpleStringProperty();
    Bindings.bindBidirectional(jobProgressString, jobProgress, new NumberStringConverter());
  }
}

解决方案如下所示:

enter image description here