我刚发现JavaFX,我真的很喜欢它。我讨厌java-default GUI,所以我立即决定个性化我的窗口。我有很多尝试,但我有一个很大的限制和一个大目标;局限性?我必须使用MVC模式。目的?使自定义窗口可重复使用。
所以......这就是我现在的观点: wstaw.org/m/2016/04/07/resoruces.png
我制作了一个包含App.java的通用包应用程序,它将启动应用程序。然后我制作另一个内部包,包含" MinimalWindow"逻辑,我需要的所有资源。
我实现了这个FXML代码来执行窗口:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.control.Label?>
<StackPane fx:id="minimalWindowShadowContainer" id="minimalWindowShadowContainer" stylesheets="@style.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" onMousePressed="#updateXY" onMouseDragged="#windowDragging" onMouseReleased="#updateStatus" >
<BorderPane fx:id="minimalWindowContainer" id="minimalWindowContainer">
<!-- This padding will create the dropshadow effect for the window behind -->
<padding>
<Insets top="5" right="5" bottom="5" left="5"/>
</padding>
<!-- "Title Bar" -->
<top>
<HBox id="titleBar" alignment="CENTER" spacing="5" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="30.0" prefWidth="600.0">
<padding>
<Insets top="5" right="5" bottom="5" left="5"/>
</padding>
<ImageView fx:id="logo" fitWidth="20" fitHeight="20"></ImageView>
<Label fx:id="lblTitle" id="title" text="MinimalWindow"></Label>
<Region HBox.hgrow="ALWAYS" prefHeight="30.0" prefWidth="200.0"></Region>
<HBox alignment="CENTER_RIGHT">
<Button id="btnMin" onMouseClicked="#minimizeApp" minHeight="20" minWidth="20" maxHeight="20" maxWidth="20"></Button>
<Button fx:id="btnMax" id="btnMax" onMouseClicked="#maximizeApp" minHeight="20" minWidth="20" maxHeight="20" maxWidth="20"></Button>
<Button id="btnCls" onMouseClicked="#closeApp" minHeight="20" minWidth="20" maxHeight="20" maxWidth="20"></Button>
</HBox>
</HBox>
</top>
<!-- The content of the window will go here -->
<center>
<StackPane fx:id="contentArea" id="contentArea"></StackPane>
</center>
<!-- Footer -->
<bottom>
<HBox id="footer">
<padding>
<Insets top="5" right="5" bottom="5" left="5"/>
</padding>
<Button fx:id="btnResize" id="btnResize" alignment="BOTTOM_RIGHT" onMouseClicked="#updateXY" onMouseEntered="#setMouseCursor" onMouseExited="#resetMouseCursor" onMouseDragged="#resizeWindow" minHeight="10" minWidth="10" maxHeight="10" maxWidth="10"></Button>
</HBox>
</bottom>
</BorderPane>
</StackPane>
我实现了控制器类:
package application.minimalWindow;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class MinimalWindow extends Application {
@FXML
Label lblTitle;
@FXML
Button btnMax, btnResize;
@FXML
StackPane minimalWindowShadowContainer, minimalWindowContainer,contentArea;
@FXML
Double SHADOW_SPACE;
final private static int MIN_WIDTH = 730, MIN_HEIGHT = 500;
private double actualX, actualY;
private boolean isMovable;
private String source, title;
private Stage mainStage;
//
// Public logic of the class
//
public MinimalWindow() {
//TODO must work...
}
//Show the window
public void show() {
mainStage.show();
}
//
// MIMIZIE | MAXIMIZE | CLOSE
//
//When pressed, will minimize the window to tray
@FXML
private void minimizeApp(MouseEvent e) {
mainStage.setIconified(true);
}
//When pressed, check if it must maximize or restore the window
@FXML
private void maximizeApp(MouseEvent e) {
if (mainStage.isMaximized()) {
setMin();
isMovable = true;
}
else {
setMax();
isMovable = false;
}
}
//When pressed, will kill the window
@FXML
private void closeApp(MouseEvent e) {
mainStage.close();
System.exit(0);
}
//
// WINDOW MOVING
//
//When i must update the XY of the click
@FXML
private void updateXY(MouseEvent e){
actualX = e.getScreenX() - mainStage.getX();
actualY = e.getScreenY() - mainStage.getY();
}
//When pressing and dragging the mouse it will move the window
@FXML
private void windowDragging(MouseEvent e) {
if (isMovable) {
mainStage.setX(e.getScreenX() - actualX);
mainStage.setY(e.getScreenY() - actualY);
}
else {
//setMin();
mainStage.setX(e.getScreenX());
mainStage.setY(e.getScreenY());
}
}
//Update the status of the window from not movable to movable, after "normalize" effect
//from the dragging it when it's maximized
@FXML
private void updateStatus(MouseEvent e) {
if (mainStage.isMaximized() == false) {
isMovable = true;
}
}
//
// WINDOW RESIZING
//
/*onMouseEntered="#setMouseCursor" onMouseExited="#resetMouseCursor" onMouseDragged="#resizeWindow"*/
@FXML
private void setMouseCursor (MouseEvent e) {
minimalWindowContainer.setCursor(Cursor.CROSSHAIR);
}
@FXML
private void resetMouseCursor (MouseEvent e) {
minimalWindowContainer.setCursor(Cursor.DEFAULT);
}
@FXML
private void resizeWindow (MouseEvent e) {
actualX = e.getScreenX() - mainStage.getX() + 13;
actualY = e.getScreenY() - mainStage.getY() + 10;
if (actualX % 5 == 0 || actualY % 5 == 0) {
if (actualX > MIN_WIDTH) {
mainStage.setWidth(actualX);
} else {
mainStage.setWidth(MIN_WIDTH);
}
if (actualY > MIN_HEIGHT) {
mainStage.setHeight(actualY);
} else {
mainStage.setHeight(MIN_HEIGHT);
}
}
}
//
// Internal methods
//
//Will set the window to MAXIMIZE size
private void setMax() {
mainStage.setMaximized(true);
btnResize.setVisible(false);
btnMax.setStyle("-fx-background-image: url('/res/dSquare.png');");
minimalWindowContainer.setPadding(new Insets(0, 0, 0, 0));
}
//Will set the window to NORMAL size
private void setMin() {
mainStage.setMaximized(false);
btnResize.setVisible(true);
btnMax.setStyle("-fx-background-image: url('/res/square.png');");
minimalWindowContainer.setPadding(new Insets(SHADOW_SPACE, SHADOW_SPACE, SHADOW_SPACE, SHADOW_SPACE));
}
@Override
public void start(Stage primaryStage) {
/* //NOT SURE IF DOING RIGHT YA'
try {
//Prepare the resource with the FXML file
FXMLLoader loader = new FXMLLoader(getClass().getResource("/application/minimalWindow/MainWindow.fxml"));
//Load the main stackpane
Parent root = loader.load();
loader.setController(this);
//Prepare the content of the window, with a minWidth/Height
Scene scene = new Scene(root, MIN_WIDTH, MIN_HEIGHT);
//Making the scene transparent
scene.setFill(Color.TRANSPARENT);
//Undecorate the window due its persolalisation
primaryStage.initStyle(StageStyle.TRANSPARENT);
//Set the content of the window
primaryStage.setScene(scene); *
}
catch (Exception e) {
e.printStackTrace();
} */
}
和造型的CSS:
* {
/* Some general colors */
primaryColor: #f9f9f9;
secondaryColor: derive(primaryColor, -75%);
textColor: white;
closeBtnColor: red;
}
#titleBar, #footer {
-fx-background-color: secondaryColor;
}
#title {
-fx-text-fill: textColor;
}
#contentArea {
-fx-background-color: primaryColor;
}
#minimalWindowShadowContainer {
-fx-background-color: transparent;
-fx-effect: dropshadow( gaussian , black , 5,0,0,0 );
-fx-background-insets: 5;
}
#btnCls, #btnMax, #btnMin, #btnResize {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-border-color: transparent;
-fx-border-width: 0;
-fx-background-position: center;
-fx-background-repeat: stretch;
}
#btnMax:hover, #btnMin:hover {
-fx-background-color: derive(secondaryColor, 20%);
}
#btnCls:hover {
-fx-background-color: derive(red, 45%);
}
#btnCls {
-fx-background-image: url('/res/x.png');
}
#btnMax {
-fx-background-image: url('/res/square.png');
}
#btnMin {
-fx-background-image: url('/res/line.png');
}
#btnResize {
-fx-background-image: url('/res/resize.png');
}
在App.java中,我应该像这样使用它:
public class App {
public static void main(String[] args) {
//Initialize the minimal window
MinimalWindow mainWindow = new MinimalWindow();
//Show the window, after all
mainWindow.show();
}
}
我在这里发布我的解决方案因为在互联网上我发现在MVC模式中没有关于自定义样式(是的......我需要为考试项目做这件事)。
有什么问题?它必须易于使用和可重复使用。试着像这样构建构造函数:
public MinimalWindow(String title, String source) {
this.title = title;
this.source = source;
start(mainStage);
}
它在解析11行(定义stackpanel的第一行)中的XAML文件时出错,或者给我一个错误&#34;引起:java.lang.IllegalStateException:Toolkit未初始化&#34;。 首先,我不知道是什么导致了它。对于第二种,互联网上的解决方案建议从应用程序扩展我的类,然后覆盖&#34;开始&#34;方法,但它没有奏效。
提问时间:任何解决方案?建议?
PS:我使用不同风格的非mvc模式编写此代码,并且效果很好:wstaw.org/m/2016/04/07/ezgif.com-crop.gif < / em>的
答案 0 :(得分:2)
Application
类代表整个应用程序。它不代表一个窗口。 JavaFX中的Windows由Stage
类表示。 Application.start()
方法是JavaFX应用程序的入口点(开始):您应该将其视为“常规”Java应用程序中main
的替代。 Application
子类实例是作为启动过程的一部分为您创建的,它也启动了FX工具包。在Oracle JDK中,可以通过调用Java运行时(例如,从命令行调用java
)并指定Application
子类作为要执行的类来启动启动过程。对于不支持直接启动JavaFX应用程序的环境,应包括调用main
的{{1}}方法,即
Application.launch(args)
因此
public class MyApp extends Application {
@Override
public void start(Stage primaryStage) {
// create objects and set up GUI, etc
}
public static void main(String[] args) {
launch(args);
}
}
子类本质上是不可重用的,你应该尽可能地保持Application
方法(它应该基本上什么也不做,但是,启动应用程序)。start(...)
子类的一个实例
Application
class as the controller class 所以要做你想做的事情,我想你想创建一个不是Application
子类的单独的MinimalWindow
类。使用FXML文档中描述的Custom Component模式使其加载自己的FXML并将其自身设置为控制器类。然后,您可以创建一个最小的主要类,扩展Application
方法创建并展示Application
的{{1}}。
答案 1 :(得分:0)
好的,我跟着从你那里学到的一切,我几乎完成了所有工作。那么,我现在拥有的是:
wstaw.org/m/2016/04/10/project.png
现在,我有MinimalWindow的FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.GridPane?>
<!-- Container that will do the "shadow" effect -->
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="BorderPane" fx:id="root" id="root" stylesheets="@MinimalWindowStyle.css" onMousePressed="#updateXY" onMouseDragged="#windowDragging" onMouseReleased="#updateStatus">
<center>
<!-- Main content -->
<BorderPane fx:id="mainWindow" id="mainWindow">
<!-- Padding will show the shadow effect under the window -->
<padding>
<Insets top="5" right="5" bottom="5" left="5"></Insets>
</padding>
<!-- Top bar of the window -->
<top>
<HBox id="titleBar" alignment="CENTER" spacing="5" prefHeight="30">
<padding>
<Insets top="5" right="5" bottom="5" left="5"/>
</padding>
<ImageView fx:id="logo" fitWidth="20" fitHeight="20"></ImageView>
<Label fx:id="lblTitle" id="title" text="MinimalWindow"></Label>
<Region HBox.hgrow="ALWAYS" prefHeight="30.0" prefWidth="200.0"></Region>
<HBox alignment="CENTER_RIGHT">
<Button id="btnMin" onMouseClicked="#minimizeApp" minHeight="20" minWidth="20" maxHeight="20" maxWidth="20"></Button>
<Button fx:id="btnMax" id="btnMax" onMouseClicked="#maximizeApp" minHeight="20" minWidth="20" maxHeight="20" maxWidth="20"></Button>
<Button id="btnCls" onMouseClicked="#closeApp" minHeight="20" minWidth="20" maxHeight="20" maxWidth="20"></Button>
</HBox>
</HBox>
</top>
<!-- Window content -->
<center>
<GridPane fx:id="contentArea" id="contentArea"></GridPane>
</center>
<!-- Footer of the window -->
<bottom>
<HBox id="footer" prefHeight="20" alignment="BOTTOM_RIGHT">
<padding>
<Insets top="5" right="5" bottom="5" left="5"/>
</padding>
<Button fx:id="btnResize" id="btnResize" onMouseClicked="#updateXY" onMouseEntered="#setMouseCursor" onMouseExited="#resetMouseCursor" onMouseDragged="#resizeWindow" minHeight="10" minWidth="10" maxHeight="10" maxWidth="10"></Button>
</HBox>
</bottom>
</BorderPane>
</center>
</fx:root>
它的风格:
* {
/* Some general colors */
primaryColor: #f9f9f9;
secondaryColor: derive(primaryColor, -75%);
textColor: white;
closeBtnColor: red;
}
#titleBar, #footer {
-fx-background-color: secondaryColor;
}
#title {
-fx-text-fill: textColor;
}
#contentArea {
-fx-background-color: primaryColor;
}
#root {
-fx-background-color: transparent;
-fx-effect: dropshadow( gaussian , black , 5,0,0,0 );
-fx-background-insets: 5;
}
#btnCls, #btnMax, #btnMin, #btnResize {
-fx-background-color: transparent;
-fx-background-radius: 0;
-fx-border-color: transparent;
-fx-border-width: 0;
-fx-background-position: center;
-fx-background-repeat: stretch;
}
#btnMax:hover, #btnMin:hover {
-fx-background-color: derive(secondaryColor, 20%);
}
#btnCls:hover {
-fx-background-color: derive(red, 45%);
}
#btnCls {
-fx-background-image: url("/resources/x.png");
}
#btnMax {
-fx-background-image: url('/resources/square.png');
}
#btnMin {
-fx-background-image: url('/resources/line.png');
}
#btnResize {
-fx-background-image: url('/resources/resize.png');
}
它的控制器类:
package controller.minimalWindow;
import application.Main;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class MinimalWindowCtrl extends BorderPane {
//Values injected from the FXML
@FXML
private BorderPane root, mainWindow;
@FXML
private Label lblTitle;
@FXML
private Button btnMax, btnResize;
@FXML
private GridPane contentArea;
//Reference to the primaryStage
final private Stage stage;
//References to min/max width/height and the shadow effect
final private int MINWIDTH, MINHEIGHT, SHADOWSPACE = 5;
//Things for the resizing/moving window
private double actualX, actualY;
private boolean isMovable = true;
public MinimalWindowCtrl (Stage stage, int minwidth, int minheight) {
//First, take the reference to the stage
this.stage = stage;
//Taking the references to the window
MINWIDTH = minwidth;
MINHEIGHT = minheight;
//Then load the window, setting the root and controller
FXMLLoader loader = new FXMLLoader(getClass().getResource("../../view/minimalWindow/MinimalWindow.fxml"));
loader.setRoot(this);
loader.setController(this);
//Try to load
try {
loader.load();
}
catch (Exception e) {
e.printStackTrace();
//TODO Show a message error
Main.close();
}
}
public void setTitle(String s) {
lblTitle.setText(s);
}
public void setContent(Node node) {
contentArea.getChildren().add(node);
}
//
// MIMIZIE | MAXIMIZE | CLOSE
//
//When pressed, will minimize the window to tray
@FXML
private void minimizeApp(MouseEvent e) {
stage.setIconified(true);
}
//When pressed, check if it must maximize or restore the window
@FXML
private void maximizeApp(MouseEvent e) {
if (stage.isMaximized()) {
setMin();
isMovable = true;
}
else {
setMax();
isMovable = false;
}
}
//When pressed, will kill the window
@FXML
private void closeApp(MouseEvent e) {
stage.close();
System.exit(0);
}
//
// WINDOW MOVING
//
//When i must update the XY of the click
@FXML
private void updateXY(MouseEvent e){
actualX = e.getScreenX() - stage.getX();
actualY = e.getScreenY() - stage.getY();
}
//When pressing and dragging the mouse it will move the window
@FXML
private void windowDragging(MouseEvent e) {
if (isMovable) {
stage.setX(e.getScreenX() - actualX);
stage.setY(e.getScreenY() - actualY);
}
else {
//setMin();
stage.setX(e.getScreenX());
stage.setY(e.getScreenY());
}
}
//Update the status of the window from not movable to movable, after "normalize" effect
//from the dragging it when it's maximized
@FXML
private void updateStatus(MouseEvent e) {
if (stage.isMaximized() == false) {
isMovable = true;
}
}
//
// WINDOW RESIZING
//
/*onMouseEntered="#setMouseCursor" onMouseExited="#resetMouseCursor" onMouseDragged="#resizeWindow"*/
@FXML
private void setMouseCursor (MouseEvent e) {
mainWindow.setCursor(Cursor.CROSSHAIR);
}
@FXML
private void resetMouseCursor (MouseEvent e) {
mainWindow.setCursor(Cursor.DEFAULT);
}
@FXML
private void resizeWindow (MouseEvent e) {
actualX = e.getScreenX() - stage.getX() + 13;
actualY = e.getScreenY() - stage.getY() + 10;
if (actualX % 5 == 0 || actualY % 5 == 0) {
if (actualX > MINWIDTH) {
stage.setWidth(actualX);
} else {
stage.setWidth(MINWIDTH);
}
if (actualY > MINHEIGHT) {
stage.setHeight(actualY);
} else {
stage.setHeight(MINHEIGHT);
}
}
}
//
// Internal methods
//
//Will set the window to MAXIMIZE size
private void setMax() {
stage.setMaximized(true);
btnResize.setVisible(false);
btnMax.setStyle("-fx-background-image: url('/res/dSquare.png');");
mainWindow.setPadding(new Insets(0, 0, 0, 0));
}
//Will set the window to NORMAL size
private void setMin() {
stage.setMaximized(false);
btnResize.setVisible(true);
btnMax.setStyle("-fx-background-image: url('/res/square.png');");
mainWindow.setPadding(new Insets(SHADOWSPACE, SHADOWSPACE, SHADOWSPACE, SHADOWSPACE));
}
}
在Main.java中我做了:
package application;
import controller.MainWindowCtrl;
import controller.minimalWindow.MinimalWindowCtrl;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.scene.Scene;
import javafx.scene.control.TabPane;
public class Main extends Application {
final private int MINWIDTH = 750, MINHEGIHT = 500;
@Override
public void start(Stage primaryStage) {
try {
//Preparing the model
//TODO the interface of model
Object m = new Object();
//Loading main content
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/MainWindow.fxml"));
TabPane mainPane = loader.load();
//Setting the model for the controller
((MainWindowCtrl) loader.getController()).setModel(m);
//Creating the style for the custom window
MinimalWindowCtrl minimalWindowCtrl = new MinimalWindowCtrl(primaryStage, MINWIDTH, MINHEGIHT);
minimalWindowCtrl.setContent(mainPane);
//Making new scene
Scene scene = new Scene(minimalWindowCtrl, MINWIDTH, MINHEGIHT);
//Setting the style to the window (undecorating it)
primaryStage.initStyle(StageStyle.TRANSPARENT);
//Setting the scene on the window
primaryStage.setScene(scene);
//Showing the window
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public static void close() {
System.exit(0);
}
}
它缺少一些功能,例如“我不知道为什么按钮的图标没有显示”,阴影仍然是错误的,但它通常有效。
结果如下: