2011-12-08 44 views
30

Tôi dường như không tìm thấy bất kỳ tài liệu nào về chủ đề này. Để đưa ra một ví dụ cụ thể hơn, giả sử tôi muốn tạo một thành phần đơn giản kết hợp hộp kiểm và nhãn. Sau đó, điền một ListView với các cá thể của thành phần tùy chỉnh này.Làm cách nào để tạo các thành phần tùy chỉnh trong JavaFX 2.0 bằng FXML?

UPDATE: xem câu trả lời của tôi cho mã hoàn chỉnh

UPDATE 2: Đối với một up-to-date hướng dẫn, xin vui lòng, tham khảo ý kiến ​​the official documentation. Đã có rất nhiều new stuff đã được thêm vào trong 2.2. Cuối cùng, Introduction to FXML bao gồm tất cả mọi thứ bạn cần biết về FXML.

CẬP NHẬT 3: Hendrik Ebbers đã thực hiện một điều cực kỳ hữu ích blog post về các điều khiển giao diện người dùng tùy chỉnh.

+0

Ví dụ kiểm soát tùy chỉnh trong liên kết 'tài liệu chính thức' của bạn bị hỏng. Trong đó có hai phương pháp không phải là một phần của API. –

+0

@danLeon đầu tiên, nó không phải là MY "tài liệu chính thức". đó là THE "tài liệu chính thức" được viết bởi các nhân viên Oracle đang làm việc trên JavaFX. thứ hai, mã tôi đang liên kết để chứa một ví dụ làm việc về cách tạo các thành phần tùy chỉnh trong JavaFX 2.2. rất có thể là phiên bản bạn có là cũ hơn, do đó thiếu phương pháp. Đây là một điểm nổi bật từ trang đó: "Trước khi bạn bắt đầu, hãy đảm bảo rằng phiên bản NetBeans IDE mà bạn đang sử dụng hỗ trợ JavaFX 2.2" – Andrey

+0

Bạn nói đúng! IDE của tôi là dưới JavaFx 2.1, Cảm ơn bạn đã bình luận. Bây giờ trên 2,2, tôi đã xóa bất kỳ phiên bản java trước đó trong máy tính của tôi. –

Trả lời

28

Cập nhật: Đối với một up-to-date hướng dẫn, xin vui lòng, tham khảo ý kiến ​​the official documentation. Đã có rất nhiều new stuff đã được thêm vào trong 2.2. Ngoài ra, Introduction to FXML bao gồm tất cả mọi thứ bạn cần biết về FXML. Cuối cùng, Hendrik Ebbers đã thực hiện một số cực kỳ hữu ích blog post về các điều khiển giao diện người dùng tùy chỉnh.


Sau một vài ngày kể từ khi nhìn xung quanh API và đọc qua một số tài liệu (Intro to FXML, Getting started with FXMLProperty binding, Future of FXML) Tôi đã tìm ra một giải pháp khá hợp lý. Thông tin nhỏ nhất về phía trước mà tôi đã học được từ thí nghiệm nhỏ này là trường hợp của bộ điều khiển (được khai báo với bộ điều khiển fx: trong FXML) được giữ bởi FXMLLoader đã tải tệp FXML ... Tồi tệ nhất, điều này quan trọng thực tế chỉ được nhắc đến trong one place trong tất cả các tài liệu tôi thấy:

một bộ điều khiển nói chung là chỉ hiển thị cho các bộ nạp FXML tạo ra nó

Vì vậy, hãy nhớ, để lập trình (từ mã Java) có được một tham chiếu đến trường hợp của một bộ điều khiển đã được khai báo trong FXML với fx:controller sử dụng FXMLLoader.getController() (tham khảo việc thực hiện lớp ChoiceCell dưới đây để có một ví dụ hoàn chỉnh).

