簡體   English   中英

JavaFX ScrollPane PickOnBounds(部分鼠標透明度)

[英]JavaFX ScrollPane PickOnBounds (partial mouse transparency)

我目前正在開發基於 JavaFX 的用於視頻編輯的 GUI 程序。 在這里,我遇到了ScrollPane的問題。 我不知道如何制作ScrollPane (真正的) pickOnBounds=„false“ 這意味着我實際上可以點擊ScrollPane到下面的Node s。 我認為這可能是ScrollPane的 Viewport 的問題。 我試圖在下面給出我們程序的大規模簡化版本:

樣式文件

.scroll-pane > .viewport {
    -fx-background-color: transparent;
    -fx-background-radius: 4;
}

示例文件

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.ScrollPane?>

<StackPane
  xmlns:fx="http://javafx.com/fxml"
  xmlns="http://javafx.com/javafx"
  fx:controller="sample.Controller" fx:id="myStackPane">
    <Pane onMousePressed="#onMouseInteraction" style="-fx-background-color: red"/>
    <AnchorPane fx:id="markerIconPane" pickOnBounds="false">
        <ScrollPane style="-fx-background-color: transparent" pickOnBounds="false" vbarPolicy="ALWAYS" hbarPolicy="ALWAYS"/>
    </AnchorPane>
</StackPane>

控制器.java

package sample;

import javafx.scene.input.MouseEvent;
import javafx.fxml.FXML;

public class Controller implements Initializable {
    //...
    public void onMouseInteraction(MouseEvent mouseEvent) {
        System.out.println(mouseEvent.getX());
    }
}

主程序

package sample;

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

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        root.getStylesheets().add("./Styles.css");
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

現在您可以清楚地看到左上角的滾動條。 當您按下滾動條外的紅色時, onMouseInteraction()會被調用,但是當您按下滾動條內的時,什么也沒有發生。

提前致謝
菲利普

假設Pane元素是您提到的視口; 您可以將onMouseInteraction處理程序作為 EventFilter 添加到堆棧窗格中,使其位於正確的事件鏈中。 例如

myStackPane.addEventFilter(Controller::onMouseInteraction) ;

這樣就不需要Pane作為額外的事件層,盡管缺點是您無法僅從 fxml 文件中添加 EventFilter。 但是,您可以在控制器的 init 方法中添加該語句作為替代。

如果您想在Pane可視化疊加層,您可以將其mouseTransparent屬性設置為 true。 這樣它就不會干擾事件(好像它不存在一樣)

如果您不能將 EventFilter 放入事件鏈中,則沒有真正好的解決方案。 在 javafx8 中,我通過使用修改后的PickResult傳播事件來“入侵”系統。 但是在以后的版本中,使用的(depricated)方法已成為私有的(即,沒有大量反射就無法使用)。


編輯

仔細閱讀你的評論后,我想我更了解這個問題了。 解決這樣的問題非常棘手,所以我創建了一個關於如何解決它的小測試。 我不確定它是否對你有用,因為我不知道 substructere 是如何布局/管理的。 所以我們開始:

這個想法是制作一個本身沒有大小的父節點,但只布局其子節點。 這樣它就不會阻止 eventPick,但仍然能夠在其他組件之上呈現組件(因為在 JavaFX 中默認情況下不啟用剪輯)。

這樣做的缺點是你需要自己照顧很多,因為父級的大小必須為“0”。 所以像滾動/子布局這樣的事情你需要自己做。

下面我創建了一個僅使用重寫的#layoutChildren方法的示例。 但在某些情況下(例如,如果您需要滾動),實現您自己的專門用於此任務的皮膚會很方便。 什么都適合您的需求。

public class Test extends Application
{

    public static void main(String[] args)
    {
        Application.launch(args);
    }
    
    
    
    @Override
    public void start(Stage primaryStage) throws Exception
    {
        Scene s = new Scene(this.createContent());
        primaryStage.setScene(s);
        primaryStage.show();
    }
    
    
    
    protected Parent createContent()
    {
        Pane forGround= this.createForGround();
        StackPane root = new StackPane(this.createBackground(), forGround)
        {
            @Override
            protected void layoutChildren()
            {
                // since the foreGround is not managed, you need to trigger the 'child-layout' manually by calling #requestLayout
                super.layoutChildren();
                forGround.requestLayout();
            }
        };
        
        return root;
    }
    
