使用FXML在JavaFX中自定义ListView

时间:2013-10-25 11:01:42

标签: javafx fxml

我想在javafx中创建自定义列表视图。在这里,我需要在列表单元格中绑定多个组件,如下所示 一个标签,一个文本字段,一个HBox下的一个按钮和 两个按钮,一个超链接,一个标签在另一个HBox和 这些HBox属于一个VBox和 这个VBox属于单个列表单元格 它将重复并制作一个列表视图。

代码是

<ListView fx:id="ListView" layoutX="0" layoutY="30" prefWidth="600" prefHeight="300">
    <HBox fx:id="listBox" alignment="CENTER_LEFT">
        <padding><Insets top="5" bottom="5" left="5"></Insets> </padding>
        <HBox alignment="CENTER_LEFT" prefWidth="170" minWidth="88">
            <Label fx:id="surveyName" text="Field A" styleClass="Name"></Label>
        </HBox>
        <VBox styleClass="Description" prefWidth="155" minWidth="86">

            <HBox>
                <HBox styleClass="surveyDesIcon" prefWidth="20" prefHeight="16"></HBox>
                <Label fx:id="surveyCode" text="PRW3456HJ"></Label>
            </HBox>
            <HBox>
                <HBox styleClass="DateIcon" prefWidth="20" prefHeight="16"></HBox>
                <Label fx:id="Date" text="PRW3456HJ"></Label>
            </HBox>
        </VBox>
        <HBox fx:id="Status" prefWidth="160" minWidth="80">
            <Label fx:id="StatusLabel" text="Checking Files.."/>
        </HBox>
        <HBox fx:id="StatusIcon1" prefWidth="50" prefHeight="50" alignment="CENTER">
            <Label styleClass="StatusIcon1" prefWidth="24" prefHeight="24" alignment="CENTER"/>
        </HBox>
        <HBox fx:id="StatusIcon2" prefWidth="50" prefHeight="50" styleClass="StatusIconBox" alignment="CENTER">
            <Hyperlink styleClass="StatusIcon2" prefWidth="24" maxHeight="24" alignment="CENTER"/>
        </HBox>
    </HBox>
</ListView>

3 个答案:

答案 0 :(得分:78)

我理解你的问题。在Listview中设置项目主要有两种方式:

1。创建ObservableList并使用ListViewObservableList)设置listView.setItems(observableList)的项目。

2。使用setCellFactory()类的ListView方法。

您更愿意使用setCellFactory()方法,因为这种方法简化了流程,并且有助于分离业务逻辑和UI(FXML)。


以下是更详细的解释:


1。创建名为listview.fxml的新FXML文件以包含ListView,并将ListViewController类设置为其控制器:

文件: listview.fxml

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.ListView?>
<?import demo.ListViewController?>

<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
     <ListView fx:id="listView"/>
</GridPane>

2. :创建控制器并将其命名为ListViewController 控制器可以加载listview.fxml文件并访问listview

文件: ListViewController.java

package demo;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.io.IOException;
import java.util.Set;

public class ListViewController
{
    @FXML
    private ListView listView;
    private Set<String> stringSet;
    ObservableList observableList = FXCollections.observableArrayList();

