javafx 无法访问自定义列表单元格中的按钮

javafx cannot access button in custom listcell

问题的核心是我无法刷新或更改场景节点(此处为TablesMain)的内容class(此处为NamePriceCell)。

我正在使用包含其他节点(其中一些是 ListView)的主 StackPane(TableMainController 扩展 StackPane)构建和应用程序。在特定的 ListView(比如 'readitemslistview')中,我创建了一个自定义 ListCell(public class NamePriceCell extends ListCell)并且这个 NamePriceCell listcell 包含一个按钮(plusbtn)。当用户单击 plusbtn 时,数组列表(比如 'chosenitemslist')会被特定单元格中显示的项目填满,同时堆栈窗格中的另一个列表视图(比如 'ordereditemslist')应该触发显示'chosenitemslist'数组列表的内容。

我还没有找到从主控制器引用 NamePriceCell 的 plusbtn 的方法。而且这个post Get ListCell via ListView

Unfortunately right now there is no API to get List Cell by index or to get All children's(listcells) for ListView.

让我放弃了这样的尝试(我无法理解那里提供的解决方案是否适合我的需要,因为我不想刷新同一个列表视图,而是从另一个同一场景)。

所以我发现从 plusbtn 获得鼠标点击动作的唯一方法是在同一个 class (NamePriceCell) 中使用 EventHandler。即使我可以根据 NamePriceCell 按钮上的点击得到一个数组列表(NamePriceCell 在另一个 class 中调用一个静态方法,里面有一个静态数组列表),我可以通过让用户单击 TableMain StackPane 的另一个按钮节点,当用户单击 NamePriceCell 按钮时,我找不到执行该操作的方法。 我尝试的事情之一是在 TablesMainController 中设置一个静态方法,我可以从 NamePriceCell EventHandler 访问它(但后来我必须使 ordereditemslist 的持有者节点静态,即使如此,它也没有用), 为包含带有相应单独控制器的 ordereditemslist 的 VBox 编写一个单独的 fxml,以便我可以在那里有一个方法来创建 ordereditemslist 并设置 cellFactory(但即使 fxml 文件正确显示并从 NamePriceCell 的 EventHandler 调用方法也有效- 显示 System.out - 它不会影响屏幕上的任何内容,除了 ordereditemslist,甚至是我测试过的标签。

我相对来说是一个 java 新手,在 javafx 方面更是如此。非常需要和感谢您的帮助。

我最后一个方法的代码如下:

VboxList.fxml

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.VBox?>
<VBox>
    <Label text="Label" id="label1"/>
    <Label text="Label" id="label2"/>
    <Label text="sdfsdf" id="label3"/>
    <ListView nodeOrientation="LEFT_TO_RIGHT" id="orderedlist" prefHeight="200.0" prefWidth="200.0"  />
</VBox>

VboxListController.java

public class VboxListController extends VBox{
    FXMLLoader fxmlLoader;
    @FXML
    private Label label1;

    @FXML
    private Label label2;

    @FXML
    private ListView<OrderItem> orderedlist;

    public VboxListController() {
        fxmlLoader = new FXMLLoader(getClass().getResource("fxml/VboxList.fxml"));
        //fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    public void getorderedlist(ArrayList<OrderItem> chosenitems){
        ObservableList<OrderItem> myObservableList = FXCollections.observableList(chosenitems);

        this.getChildren().remove(orderedlist);

        orderedlist = new ListView<>();
        orderedlist.setItems(myObservableList);
        orderedlist.setCellFactory(new Callback<ListView<OrderItem>, ListCell<OrderItem>>() {

            @Override
            public ListCell<OrderItem> call(ListView<OrderItem> p) {

                ListCell<OrderItem> cell = new ListCell<OrderItem>() {

                    @Override
                    protected void updateItem(OrderItem dataobj, boolean bln) {
                        super.updateItem(dataobj, bln);
                        if (dataobj != null) {
                            setText(dataobj.read_item_name);
                        }
                    }

                };
                return cell;
            }
        });
        this.getChildren().add(orderedlist);
        orderedlist.setItems(null);
        orderedlist.setItems(myObservableList);
        label1 = null;
        this.getChildren().remove(label1);
        label1 = new Label();
label1.setText("oooops!");
        System.out.println("sdf");
        this.getChildren().add(label1);


    }


}

NamePriceCell.java

public class NamePriceCell extends ListCell<ReadItemChoiceObj> {
    int count=0;
    @FXML
    Label namelbl;

    @FXML
    Label pricelbl;

    @FXML
    Button plusbtn;

    @FXML
    Region spacer;

    @FXML
    HBox cellHbox;

    FXMLLoader mLLoader;

    ReadItemChoiceObj readitem;
    ArrayList<ReadItemChoiceObj> chosenreaditems;
    @Override
    protected void updateItem(ReadItemChoiceObj readitem, boolean empty) {
        super.updateItem(readitem, empty);
        chosenreaditems = new ArrayList<>();

        if(empty || readitem == null) {

            setText(null);
            setGraphic(null);

        } else {

            this.readitem = readitem;
            if (mLLoader == null) {
                mLLoader = new FXMLLoader(getClass().getResource("fxml/NamePriceCell.fxml"));
                mLLoader.setController(this);
                try {
                    mLLoader.load();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }


            namelbl.setText(readitem.getname());
            namelbl.setMaxWidth(500);
            pricelbl.setText(String.format("%.2f", readitem.getprice()));
            pricelbl.setStyle("-fx-font: 8pt  \"Arial\";");
            pricelbl.setMaxWidth(40);
            spacer.setMaxWidth(10);
            spacer.setMinWidth(10);
            plusbtn.setMaxWidth(20);
            cellHbox.setHgrow(namelbl, Priority.ALWAYS);

            cellHbox.setAlignment(Pos.BASELINE_LEFT);

            setText(null);
            setGraphic(cellHbox);

            plusbtn.setOnMouseClicked(whattodohandler);
        }

    }



    EventHandler<MouseEvent> whattodohandler = new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            ChosenItemsStat.getplusbtnclicked(readitem);
            ChosenItemsStat chos = new ChosenItemsStat();

            count+=1;
            plusbtn.setText(String.valueOf(count));
            new VboxListController().getorderedlist(chos.sendchosenlist());
        }
    };

}