    protected Node createBackground()
    {
        GridPane pane = new GridPane();
        pane.setPadding(new Insets(5.0));
        pane.setVgap(5.0);
        pane.setHgap(5.0);
        
        for(int i = 0; i < 2; i++)
        {
            for(int j = 0; j < 2; j++)
            {
                Button b = new Button("test " + ((i * 2) + j));
                // some layout to make things testable
                GridPane.setHgrow(b, Priority.ALWAYS);
                GridPane.setVgrow(b, Priority.ALWAYS);
                GridPane.setFillHeight(b, true);
                GridPane.setFillWidth(b, true);
                GridPane.setHalignment(b, HPos.CENTER);
                GridPane.setValignment(b, VPos.CENTER);
                b.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
                // let us know when clicked
                b.setOnAction(E -> System.out.println(b.getText()));
                pane.add(b, i, j);
            }
        }
        
        return pane;
    }
    
    protected Pane createForGround()
    {
        Circle c1 = new Circle(20.0);
        c1.setFill(Color.BLUE);
        c1.setManaged(false);
        c1.setPickOnBounds(false);
        // let us know when clicked
        c1.setOnMouseClicked(E -> System.out.println("clicked blue"));
        
        Circle c2 = new Circle(20.0);
        c2.setFill(Color.RED);
        c2.setManaged(false);
        c2.setPickOnBounds(false);
        // let us know when clicked
        c2.setOnMouseClicked(E -> System.out.println("clicked red"));
        
        
        
        ////////////////////////////////////////////////////////////////
        // this is the old/current method
        ////////////////////////////////////////////////////////////////
        
        // VBox pane = new VBox();
        // pane.setAlignment(Pos.CENTER);
        // pane.setStyle("-fx-background-color:#AAAAAAAA");
        // pane.getChildren().addAll(c1, c2);
        
        
        ////////////////////////////////////////////////////////////////
        // this is the new/improved method
        ////////////////////////////////////////////////////////////////
        
        Pane pane = new Pane()
        {
            protected void layoutChildren()
            {
                // in here you need to layout the children yourself as the default method will not work due to this Pane being size '0'
                
                // get parent bounds instead of this Pane, as it has size '0'
                double pw = ((Region)this.getParent()).getWidth();
                double ph = ((Region)this.getParent()).getHeight();
                
                // resize
                c1.setRadius(ph / 4);
                c2.setRadius(ph / 4);
                
                c1.relocate(((pw - (ph / 2)) / 2), 0);
                c2.relocate(((pw - (ph / 2)) / 2), ph / 2);
            };
        };
        // make sure you set the pickOnBounds to 'false' on the pane as well, otherwise it will still create a(n event pickable)box around its children, even though it is not rendered!
        pane.setPickOnBounds(false);
        // set managed to false, so it will remain size 0
        pane.setManaged(false);
        // set style to make things visible in case of oddities
        pane.setStyle("-fx-background-color:#AAAAAAAA");
        pane.getChildren().addAll(c1, c2);
        
        return pane;
    }
}

同樣,我不知道這是否適用於您的情況(因為我錯過了實現細節)。 但是,無論您以哪種方式轉動它,我懷疑您將無法在不覆蓋默認機制或創建自己的父類型/皮膚的情況下實現這一目標。

我不知道這是否適合您,但您可以更改sample.fxml文件並將onMousePressed="#onMouseInteraction"ScrollPane

這是我提議更改的文件sample.fxml

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.ScrollPane?>

<StackPane
  xmlns:fx="http://javafx.com/fxml"
  xmlns="http://javafx.com/javafx"
  fx:controller="guitests.jfxtests.sample.Controller" fx:id="myStackPane">
    <Pane onMousePressed="#onMouseInteraction" style="-fx-background-color: red"/>
    <AnchorPane fx:id="markerIconPane" pickOnBounds="false">
        <ScrollPane onMousePressed="#onMouseInteraction" style="-fx-background-color: transparent" pickOnBounds="false" vbarPolicy="ALWAYS" hbarPolicy="ALWAYS"/>
    </AnchorPane>
</StackPane>

然后,當您在ScrollPane內部單擊時,將調用方法onMouseInteraction() ,就像您在Pane內部單擊時一樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM