Внутренние рамки в JavaFX

Я нашел этот пример внутренних рамок

http://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html

Можно ли сделать такие же внутренние фреймы в JavaFX?

2 ответа

В JFXtras есть элемент управления Window, где вы можете добавлять контент и обрабатывать поведение внутреннего окна.

Сначала вам нужно будет поместить в ваш classpath библиотеку jfxtras. У них есть некоторые инструкции, где вы можете получить библиотеку. Если вы используете Maven, просто нужно добавить:

<dependency>
    <groupId>org.jfxtras</groupId>
    <artifactId>jfxtras-labs</artifactId>
    <version>2.2-r5</version>
</dependency>

Или загрузите библиотеку и поместите ее в classpath вашего проекта, что угодно.

Теперь я поместил пример демо-версии Window с небольшой разницей, позволяющей создавать несколько окон.

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import jfxtras.labs.scene.control.window.CloseIcon;
import jfxtras.labs.scene.control.window.MinimizeIcon;
    import jfxtras.labs.scene.control.window.Window;


public class WindowTests extends Application {
private static int counter = 1;

private void init(Stage primaryStage) {
    final Group root = new Group();

    Button button = new Button("Add more windows");     

    root.getChildren().addAll(button);
    primaryStage.setResizable(false);
    primaryStage.setScene(new Scene(root, 600, 500));

    button.setOnAction(new EventHandler<ActionEvent>() {            
        @Override
        public void handle(ActionEvent arg0) {
            // create a window with title "My Window"
            Window w = new Window("My Window#"+counter);
            // set the window position to 10,10 (coordinates inside canvas)
            w.setLayoutX(10);
            w.setLayoutY(10);
            // define the initial window size
            w.setPrefSize(300, 200);
            // either to the left
            w.getLeftIcons().add(new CloseIcon(w));
            // .. or to the right
            w.getRightIcons().add(new MinimizeIcon(w));
            // add some content
            w.getContentPane().getChildren().add(new Label("Content... \nof the window#"+counter++));
            // add the window to the canvas
            root.getChildren().add(w);  
        }
    });
}

public double getSampleWidth() {return 600;}
public double getSampleHeight() {return 500;}

@Override
public void start(Stage primaryStage) throws Exception {
    init(primaryStage);
    primaryStage.show();


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

В оригинальной демонстрации код события был в методе init, и никакая кнопка не была включена. Я добавляю кнопку для динамического создания окон и добавления их на экран.

Вот снимок результата приложения:

снимок

Я полностью рекомендую вам попробовать демо jfxtras. У них действительно классные вещи. Надеюсь, поможет.

Вы можете сами реализовать простое внутреннее окно. Основная идея, что InternalWindow Класс просто скелет, который имеет внутреннюю структуру, как функциональность. Вы можете применить любой контент к нему.

1) Объявить класс

public class InternalWindow extends Region

2) Вы должны быть в состоянии установить контент в окне

public void setRoot(Node node) {
        getChildren().add(node);
}

3) Вы должны быть в состоянии вывести окно на передний план, если существует много окон

public void makeFocusable() {    
        this.setOnMouseClicked(mouseEvent -> {
            toFront();
        });    
}

4) Теперь нам нужно перетащить функциональность

//just for encapsulation
private static class Delta {
    double x, y;
}

//we can select nodes that react drag event
public void makeDragable(Node what) {
    final Delta dragDelta = new Delta();
    what.setOnMousePressed(mouseEvent -> {
        dragDelta.x = getLayoutX() - mouseEvent.getScreenX();
        dragDelta.y = getLayoutY() - mouseEvent.getScreenY();
        //also bring to front when moving
        toFront();
    });
    what.setOnMouseDragged(mouseEvent -> {
        setLayoutX(mouseEvent.getScreenX() + dragDelta.x);
        setLayoutY(mouseEvent.getScreenY() + dragDelta.y);
    });
}

5) Также мы хотим иметь возможность изменить размер окна (я показываю только простое изменение размера справа внизу)

//current state
private boolean RESIZE_BOTTOM;
private boolean RESIZE_RIGHT;

public void makeResizable(double mouseBorderWidth) {
    this.setOnMouseMoved(mouseEvent -> {
        //local window's coordiantes
        double mouseX = mouseEvent.getX();
        double mouseY = mouseEvent.getY();
        //window size
        double width = this.boundsInLocalProperty().get().getWidth();
        double height = this.boundsInLocalProperty().get().getHeight();
        //if we on the edge, change state and cursor
        if (Math.abs(mouseX - width) < mouseBorderWidth
                && Math.abs(mouseY - height) < mouseBorderWidth) {
            RESIZE_RIGHT = true;
            RESIZE_BOTTOM = true;
            this.setCursor(Cursor.NW_RESIZE);
        } else {
            RESIZE_BOTTOM = false;
            RESIZE_RIGHT = false;
            this.setCursor(Cursor.DEFAULT);
        }

    });
    this.setOnMouseDragged(mouseEvent -> {
        //resize root
        Region region = (Region) getChildren().get(0);
        //resize logic depends on state
        if (RESIZE_BOTTOM && RESIZE_RIGHT) {
            region.setPrefSize(mouseEvent.getX(), mouseEvent.getY());
        } else if (RESIZE_RIGHT) {
            region.setPrefWidth(mouseEvent.getX());
        } else if (RESIZE_BOTTOM) {
            region.setPrefHeight(mouseEvent.getY());
        }
    });
}

6) Использование. Сначала мы строим все макет. Затем примените его к InternalWindow.

private InternalWindow constructWindow() {
    // content
    ImageView imageView = new ImageView("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Cheetah4.jpg/250px-Cheetah4.jpg");
    // title bar
    BorderPane titleBar = new BorderPane();
    titleBar.setStyle("-fx-background-color: green; -fx-padding: 3");
    Label label = new Label("header");
    titleBar.setLeft(label);
    Button closeButton = new Button("x");
    titleBar.setRight(closeButton);
    // title bat + content
    BorderPane windowPane = new BorderPane();
    windowPane.setStyle("-fx-border-width: 1; -fx-border-color: black");
    windowPane.setTop(titleBar);
    windowPane.setCenter(imageView);

    //apply layout to InternalWindow
    InternalWindow interalWindow = new InternalWindow();
    interalWindow.setRoot(windowPane);
    //drag only by title
    interalWindow.makeDragable(titleBar);
    interalWindow.makeDragable(label);
    interalWindow.makeResizable(20);
    interalWindow.makeFocusable();
    return interalWindow;
}

7) А как добавить окно в макет

@Override
public void start(Stage primaryStage) throws Exception {
    Pane root = new Pane();
    root.getChildren().add(constructWindow());
    root.getChildren().add(constructWindow());
    primaryStage.setScene(new Scene(root, 300, 275));
    primaryStage.show();
}

Результат

Полный код: гист

Upd про кнопку закрытия:

Вы можете добавить метод к InternalWindow

public void setCloseButton(Button btn) {
    btn.setOnAction(event -> ((Pane) getParent()).getChildren().remove(this));
}

И когда построить:

interalWindow.setCloseButton(closeButton);
Другие вопросы по тегам