目录
Kotlin+JavaFX系列
- 使用Kotlin开发JavaFX
- Windows下的JavaFX桌面应用程序打包ABC
- Kotlin编写JavaFX的顺滑
- JavaFX动画:有趣的AnimationTimer
- JavaFX应用程序图标
- JavaFX专业开发者与业余开发者之间就差一个一个Icon packs
- JavaFX七巧板游戏:布局窗格Panes
- JavaFX七巧板游戏:布局控件
- JavaFX+Kotlin游戏从入门到放弃:拯救蛇蛇大作战又名454行实现几何数独游戏
- Kotlin编写JavaFX的顺滑之数据控件(一)列表视图ListView
- Kotlin编写JavaFX的顺滑之数据控件(二)表视图TableView基础应用
- Kotlin编写JavaFX的顺滑之数据控件(二)表视图TableView基础深入浅出
JavaFX布局之各种Pane
s
上次写博客还是上次。
自开始把这两东西一起学Kotlin+JavaFX的顺滑已经过去差不多5个月,联盟……都是废物。我已经编了大概3个小的应用软件,申请了两个软件著作权(一个图标库的实在无聊),最近又把专业方向的一个软件实现出来了。
我整个的学习方式都是捡到什么用什么,在GUI布局方面用得最熟的是三个:
BorderPane
VBox
HBox
差不多我已经觉得自己天下无敌,biu~~~【全文完】
这几天闲下来,感觉还是干点活。于是把JavaFX界面布局的核心类Pane
的类图大概拉了一下。
除了我唯一学会的三板斧,还有一堆内容。那就开动吧,把源码和文档整理一下。看看下一步是不是能显得更专业一点。
Pane
从词源上看,Pane
是窗户中的一块玻璃。在JavaFX中,Pane
是一个Region
(的子类),也就是代表窗口的一个区域,这个区域负责其children
的定位(layout
)。
所以的子类都暴露children
属性(Java的getChildren
方法,下面只采用Kotlin的,不再提醒对应的Java含义),并实现子节点的定位。当然,children
相关的实现实际上在Parent
类中。
Pane
类可以直接使用,这时候,就需要自行设置其子节点的位置。
class PaneLearningApplication : Application() {
override fun start(primaryStage: Stage) {
val canvas = Pane().apply {
// background: 展示Pane的区域
style = "-fx-background-color: black;"
setPrefSize(200.0, 200.0)
children.addAll(Circle(50.0, Color.BLUE).apply {
// 蓝色的50半径的圆形
// 设置layoutX, layoutY,translateX和translateY不变
relocate(20.0, 20.0)
}, Rectangle(100.0, 100.0, Color.RED).apply {
// 红色的正方形,边长100.0
// 设置layoutX, layoutY,translateX和translateY不变
relocate(70.0, 70.0)
})
}
primaryStage.scene = Scene(canvas, 800.0, 600.0)
primaryStage.show()
}
}
fun main() {
Application.launch(PaneLearningApplication::class.java)
}
这例子的结果是:
两个Node
的左上角位置分别为[20,20],[70,70]。此时如果缩放窗口,Pane
的内容不会随之缩放,可能超出实际显示范围。
总计以下,Pane
和其子类:
- 是一个区域(
Region
),负责窗口一个区域的图形展示,上面的例子,专门取名叫canvas
- 也是一个父节点(
Parent
),可以为其children
增加节点,并且提供管理子节点的功能 - 主要职责是布局,因此一些叫做Pane但是有自己的可视化元素的实际上是控件,并不是布局(后面专门写一篇)
为了后面的演示方便,按照kotlin的惯例,我们定义一个函数:
fun getPane(color: String) = Pane().apply {
// background: 展示Pane的区域,参数color为背景色
style = "-fx-background-color: $color;"
setPrefSize(200.0, 200.0)
children.addAll(Circle(50.0, Color.BLUE).apply {
// 蓝色的50半径的圆形
// 设置layoutX, layoutY,translateX和translateY不变
relocate(20.0, 20.0)
}, Rectangle(100.0, 100.0, Color.RED).apply {
// 红色的正方形,边长100.0
// 设置layoutX, layoutY,translateX和translateY不变
relocate(70.0, 70.0)
})
}
这个函数返回一个Pane
,函数有一个参数设置背景颜色。Pane
的建议大小为200×200
,绘制一个圆一个正方形。
布局类的用法和展示
BorderPane
先从我最喜欢的开始。
fun borderPane() = BorderPane().apply {
top = getPane("black")
bottom = getPane("green")
left = getPane("yellow")
right = getPane("pink")
center = getPane("cyan")
}
class PaneLearningApplication : Application() {
override fun start(primaryStage: Stage) {
primaryStage.scene = Scene(
borderPane(),
800.0, 600.0
)
primaryStage.show()
}
}
其他部分与前面相同,Stage
的Scene
的根节点(类型是Parent
)是一个BorderPane
。最终5个颜色分开的5个区域展示如下图,注意五个Pane
的布局,这样基本上就可以定义一般常见应用程序的窗口布置,最上方(黑色部分)通常布置菜单和工具按钮,左边(黄色)通常布置一个树状或者列表来选择操作对象,右边(粉红色)通常显示一个信息,最下方(绿色)输出状态信息,我喜欢弄一个类似于终端输出的东西,最中间(cyan部分)就是主题操作部分。BorderPane
的布局还有很友好的一点,就是默认的空间都给center
,缩小放大,上下两个部分的高度,左右两个部分的宽度均不发生变化。
HBox
和VBox
fun hbox() = HBox( //HBox和VBox可以互换
getPane("green"),
getPane("yellow"),
getPane("cyan"),
getPane("black")
)
class PaneLearningApplication : Application() {
override fun start(primaryStage: Stage) {
primaryStage.scene = Scene(
hbox(),
800.0, 600.0
)
primaryStage.show()
}
}
这两个布局都非常简单,HBox
是一行,VBox
是一列。从VBox
的例子里也可以看到,作为父类的Pane
基本啥都不干,children
跑到外面,他也不调整人家的尺寸和位置……
基本上,会这三个就能完成大部分的布局工作。
GridPane
网格布局,这个照说是非常强大,我们来整个例子看看。
fun gridPane() = GridPane().apply {
// 子节点, 0基列, 0基行, 宽度(列),高度(行)
add(getPane("gray"), 0, 0, 3, 1)
add(getPane("black"), 0, 1, 1, 1)
add(getPane("yellow"), 1,1,1, 3)
add(getPane("black"), 2, 1, 1, 1)
add(getPane("green"),0, 2,1,1)
add(getPane("green"), 2, 2,1,1)
add(getPane("black"), 0, 3, 1, 1)
add(getPane("black"), 2, 3, 1, 1)
add(getPane("cyan"), 0,4,3,1)
}
GridPane
不限制子节点的相互覆盖,把后添加的放在前面。
TilePane
, FlowPane
这两个布局就是贴砖块,后者有横向和纵向两个贴的顺序。
val panes = arrayOf(
getPane("black"),
getPane("green"),
getPane("cyan"),
getPane("yellow"),
getPane("gray"),
getPane("white"),
getPane("pink"),
getPane("purple")
)
fun tilePane() = TilePane(*panes) // 贴砖
fun hflowPane() = FlowPane(*panes).apply { // 横向贴砖
orientation = Orientation.HORIZONTAL
}
fun vflowPane() = FlowPane(*panes).apply { //纵向贴砖 orientation = Orientation.VERTICAL
}
TilePane
:缩放窗口时,与横向贴砖的FlowPane
很类似,但是每个子节点保持相同尺寸。
FlowPane
两种布局的不同之处,把窗口缩放一下就很清楚。
AnchorPane
和 StackPane
这两个Pane
没有什么好演示的。前者,设定子节点的锚点,后者把子节点一层一层堆放。
我们把前面的panes
映射一下,设置新顶部和左侧的锚点位移。
fun anchorPane() = AnchorPane().apply {
val mps = panes.mapIndexed{i, p ->
setTopAnchor(p, i * 50.0)
setLeftAnchor(p, i * 50.0)
p
}.toTypedArray()
children.addAll(*mps)
}
这里,我们为了区分,把每个Pane
的最大尺寸做映射。从图中很容易看到堆叠的效果。
fun stackPane() = StackPane().apply {
val n = panes.size
val mps = panes.mapIndexed{i, p ->
(p.apply {
val k = n + 1 - i
val d = prefWidth * pow(1.12, k.toDouble())
setMaxSize(d, d)
})
}.toTypedArray()
children.addAll(*mps)
}
结论
Pane
从含义上看是布局的窗格;- Java FX已经定义的布局窗格可以构成复杂的GUI;
还有一个控件Control
实际上也有布局的作用,而且过分的是,还有一些名称里包含了Pane这个,比如TitledPane
,ScrollPane
这些的。实际上这里的语义并不完整。
此外,Pane
还有一个子类TextFlow
,那个是为了构成文本流,在GUI编程中并不作为布局来使用。