和TablesMain.java

的一部分
 @FXML
    Label label1;

    @FXML
    BorderPane borderpane;

    @FXML
    Pane tablepane;

    @FXML
    ListView<DataObj> tabcatlist;

    @FXML
     VBox VboxList;

    @FXML
     VboxListController vboxListController;

  /*  @FXML
    ListView<OrderItem> orderedlist;*/

    @FXML
    VBox leftVbox;

 public TablesMain(Order order) {
        this.order = order;
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml/tablesmain.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException exception) {

            throw new RuntimeException(exception);
        }

        ObservableList<DataObj> myObservableList = FXCollections.observableList(gettable_categories());
        tabcatlist.setItems(myObservableList);
        tabcatlist.setCellFactory(new Callback<ListView<DataObj>, ListCell<DataObj>>() {

            @Override
            public ListCell<DataObj> call(ListView<DataObj> p) {

                ListCell<DataObj> cell = new ListCell<DataObj>() {

                    @Override
                    protected void updateItem(DataObj dataobj, boolean bln) {
                        super.updateItem(dataobj, bln);
                        if (dataobj != null) {
                            setText(dataobj.getname());
                        }
                    }

                };
                return cell;
            }
        });
        if (table_categoryID == 0) table_categoryID = tablecategories.get(0).getid();
        gettables(table_categoryID);
    }

 private void make_readitemlist(int itemcategoryID) {
             readitems = new ArrayList<>();


            .... the readitems  arraylist gets filled up...


        ObservableList<ReadItemChoiceObj> myObservableList = FXCollections.observableList(readitems);
        readitemlist.setItems(myObservableList);

        readitemlist.setCellFactory(new Callback<ListView<ReadItemChoiceObj>,
                                            ListCell<ReadItemChoiceObj>>() {
                                        @Override
                                        public ListCell<ReadItemChoiceObj> call(ListView<ReadItemChoiceObj> list) {
                                            readitemlistcell = new NamePriceCell();

                                            return readitemlistcell;
                                        }

                                    }
        );

        readitemlist.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                ReadItemChoiceObj dataobj = (ReadItemChoiceObj) readitemlist.getSelectionModel().getSelectedItem();
                System.out.println(String.valueOf(dataobj.getid()));

               // showorderedlist(new ChosenItemsStat().sendchosenlist());
            }
        });
        System.out.println(readitemlist.cellFactoryProperty().getClass().toString());
    }

...
}

由于已经将近 5 个月没有其他贡献,我想 post 我的解决方案作为答案。这不是我的,而是受到我问题中提到的 post 的启发(或复制)(或另一个,现在我不记得了,请原谅我可能错位的学分)。 更准确地说,我在 TableMain 文件(主控制器 class)中创建了一个静态 StringProperty。每次单击 plusbtn(扩展 ListCell 的 NamePriceCell class)时,都会在 TableMain 控制器 class 的静态 StringProperty 中强制转换为字符串的随机数。在这个 class 中,StringProperty 添加了一个 ChangeListener,它依次触发(现在从主控制器内部 - 这就是线索)有序列表的刷新(必须使用添加的项目刷新的列表视图) .

NamePriceCell.java