    public ListViewController()
    {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listview.fxml"));
        fxmlLoader.setController(this);
        try
        {
            Parent parent = (Parent)fxmlLoader.load();
            Scene scene = new Scene(parent, 400.0 ,500.0);
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void setListView()
    {
        stringSet.add("String 1");
        stringSet.add("String 2");
        stringSet.add("String 3");
        stringSet.add("String 4");
        observableList.setAll(stringSet);
        listView.setItems(observableList);
        listView.setCellFactory(new Callback<ListView<String>, javafx.scene.control.ListCell<String>>()
        {
            @Override
            public ListCell<String> call(ListView<String> listView)
            {
                return new ListViewCell();
            }
        });
    }
}

3。首先,您需要设置ObservableList的值。 这非常重要。
然后,使用ObservableList设置列表项,并在setCellFactory()上调用ListView方法。在给定的示例中,我只需将String值添加到String集(Set<String> stringSet)。


4。setCellFactory()上调用ListView方法后,它将返回ListCell。因此,为了简单起见,我添加了一个扩展ListCell的类,setGraphic()方法适用于ListCell(),并将设置ListCell的项目。 / p>

文件: ListViewCell.java

package demo;

import javafx.scene.control.ListCell;

public class ListViewCell extends ListCell<String>
{
    @Override
    public void updateItem(String string, boolean empty)
    {
        super.updateItem(string,empty);
        if(string != null)
        {
            Data data = new Data();
            data.setInfo(string);
            setGraphic(data.getBox());
        }
    }
}

5. 我刚刚添加了一个类,它将加载listCellItem.fxml并返回HBox,其中包含其他组件作为子项。
然后将HBox设置为ListCell

文件: listCellItem.fxml

 <?import demo.Data?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.control.Label?>

<HBox xmlns:fx="http://javafx.com/fxml" fx:id="hBox">
<children>
    <Label  fx:id="label1"/>
    <Label  fx:id="label2"/>
</children>
</HBox>

文件: Data.java

package demo;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import java.io.IOException;

public class Data
{
    @FXML
    private HBox hBox;
    @FXML
    private Label label1;
    @FXML
    private Label label2;

    public Data()
    {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/listCellItem.fxml"));
        fxmlLoader.setController(this);
        try
        {
            fxmlLoader.load();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void setInfo(String string)
    {
        label1.setText(string);
        label2.setText(string);
    }

    public HBox getBox()
    {
        return hBox;
    }
}

使用这种方式,您可以使用setCellFactory()方法分离业务逻辑和FXML。

希望这有用。

答案 1 :(得分:4)

@ Anvay 的上述示例需要进行一些调整才能正常工作。这些都是设置在轨道上的简单方法。

  1. ListViewController需要在JavaFX应用程序线程上运行。
  2. 您只能从JavaFX 初始化()方法调用注入的@FXML元素
  3. 需要调用setListView()
  4. 示例中的stringSet需要在调用setListView()之前使用new进行分配。
  5. 下面的ListViewController可以处理这些更改。我将“ stringSet ”更改为列表“ stringList ”。控制器几乎是 Scene Builder 2

    提供的样本控制器
     public class ListViewController 
     {
    
         @FXML private   ResourceBundle      resources;
    
         @FXML private   URL                 location;
    
         @FXML private   ListView            listView;
    
         private         List<String>        stringList     = new ArrayList<>(5);
         private         ObservableList      observableList = FXCollections.observableArrayList();
    
         public void setListView(){
    
             stringList.add("String 1");
             stringList.add("String 2");
             stringList.add("String 3");
             stringList.add("String 4");
    
             observableList.setAll(stringList);
    
             listView.setItems(observableList);
    
             listView.setCellFactory(
                 new Callback<ListView<String>, javafx.scene.control.ListCell<String>>() {
                     @Override
                     public ListCell<String> call(ListView<String> listView) {
                         return new ListViewCell();
                     }
                 });
         }
    
         @FXML
         void initialize() {
             assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file 'CustomList.fxml'.";
    
             setListView();
         }
    
     }//ListViewController
    

    需要从JavaFX应用程序的main()方法启动JavaFX平台。 Netbeans从Maven JavaFX应用程序模板中提供了大部分结构。

    public class MainApp extends Application {
    
        @Override
        public void start(Stage stage) throws Exception {
    
            Parent root = FXMLLoader.load(getClass().getResource("/fxml/CustomList.fxml"));
    
            Scene scene = new Scene(root);
            scene.getStylesheets().add("/styles/Styles.css");
    
            stage.setTitle("CustomList");
            stage.setScene(scene);
            stage.show();
        }
    
        /**
         *  The main() method is ignored in correctly deployed JavaFX application.
         * 
         *  @param args the command line arguments
         **/
        public static void main(String[] args) {
    
            launch(args);
        }
    }
    

答案 2 :(得分:1)

The answer by Anvay for some reason didnt work for me, what i had to do to fix it was just some very small tweaks:

  1. remove import data statement from listCellItem.fxml
  2. as the comment below the post states in Data.java put hBox = fmxlLoader.load()
  3. I also had a main class (intellij auto generated).

    public class MainMain extends Application {
    
    @Override
    public void start(Stage primaryStage) throws Exception{
    
    FXMLLoader fxmlLoader = new 
    FXMLLoader(getClass().getResource("MainController.fxml"));
    try
    {
        Parent root = fxmlLoader.load();
    
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Title");
        primaryStage.show();
    }
    catch (IOException e)
    {
        throw new RuntimeException(e);
    }
    }
    
    
    
    public static void main(String[] args) {
        launch(args);
    }
    

I know this was probably obvious for most of the experts here, but these issues perplexed me for hours while i was debugging.