关于如何显示JavaFX ListView单元格的从属视图的建议

关于如何显示JavaFX ListView单元格的从属视图的建议,java,listview,javafx-8,Java,Listview,Javafx 8,我们希望将ListView填充为单个JavaFX视图的实例列表。这是我们试图早点开始工作的事情,但时间不够了。当时的后备解决方案是有一个四行的静态面板(即不使用ListView)。在静态版本中,每一行都是同一JavaFX(FXML)视图的实例 每一行都是与一个小FXML文件匹配的控制器的一个单独(实例)。其工作方式是,当调用initialize()方法时,视图将获得一个id,该id映射到模型数据,然后控制器/“视图”管理自己的数据行。在静态情况下,我们可以使用FXML include指令,这使事

我们希望将ListView填充为单个JavaFX视图的实例列表。这是我们试图早点开始工作的事情,但时间不够了。当时的后备解决方案是有一个四行的静态面板(即不使用ListView)。在静态版本中,每一行都是同一JavaFX(FXML)视图的实例

每一行都是与一个小FXML文件匹配的控制器的一个单独(实例)。其工作方式是,当调用initialize()方法时,视图将获得一个id,该id映射到模型数据,然后控制器/“视图”管理自己的数据行。在静态情况下,我们可以使用FXML include指令,这使事情变得非常简单。我将调用单独的行,一个行视图

要以这种方式使用ListView,我们需要执行以下操作:

  • 列表视图控制器必须调用“列表加载器”来加载/初始化列表
  • 我们希望行视图在ListView中显示为单独的行
    • 如果ListView可以采用(子)视图/(子)组件,那么这样做似乎没有问题
    • 行视图是预打包的子视图
  • 需要能够根据行更改的需要“加载”新行视图FXML定义。
    • 是否有方法“克隆”现有视图以制作副本
  • 虽然该问题与POJO问题类似:

    主要区别在于我们的行可以是任意的FXML。我认为,通过调整我们现有的固定尺寸面板,机械部分将发挥作用。关键概念是建立显示“scaffold”并允许交换视图,而不是每次设计/需求更改时都必须重新编码列表框

    更新…

    我取得了一些进展。我可以使用名为“ViewLoader”的类加载FXML文件,该类使用loadView(urlStr)方法。(puts,仅写入日志或sysout)

    我从一个已知的布局开始,因此loadView()加载并返回一个AnchorPane,该AnchorPane在名为“urlStr”的文件中定义。到现在为止,一直都还不错。在调试器中,我可以观察已加载的视图,它将加载其控制器。每个单元都有自己的控制器,并以这种方式独立工作

    在第一步中,天真的方法是尝试通过CellFactory设置视图。Bzzt!这会使视图无效,因此不能这样做

    显然,我们需要的是一种用自定义单元格视图加载ListView的方法。但我对如何做到这一点感到困惑。目前,我们正在使用一个名为:

    public class CustomListView
    {
        :
    
        public class  CustomCellFactory implements  Callback<ListView<MyObject>, ListCell<MyObject>> 
        {
    
            @Override
            public ListCell<MyObject> call( ListView<MyObject> listView ) {
    
                ListCell<MyObject> cell = new ListCellType();
    
                return cell;
            }
    
        }//CustomCellCallback class
    
    }//CustomListView
    
    公共类CustomListView
    {
    :
    公共类CustomCellFactory实现回调
    {
    @凌驾
    公共ListCell调用(ListView ListView){
    ListCell=新的ListCellType();
    返回单元;
    }
    }//CustomCellCallback类
    }//自定义列表视图
    
    在我看来,工厂模式的逻辑,然后新ListCellType()应该返回加载的AnchorPane作为视图。其中ListCellType定义为

    公共类ListCellType扩展ListCell
    {
    :
    }//列表单元类型
    
    然而,在我看来,似乎我们需要工厂返回加载的视图,因此

      ListCell<MyObject> cell = new ListCellType();
    
    ListCell cell=new ListCellType();
    
    这个应用程序需要返回一个JavaFX,或者在这个特定的情况下返回一个(稍后我们可以使它更通用)。好像:

    ListCell,需要ListView而不是节点


    如何做到这一点,也许使用不同的工厂/更新方案?

    我相信我们已经找到了一个原始答案,它足以证明概念,并可用作“通用”模式的起点。内核来自此自定义ListView示例的几个扩展:

    • ,及
    如前所述。这些示例使用简单对象作为列表内容。第一个要求是拥有子视图的列表视图,其中子视图是另一个FXML文件。从概念上讲,这类似于使用FXML运算符的ListVew

    原始解决方案的基础是将单元数据与单元视图配对。因此,对于自定义单元格视图列表,ListView需要一个(视图、数据)元组的可观察列表。或者,当子视图的控制器足够智能,可以在模型中查找数据时,这可能只是一个“子视图列表”。现在,我们的原始示例将数据和视图的一个实例放在一起,即

    public class ListCellView   extends CellView
    {
        @FXML private Label             label1;
        @FXML private Label             label2;
    
    
        @Override
        public void setInfo( MyObject myObject ){
    
           label1.setText( myObject.getDay() );
           label2.setText( Integer.toString( myObject.getNumber() ) );
    
           label1.setTextFill( myObject.getColor() );
        }
    
    
            /**
             *  Initialize
             *    -- must use java -ea <program>, to check asserts
             **/
        @FXML
        @Override
        protected void initialize() {
    
            super.initialize();
    
            assert label1       != null : "fx:id=\"label1\" was not injected: check your FXML file 'CustomCell.fxml'.";
            assert label2       != null : "fx:id=\"label2\" was not injected: check your FXML file 'CustomCell.fxml'.";
        }
    
    }//ListCellView
    
    在运行时代码中,ListView类:CustomListView由JavaFX应用程序平台加载。为了测试该过程,我们使用静态数据初始化了显示,所有这些数据都使用相同的FXML视图,如下所示的“MyObject”对象列表

    public class CustomListView
    {
        @FXML private ResourceBundle        resources;
        @FXML private URL                   location;
        @FXML private ListView<MyObject>    listView;
    
        List<MyObject>                      myList              = prepareMyList();
        ObservableList<MyObject>            myObservableList    = FXCollections.observableList( this.myList );
    
    
        private void setListView(){
    
            this.listView.setItems( this.myObservableList );
    
            listView.setCellFactory(
                new Callback<ListView<MyObject>, javafx.scene.control.ListCell<MyObject>>() {
                    @Override
                    public ListCell<MyObject> call(ListView<MyObject> listView) {
                        return new CustomListCell();
                    }
                }//callback
            );//setCellFactory
    
        }//setListView
    
            /**
             * Initializes the controller class.
             */
        @FXML
        void initialize() {
    
            assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file 'CustomList.fxml'.";
    
            this.setListView();
        }
    
            /**
             *  Prepare My List
             *    - Create dummy list of Views and data = MyObject-s
             **/
        private List<MyObject> prepareMyList() {
    
            final String    FXML_VIEW = "/fxml/ListCell.fxml";
            List<MyObject>  newList   = new ArrayList<>();
    
            newList.add(new MyObject( FXML_VIEW, "Sunday",    50, Color.RED        ));
            newList.add(new MyObject( FXML_VIEW, "Monday",    60, Color.GREEN      ));
            newList.add(new MyObject( FXML_VIEW, "Tuesday",   20, Color.BLUE       ));
            newList.add(new MyObject( FXML_VIEW, "Wednesday", 90, Color.VIOLET     ));
            newList.add(new MyObject( FXML_VIEW, "Thursday",  30, Color.BLUEVIOLET ));
            newList.add(new MyObject( FXML_VIEW, "Friday",    62, Color.BROWN      ));
            newList.add(new MyObject( FXML_VIEW, "Saturday",  65, Color.GOLD       ));
    
            return newList;
        }
    
    }//CustomListView
    
    MyObject保留“ViewLoader”类型,以在加载后保存FXML视图和控制器

    public class ViewLoader
    {
        private    String      fxmlStr         = "(none)";
        private    Parent      rootNode        = null;
        private    CellView    viewController  = null;
    
    
        public  void loadView( final String urlStr ){
    
            Parent      root        = null;
            URL         fxmlResource;
            FXMLLoader  fxmlLoader  = null;
            try
            {
                fxmlResource = getClass().getResource( urlStr );
                fxmlLoader = new FXMLLoader( fxmlResource, Resources.getResourceBundle() );
    
                root = fxmlLoader.load( );
    
                this.viewController = fxmlLoader.getController();
                this.fxmlStr        = urlStr;
                this.rootNode       = root;
            }
            catch (Exception ex)
            {
                puts( "  * Exception on FXMLLoader.load()");
                puts( "  * "+ex.getMessage());
                puts( "    ----------------------------------------\n");
            }
        }
    
    
        public  CellView    getView(){
    
            return this.viewController;
        }
    
        public  ViewLoader( final String urlStr ){
    
            loadView( urlStr );
        }
    
    }//ViewLoader class
    
    此示例在JavaFX ListView中愉快地显示了下级视图单元格的列表。子视图需要理解“MyObject”数据。在最终版本中,数据将与子视图解耦,并从某个模型对象检索

    每个ListView单元格都是正在使用的特定“ListCellView”控制器的唯一实例。对此模式的改进将能够使用数据源初始化子视图,并保持数据与视图的干净分离。与此不同,子视图必须手动加载。如果需要,可以在CustomListView.FXML主目录中将FXML文件名指定为不可见标签


    希望其他人觉得它有用,或者如果没有,至少你可以看到更多管理ListView-s的工作方式。

    hmm。。。从技术上讲,您需要知道要加载哪个fxml的数据元素列表和从该fxml加载视图的cellFactory。但从未尝试过。只想对每一行重复使用相同的FXML。那么你说的是一个定制的细胞工厂。
    public class ListCellView   extends CellView
    {
        @FXML private Label             label1;
        @FXML private Label             label2;
    
    
        @Override
        public void setInfo( MyObject myObject ){
    
           label1.setText( myObject.getDay() );
           label2.setText( Integer.toString( myObject.getNumber() ) );
    
           label1.setTextFill( myObject.getColor() );
        }
    
    
            /**
             *  Initialize
             *    -- must use java -ea <program>, to check asserts
             **/
        @FXML
        @Override
        protected void initialize() {
    
            super.initialize();
    
            assert label1       != null : "fx:id=\"label1\" was not injected: check your FXML file 'CustomCell.fxml'.";
            assert label2       != null : "fx:id=\"label2\" was not injected: check your FXML file 'CustomCell.fxml'.";
        }
    
    }//ListCellView
    
    public abstract class CellView
    {
        @FXML private ResourceBundle    resources;
        @FXML private URL               location;
        @FXML private AnchorPane        customCell;
        @FXML private HBox              hBox;
    
        public abstract void setInfo( MyObject myObject );
    
        public HBox getBox() {
    
            return hBox;
        }
    
        protected void initialize() {
    
            assert customCell   != null : "fx:id=\"customCell\" was not injected: check your FXML file 'CustomCell.fxml'.";
            assert hBox         != null : "fx:id=\"hBox\" was not injected: check your FXML file 'CustomCell.fxml'.";
        }
    
        public  CellView(){
        }
    
    }//CellView
    
    public class CustomListView
    {
        @FXML private ResourceBundle        resources;
        @FXML private URL                   location;
        @FXML private ListView<MyObject>    listView;
    
        List<MyObject>                      myList              = prepareMyList();
        ObservableList<MyObject>            myObservableList    = FXCollections.observableList( this.myList );
    
    
        private void setListView(){
    
            this.listView.setItems( this.myObservableList );
    
            listView.setCellFactory(
                new Callback<ListView<MyObject>, javafx.scene.control.ListCell<MyObject>>() {
                    @Override
                    public ListCell<MyObject> call(ListView<MyObject> listView) {
                        return new CustomListCell();
                    }
                }//callback
            );//setCellFactory
    
        }//setListView
    
            /**
             * Initializes the controller class.
             */
        @FXML
        void initialize() {
    
            assert listView != null : "fx:id=\"listView\" was not injected: check your FXML file 'CustomList.fxml'.";
    
            this.setListView();
        }
    
            /**
             *  Prepare My List
             *    - Create dummy list of Views and data = MyObject-s
             **/
        private List<MyObject> prepareMyList() {
    
            final String    FXML_VIEW = "/fxml/ListCell.fxml";
            List<MyObject>  newList   = new ArrayList<>();
    
            newList.add(new MyObject( FXML_VIEW, "Sunday",    50, Color.RED        ));
            newList.add(new MyObject( FXML_VIEW, "Monday",    60, Color.GREEN      ));
            newList.add(new MyObject( FXML_VIEW, "Tuesday",   20, Color.BLUE       ));
            newList.add(new MyObject( FXML_VIEW, "Wednesday", 90, Color.VIOLET     ));
            newList.add(new MyObject( FXML_VIEW, "Thursday",  30, Color.BLUEVIOLET ));
            newList.add(new MyObject( FXML_VIEW, "Friday",    62, Color.BROWN      ));
            newList.add(new MyObject( FXML_VIEW, "Saturday",  65, Color.GOLD       ));
    
            return newList;
        }
    
    }//CustomListView
    
    public class MyObject
    {
        private     ViewLoader      view    = null;
        private     String          day;
        private     int             number;
        private     Color           colour;
    
        public  String getDay() {
    
            return day;
        }
    
        public  int getNumber() {
    
            return number;
        }
    
        public  Color getColor(){
    
            return colour;
        }
    
    
        public  CellView    getView(){
    
            return this.view.getView();
        }
    
    
        public  MyObject( final String fxmlView, final String d, final int n, final Color c) {
    
            view    = new ViewLoader( fxmlView );
    
            day     = d;
            number  = n;
            colour  = c;
        }
    
    }//MyObject class
    
    public class ViewLoader
    {
        private    String      fxmlStr         = "(none)";
        private    Parent      rootNode        = null;
        private    CellView    viewController  = null;
    
    
        public  void loadView( final String urlStr ){
    
            Parent      root        = null;
            URL         fxmlResource;
            FXMLLoader  fxmlLoader  = null;
            try
            {
                fxmlResource = getClass().getResource( urlStr );
                fxmlLoader = new FXMLLoader( fxmlResource, Resources.getResourceBundle() );
    
                root = fxmlLoader.load( );
    
                this.viewController = fxmlLoader.getController();
                this.fxmlStr        = urlStr;
                this.rootNode       = root;
            }
            catch (Exception ex)
            {
                puts( "  * Exception on FXMLLoader.load()");
                puts( "  * "+ex.getMessage());
                puts( "    ----------------------------------------\n");
            }
        }
    
    
        public  CellView    getView(){
    
            return this.viewController;
        }
    
        public  ViewLoader( final String urlStr ){
    
            loadView( urlStr );
        }
    
    }//ViewLoader class