Một điều cần lưu ý là Property.bindBiderctional() sẽ đặt giá trị của thuộc tính gọi thành giá trị của thuộc tính được chuyển làm đối số. Với hai thuộc tính boolean target (ban đầu được đặt thành false) và source (ban đầu được đặt thành true), hãy gọi target.bindBidirectional(source) sẽ đặt giá trị target thành true.Rõ ràng, bất kỳ thay đổi tiếp theo hoặc bất động sản sẽ thay đổi giá trị tài sản của người khác (target.set(false) sẽ làm cho giá trị của source được thiết lập để false):

BooleanProperty target = new SimpleBooleanProperty();//value is false 
BooleanProperty source = new SimpleBooleanProperty(true);//value is true 
target.bindBidirectional(source);//target.get() will now return true 
target.set(false);//both values are now false 
source.set(true);//both values are now true 

Dù sao, đây là mã hoàn chỉnh trình bày cách FXML và Java có thể làm việc cùng nhau (cũng như một vài điều hữu ích khác)

cấu trúc

gói:

com.example.javafx.choice 
    ChoiceCell.java 
    ChoiceController.java 
    ChoiceModel.java 
    ChoiceView.fxml 
com.example.javafx.mvc 
    FxmlMvcPatternDemo.java 
    MainController.java 
    MainView.fxml 
    MainView.properties 

FxmlMvcPatternDemo.java

package com.example.javafx.mvc; 

import java.util.ResourceBundle; 
import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class FxmlMvcPatternDemo extends Application 
{ 
    public static void main(String[] args) throws ClassNotFoundException 
    { 
     Application.launch(FxmlMvcPatternDemo.class, args); 
    } 

    @Override 
    public void start(Stage stage) throws Exception 
    { 
     Parent root = FXMLLoader.load 
     (
      FxmlMvcPatternDemo.class.getResource("MainView.fxml"), 
      ResourceBundle.getBundle(FxmlMvcPatternDemo.class.getPackage().getName()+".MainView")/*properties file*/ 
     ); 

     stage.setScene(new Scene(root)); 
     stage.show(); 
    } 
} 

MainView.fxml

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

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

<VBox 
    xmlns:fx="http://javafx.com/fxml" 
    fx:controller="com.example.javafx.mvc.MainController" 

    prefWidth="300" 
    prefHeight="400" 
    fillWidth="false" 
> 
    <children> 
     <Label text="%title" /> 
     <ListView fx:id="choicesView" /> 
     <Button text="Force Change" onAction="#handleForceChange" /> 
    </children> 
</VBox> 

MainView.properties

title=JavaFX 2.0 FXML MVC demo 

MainController.java

package com.example.javafx.mvc; 

import com.example.javafx.choice.ChoiceCell; 
import com.example.javafx.choice.ChoiceModel; 
import java.net.URL; 
import java.util.ResourceBundle; 
import javafx.collections.FXCollections; 
import javafx.event.ActionEvent; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.ListCell; 
import javafx.scene.control.ListView; 
import javafx.util.Callback; 

public class MainController implements Initializable 
{ 
    @FXML 
    private ListView<ChoiceModel> choicesView; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) 
    { 
     choicesView.setCellFactory(new Callback<ListView<ChoiceModel>, ListCell<ChoiceModel>>() 
     { 
      public ListCell<ChoiceModel> call(ListView<ChoiceModel> p) 
      { 
       return new ChoiceCell(); 
      } 
     }); 
     choicesView.setItems(FXCollections.observableArrayList 
     (
      new ChoiceModel("Tiger", true), 
      new ChoiceModel("Shark", false), 
      new ChoiceModel("Bear", false), 
      new ChoiceModel("Wolf", true) 
     )); 
    } 

    @FXML 
    private void handleForceChange(ActionEvent event) 
    { 
     if(choicesView != null && choicesView.getItems().size() > 0) 
     { 
      boolean isSelected = choicesView.getItems().get(0).isSelected(); 
      choicesView.getItems().get(0).setSelected(!isSelected); 
     } 
    } 
} 

ChoiceView.fxml

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

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

<HBox 
    xmlns:fx="http://javafx.com/fxml" 

    fx:controller="com.example.javafx.choice.ChoiceController" 
