Java 控件Fx:确保弹出箭头始终指向正确的点

Java 控件Fx:确保弹出箭头始终指向正确的点,java,javafx,javafx-8,popover,controlsfx,Java,Javafx,Javafx 8,Popover,Controlsfx,我正在使用ControlsFX中的PopOver,在表格视图中如果我触发单元格的启动它,它应该弹出PopOver。这部分是有效的,问题是,指向行的箭头每次都不在正确的位置。如果我从表格底部的表格中选择一行,它将指向其上方的单元格 我需要该箭头每次都指向表视图中的右侧单元格 ,版本:8.40.14 我怎样才能解决这个问题 下面是代码,您可以在其中看到它的工作原理: package stackoverflow.popover; import com.sun.deploy.util.StringUt

我正在使用ControlsFX中的
PopOver
,在
表格视图中
如果我触发单元格的
启动它
,它应该弹出
PopOver
。这部分是有效的,问题是,指向行的箭头每次都不在正确的位置。如果我从表格底部的表格中选择一行,它将指向其上方的单元格

我需要该箭头每次都指向
表视图中的右侧单元格

,版本:8.40.14

我怎样才能解决这个问题

下面是代码,您可以在其中看到它的工作原理:

package stackoverflow.popover;

import com.sun.deploy.util.StringUtils;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.controlsfx.control.PopOver;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    @FXML
    private TableView<Model> table;
    @FXML
    private TableColumn<Model, ObservableList<String>> listCell;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Model model = new Model(FXCollections.observableArrayList("Apple", "Peach"));

        ObservableList<Model> items = FXCollections.observableArrayList();
        for (int i = 0; i < 50; i++) {
            items.add(model);
        }

        table.setItems(items);
        table.setEditable(true);
        listCell.setCellFactory(factory -> new ListTableCell(
                FXCollections.observableArrayList("Apple", "Orange", "Peach", "Banana", "Lemon", "Lime")));
        listCell.setCellValueFactory(data -> data.getValue().list);
    }

    private class ListTableCell extends TableCell<Model, ObservableList<String>> {

        private ObservableList<String> allItems;

        ListTableCell(ObservableList<String> allItems) {
            this.allItems = allItems;
        }

        @Override
        public void startEdit() {
            super.startEdit();
            PopOver popOver = new PopOver();
            popOver.setAutoHide(true);
            PopupController sc = new PopupController(allItems, new ArrayList<>(getItem()));
            popOver.setContentNode(new StackPane(sc.getPane()));
            popOver.setOnHiding(event -> commitEdit(sc.getItems()));
            popOver.show(this);
        }

        @Override
        protected void updateItem(ObservableList<String> item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
            } else {
                setText(StringUtils.join(item, ","));
            }
        }
    }

    private class Model {

        ListProperty<String> list;

        public Model(ObservableList<String> list) {
            this.list = new SimpleListProperty<>(list);
        }
    }

    private class PopupController {

        private BorderPane pane = new BorderPane();

        private ListView<String> left = new ListView<>();
        private ListView<String> right = new ListView<>();

        private Button toLeft = new Button("<");
        private Button toRight = new Button(">");

        PopupController(List<String> all, List<String> selected) {

            VBox leftBox = new VBox();
            leftBox.setSpacing(5);
            leftBox.getChildren().add(toRight);
            leftBox.getChildren().add(left);
            pane.setLeft(leftBox);

            VBox rightBox = new VBox();
            rightBox.setSpacing(5);
            rightBox.getChildren().add(toLeft);
            rightBox.getChildren().add(right);
            pane.setRight(rightBox);

            ObservableList<String> allItems = FXCollections.observableArrayList(all);
            allItems.removeAll(selected);

            left.setItems(allItems);
            right.setItems(FXCollections.observableArrayList(selected));

            toLeft.disableProperty().bind(right.getSelectionModel().selectedItemProperty().isNull());
            toRight.disableProperty().bind(left.getSelectionModel().selectedItemProperty().isNull());

            toLeft.setOnAction(event -> {
                String str = right.getSelectionModel().getSelectedItem();
                right.getItems().remove(str);
                left.getItems().add(str);
            });

            toRight.setOnAction(event -> {
                String str = left.getSelectionModel().getSelectedItem();
                left.getItems().remove(str);
                right.getItems().add(str);
            });
        }

        BorderPane getPane() {
            return pane;
        }

        ObservableList<String> getItems() {
            return right.getItems();
        }
    }

}
package stackoverflow.popover;
导入com.sun.deploy.util.StringUtils;
导入javafx.beans.property.ListProperty;
导入javafx.beans.property.SimpleListProperty;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.fxml.fxml;
导入javafx.fxml.Initializable;
导入javafx.scene.control.Button;
导入javafx.scene.control.ListView;
导入javafx.scene.control.TableCell;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TableView;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.StackPane;
导入javafx.scene.layout.VBox;
导入org.controlsfx.control.PopOver;
导入java.net.URL;
导入java.util.ArrayList;
导入java.util.List;
导入java.util.ResourceBundle;
公共类控制器实现可初始化{
@FXML
私有表视图表;
@FXML
私有表列列表单元;
@凌驾
公共void初始化(URL位置、ResourceBundle资源){
模型=新模型(FXCollections.observearraylist(“苹果”、“桃子”));
ObservableList items=FXCollections.observableArrayList();
对于(int i=0;i<50;i++){
增加(型号);
}
表2.设置项目(项目);
table.setEditable(true);
setCellFactory(工厂->新建ListTableCell(
FXCollections.observableArrayList(“苹果”、“橘子”、“桃子”、“香蕉”、“柠檬”、“酸橙”));
setCellValueFactory(数据->数据.getValue().list);
}
私有类ListTableCell扩展了TableCell{
私人观察者联盟;
ListTableCell(可观察列表allItems){
this.allItems=allItems;
}
@凌驾
公开作废已启动IT(){
super.startEdit();
PopOver PopOver=新PopOver();
设置自动隐藏(true);
PopupController sc=新的PopupController(allItems,新的ArrayList(getItem());
setContentNode(新的堆栈窗格(sc.getPane());
setOnHiding(事件->提交(sc.getItems());
popOver.show(这个);
}
@凌驾
受保护的void updateItem(可观察列表项,布尔值为空){
super.updateItem(项,空);
if(空){
setText(空);
}否则{
setText(StringUtils.join(项目“,”);
}
}
}
私有类模型{
列表属性列表;
公共模型(可观察列表){
this.list=新的SimpleListProperty(列表);
}
}
私有类PopupController{
私有边框窗格=新建边框窗格();
private ListView left=新建ListView();
private ListView right=新建ListView();
私有按钮toLeft=新按钮(“”);
PopupController(列出所有,列出所选){
VBox leftBox=新的VBox();
leftBox.setspace(5);
leftBox.getChildren().add(toRight);
leftBox.getChildren().add(左);
窗格。设置左(左框);
VBox rightBox=新的VBox();
右框。设置间距(5);
rightBox.getChildren().add(toLeft);
rightBox.getChildren().add(右);
窗格。设置权限(右框);
ObservableList allItems=FXCollections.observableArrayList(全部);
allItems.removeAll(选定);
左。设置项(所有项);
右.setItems(FXCollections.observableArrayList(选中));
toLeft.disableProperty().bind(right.getSelectionModel().selectedItemProperty().isNull());
toRight.disableProperty().bind(left.getSelectionModel().SelectEditeProperty().isNull());
toLeft.setOnAction(事件->{
字符串str=right.getSelectionModel().getSelectedItem();
right.getItems().remove(str);
left.getItems().add(str);
});
toRight.setOnAction(事件->{
字符串str=left.getSelectionModel().getSelectedItem();
left.getItems().remove(str);
right.getItems().add(str);
});
}
BorderPane getPane(){
返回窗格;
}
ObservableList getItems(){
return right.getItems();
}
}
}
这里有两个截图来说明我的意思:

这甚至是最糟糕的:(使用
setAutoFix(false)
) 尝试在PopOver实例上设置setAutoFix(false)。从PopOver的超类PopupWindow的autoFix属性文档中:

此方便变量指示在显示弹出窗口时, 它应该自动更正其位置,使其不会结束 在屏幕外向上定位


我不是ControlFX的专家,但我相信您面临的问题是,因为您的PopOver的高度大于您当前的屏幕大小,因此它试图以一种方式重新定位自己,使其位于屏幕本地边界内。因此,为了实现您正在尝试的功能,您需要手动设置PopOver控件的箭头位置。以下是如何解决问题(使用代码):

@覆盖
公开作废已启动IT(){
super.startEdit();
PopOver PopOver=新PopOver();
po
    @Override
    public void startEdit() {
        super.startEdit();
        PopOver popOver = new PopOver();
        popOver.setAutoHide(true);
        // first set auto fix to false 
        // to manually set the arrow location
        popOver.setAutoFix(false);   
        PopupController sc = new PopupController(allItems, new ArrayList<>(getItem()));

        // set a specific height for our pane
        final double paneHeight = 300;

        StackPane popOverPane = new StackPane(sc.getPane());
        popOverPane.setPrefHeight(paneHeight);

        popOver.setContentNode(popOverPane);
        popOver.setOnHiding(event -> commitEdit(sc.getItems()));

        // find coordinates relative to the screen
        Bounds screenBounds = this.localToScreen(this.getBoundsInLocal());

        // get our current y position ( on screen )
        int yPos = (int) screenBounds.getMinY();

        // get screen size 
        Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
        int screenHeight = (int) primaryScreenBounds.getHeight();

        // if the PopOver height + the current position is greater than
        // the max screen's height then set the arrow position to bottom left
        if(screenHeight < yPos + paneHeight) {
            popOver.setArrowLocation(ArrowLocation.LEFT_BOTTOM);
        }

        popOver.show(this);
    }