public class NamePriceCell extends ListCell<ReadItemChoiceObj> {
@FXML
Label namelbl;

@FXML
Label pricelbl;

@FXML
Button plusbtn;

@FXML
Region spacer;

@FXML
HBox cellHbox;

FXMLLoader mLLoader;

ReadItemChoiceObj readitem;

@Override
protected void updateItem(ReadItemChoiceObj readitem, boolean empty) {
    super.updateItem(readitem, empty);

    if(empty || readitem == null) {

        setText(null);
        setGraphic(null);

    } else {

        this.readitem = readitem;
        if (mLLoader == null) {
            mLLoader = new FXMLLoader(getClass().getResource("fxml/NamePriceCell.fxml"));
            mLLoader.setController(this);
            try {
                mLLoader.load();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


        namelbl.setText(readitem.getname());
        namelbl.setMaxWidth(500);
        pricelbl.setText(String.format("%.2f", readitem.getprice()));
        pricelbl.setStyle("-fx-font: 8pt  \"Arial\";");
        pricelbl.setMaxWidth(40);
        spacer.setMaxWidth(10);
        spacer.setMinWidth(10);
        plusbtn.setMaxWidth(20);
        cellHbox.setHgrow(namelbl, Priority.ALWAYS);

        cellHbox.setAlignment(Pos.BASELINE_LEFT);

        setText(null);
        setGraphic(cellHbox);

        plusbtn.setOnMouseClicked(whattodohandler);
    }

}



EventHandler<MouseEvent> whattodohandler = new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {


        OrderItem orderitem = new OrderItem();
        orderitem.read_item_name = readitem.getname();
        orderitem.read_item_price=readitem.getprice();
        orderitem.read_itemID=readitem.getid();
        orderitem.choice_name_list = new ArrayList<String>();
        orderitem.choice_price_list = new ArrayList<Float>();
        orderitem.choice_id_list = new ArrayList<Integer>();

        ChosenItemsStat.getplusbtnclicked(orderitem);
        Random rand = new Random();
        TablesMain.stringProperty.setValue(String.valueOf(rand));
    }
};

}

ChosenItemsStat.java(静态 class 接收列表的添加项)

public class ChosenItemsStat {
static ArrayList<OrderItem> chosenorderitemsstat  = new ArrayList<>();
static void getplusbtnclicked(OrderItem orderitem){
    chosenorderitemsstat.add(orderitem);
    }
}

我没有使用 VboxList.fxml 或 VboxListController.java,因为我将其包含在 TablesMain.java 中(见下文)

TablesMain.java(与问题相同,但添加了此内容)

stringProperty = new SimpleStringProperty();
    stringProperty.setValue("");
    tablelbl.setText(stringProperty.getValue());
    stringProperty.addListener(new ChangeListener(){
        @Override
        public void changed(ObservableValue observable, Object oldValue, Object newValue) {
            showselectedlist();
        }
    });

而通过更改 StringProperty 值调用的 showselectedlist() 是下面的那个(在此方法中,单元格的构造与删除的 class (VboxListController)

private void showselectedlist(){
    orderitems.addAll(ChosenItemsStat.chosenorderitemsstat);
    ChosenItemsStat.chosenorderitemsstat.clear();
    ListView<OrderItem> selectedlist = new ListView<>();
    ObservableList<OrderItem> myObservableList = FXCollections.observableList(orderitems);
    selectedlist.setItems(myObservableList);
    selectedlist.setMaxWidth(220);
    selectedlist.setCellFactory(new Callback<ListView<OrderItem>, ListCell<OrderItem>>() {

        @Override
        public ListCell<OrderItem> call(ListView<OrderItem> p) {
            ListCell<OrderItem> cell = new ListCell<OrderItem>(){
                OrderedCell ordcell= new OrderedCell();
                @Override
                protected void updateItem(OrderItem orderitem, boolean bln) {
                    super.updateItem(orderitem, bln);
                    if (orderitem != null) {
                        Float price = ordcell.getitemTempPrice(orderitem,orderitem.read_item_price);

                        HBox namepriceHbox = new HBox();
                        Label lblprice= new Label(String.format("%.2f",price));
                        Region betweenregion = new Region();
                        Label lblname = new Label();

                        lblname.setText(orderitem.read_item_name );
                        lblname.setStyle("-fx-font: 10pt  \"Arial\";-fx-font-weight:bold");

                        namepriceHbox.setHgrow(betweenregion, Priority.ALWAYS);
                        namepriceHbox.getChildren().addAll(lblname,betweenregion,lblprice);

                        VBox allVbox = new VBox();
                        Text lblchoices = new Text();

                        String choices = ordcell.choicestostring(orderitem);
                        lblchoices.setText(choices);
                        lblchoices.setWrappingWidth(listpane.getLayoutBounds().getWidth());

                        if (choices.equals("")) allVbox.getChildren().addAll(namepriceHbox);
                        else allVbox.getChildren().addAll(namepriceHbox, lblchoices);

                        double namepricewidth = listpane.getLayoutBounds().getWidth();
                        System.out.println("namepricewidth is "+String.valueOf(namepricewidth));

                        //allVbox.setPadding(new Insets(10,0,10,0));
                        setGraphic(allVbox);
                    }
                }

            };
            return cell;
        }
    });
    listpane.getChildren().add(selectedlist);
}