> 
    <children> 
     <CheckBox fx:id="isSelectedView" /> 
     <Label fx:id="labelView" /> 
    </children> 
</HBox> 

ChoiceController.java

package com.example.javafx.choice; 

import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.CheckBox; 
import javafx.scene.control.Label; 

public class ChoiceController 
{ 
    private final ChangeListener<String> LABEL_CHANGE_LISTENER = new ChangeListener<String>() 
    { 
     public void changed(ObservableValue<? extends String> property, String oldValue, String newValue) 
     { 
      updateLabelView(newValue); 
     } 
    }; 

    private final ChangeListener<Boolean> IS_SELECTED_CHANGE_LISTENER = new ChangeListener<Boolean>() 
    { 
     public void changed(ObservableValue<? extends Boolean> property, Boolean oldValue, Boolean newValue) 
     { 
      updateIsSelectedView(newValue); 
     } 
    }; 

    @FXML 
    private Label labelView; 

    @FXML 
    private CheckBox isSelectedView; 

    private ChoiceModel model; 

    public ChoiceModel getModel() 
    { 
     return model; 
    } 

    public void setModel(ChoiceModel model) 
    { 
     if(this.model != null) 
      removeModelListeners(); 
     this.model = model; 
     setupModelListeners(); 
     updateView(); 
    } 

    private void removeModelListeners() 
    { 
     model.labelProperty().removeListener(LABEL_CHANGE_LISTENER); 
     model.isSelectedProperty().removeListener(IS_SELECTED_CHANGE_LISTENER); 
     isSelectedView.selectedProperty().unbindBidirectional(model.isSelectedProperty()) 
    } 

    private void setupModelListeners() 
    { 
     model.labelProperty().addListener(LABEL_CHANGE_LISTENER); 
     model.isSelectedProperty().addListener(IS_SELECTED_CHANGE_LISTENER); 
     isSelectedView.selectedProperty().bindBidirectional(model.isSelectedProperty()); 
    } 

    private void updateView() 
    { 
     updateLabelView(); 
     updateIsSelectedView(); 
    } 

    private void updateLabelView(){ updateLabelView(model.getLabel()); } 
    private void updateLabelView(String newValue) 
    { 
     labelView.setText(newValue); 
    } 

    private void updateIsSelectedView(){ updateIsSelectedView(model.isSelected()); } 
    private void updateIsSelectedView(boolean newValue) 
    { 
     isSelectedView.setSelected(newValue); 
    } 
} 

ChoiceModel.java

package com.example.javafx.choice; 

import javafx.beans.property.BooleanProperty; 
import javafx.beans.property.SimpleBooleanProperty; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 

public class ChoiceModel 
{ 
    private final StringProperty label; 
    private final BooleanProperty isSelected; 

    public ChoiceModel() 
    { 
     this(null, false); 
    } 

    public ChoiceModel(String label) 
    { 
     this(label, false); 
    } 

    public ChoiceModel(String label, boolean isSelected) 
    { 
     this.label = new SimpleStringProperty(label); 
     this.isSelected = new SimpleBooleanProperty(isSelected); 
    } 

    public String getLabel(){ return label.get(); } 
    public void setLabel(String label){ this.label.set(label); } 
    public StringProperty labelProperty(){ return label; } 

    public boolean isSelected(){ return isSelected.get(); } 
    public void setSelected(boolean isSelected){ this.isSelected.set(isSelected); } 
    public BooleanProperty isSelectedProperty(){ return isSelected; } 
} 

ChoiceCell.java

package com.example.javafx.choice; 

import java.io.IOException; 
import java.net.URL; 
import javafx.fxml.FXMLLoader; 
import javafx.fxml.JavaFXBuilderFactory; 
import javafx.scene.Node; 
import javafx.scene.control.ListCell; 

public class ChoiceCell extends ListCell<ChoiceModel> 
{ 
    @Override 
    protected void updateItem(ChoiceModel model, boolean bln) 
    { 
     super.updateItem(model, bln); 

     if(model != null) 
     { 
      URL location = ChoiceController.class.getResource("ChoiceView.fxml"); 

      FXMLLoader fxmlLoader = new FXMLLoader(); 
      fxmlLoader.setLocation(location); 
      fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory()); 

      try 
      { 
       Node root = (Node)fxmlLoader.load(location.openStream()); 
       ChoiceController controller = (ChoiceController)fxmlLoader.getController(); 
       controller.setModel(model); 
       setGraphic(root); 
      } 
      catch(IOException ioe) 
      { 
       throw new IllegalStateException(ioe); 
      } 
     } 
    } 
} 
+0

oooooh NGAY BÂY GIỜ tôi hiểu rồi. cám ơn rất nhiều. – glasspill

+0

vui lòng trợ giúp. tuy nhiên, hãy đảm bảo bạn xem qua các liên kết tôi đã cung cấp trong bản cập nhật. nhiều thứ mới đã được thêm vào trong 2.2 – Andrey

2

Câu trả lời nhanh là < fx: bao gồm thẻ >, tuy nhiên, bạn cần phải đặt ChoiceModel trong lớp Bộ điều khiển.

<VBox 
    xmlns:fx="http://javafx.com/fxml" 

    fx:controller="fxmltestinclude.ChoiceDemo" 
> 
    <children> 
    **<fx:include source="Choice.fxml" />** 
    <ListView fx:id="choices" /> 
    </children> 
</VBox> 
+0

Ngoài ra, hãy xem tài liệu này http://fxexperience.com/wp-content/uploads/2011/08/Introducing-FXML.pdf – JimClarke

+0

ok, nhưng làm cách nào tôi sử dụng Choice.fxml để hiển thị từng mục trong 'lựa chọn' danh sách? – Andrey

4

Đối với JavaFX 2.1, Bạn có thể tạo một bộ phận kiểm soát FXML tùy chỉnh bằng cách này:

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

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

<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="customcontrolexample.FXML1Controller"> 
    <children> 
     <MyComponent welcome="1234"/> 
    </children> 
</VBox> 

đang Component:

MyComponent.java

package customcontrolexample.myCommponent; 

import java.io.IOException; 
import javafx.beans.property.StringProperty; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Node; 
import javafx.scene.layout.Pane; 
import javafx.util.Callback; 

public class MyComponent extends Pane { 

    private Node view; 
    private MyComponentController controller; 

    public MyComponent() { 
     FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("myComponent.fxml")); 
     fxmlLoader.setControllerFactory(new Callback<Class<?>, Object>() { 
      @Override 
      public Object call(Class<?> param) { 
       return controller = new MyComponentController(); 
      } 
     }); 
     try { 
      view = (Node) fxmlLoader.load(); 

     } catch (IOException ex) { 
     } 
     getChildren().add(view); 
    } 

    public void setWelcome(String str) { 
     controller.textField.setText(str); 
    } 

    public String getWelcome() { 
     return controller.textField.getText(); 
    } 

    public StringProperty welcomeProperty() { 
     return controller.textField.textProperty(); 
    } 
} 

MyComponentController.java

package customcontrolexample.myCommponent; 

import java.net.URL; 
import java.util.ResourceBundle; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.TextField; 

public class MyComponentController implements Initializable { 

    int i = 0; 
    @FXML 
    TextField textField; 

    @FXML 
    protected void doSomething() { 
     textField.setText("The button was clicked #" + ++i); 
    } 

    @Override 
    public void initialize(URL location, ResourceBundle resources) { 
     textField.setText("Just click the button!"); 
    } 
} 

myComponent.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.*?> 

<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="customcontrolexample.myCommponent.MyComponentController"> 
    <children> 
    <TextField fx:id="textField" prefWidth="200.0" /> 
    <Button mnemonicParsing="false" onAction="#doSomething" text="B" /> 
    </children> 
</VBox> 

Mã này cần phải kiểm tra nếu không có bộ nhớ rò rỉ.

+1

Cảm ơn rất nhiều, điều đó đã giúp ích rất nhiều. – ShaggyInjun

Các vấn đề liên quan