TornadoFX編程指南霉晕,第7章庭再,布局和菜單

譯自《Layouts and Menus

布局和菜單

復(fù)雜的UI需要很多控件捞奕。 這些控件可能需要使用設(shè)置策略(set policies),進(jìn)行分組拄轻,定位并調(diào)整大小颅围。 幸運(yùn)的是,TornadoFX簡(jiǎn)化了JavaFX自帶的許多布局(layouts)恨搓,并且具有自己的專有Form布局院促。

TornadoFX還具有類型安全的構(gòu)建器(type-safe builders),以高度結(jié)構(gòu)化斧抱,聲明性的方式創(chuàng)建菜單常拓。 使用常規(guī)JavaFX代碼構(gòu)建菜單尤其繁瑣,而Kotlin在這個(gè)部分真的很出色辉浦。

布局構(gòu)建器(Builders for Layouts)

布局(Layouts)將控制分組弄抬,并設(shè)置有關(guān)其大小和定位行為的策略(policies)。 在技??術(shù)上宪郊,布局(layouts)本身就是控件掂恕,因此您可以在布局中嵌套布局。 這對(duì)于構(gòu)建復(fù)雜的UI來(lái)說(shuō)至關(guān)重要弛槐,而TornadoFX可以通過(guò)明顯地顯示嵌套關(guān)系來(lái)簡(jiǎn)化UI代碼的維護(hù)懊亡。

VBox

VBox按照控件在其塊中聲明的順序垂直堆疊控件(圖7.1)。

vbox {
    button("Button 1").setOnAction {
        println("Button 1 Pressed")
    }
    button("Button 2").setOnAction {
        println("Button 2 Pressed")
    }
}
圖7.1

您還可以在子控件的塊中調(diào)用vboxConstraints()來(lái)更改VBox的邊距(margin)和垂直增長(zhǎng)(vertical growing)行為乎串。

vbox {
    button("Button 1") {
         vboxConstraints {
            marginBottom = 20.0
            vGrow = Priority.ALWAYS
          }
    }
    button("Button 2")
}

您可以用vGrow速記擴(kuò)展屬性(shorthand extension property)店枣,而無(wú)需調(diào)用vboxConstraints()

vbox {
    button("Button 1") {
           vGrow = Priority.ALWAYS
    }
    button("Button 2")
}

HBox

HBox行為幾乎與VBox相同叹誉,但是按照其塊中聲明的順序從左到右水平堆疊所有控件艰争。

hbox {
    button("Button 1").setOnAction {
        println("Button 1 Pressed")
    }
    button("Button 2").setOnAction {
        println("Button 2 Pressed")
    }
}
圖7.2

您還可以在子控件的塊內(nèi)調(diào)用hboxConstraints()來(lái)更改HBox的邊距(margin)和橫向增長(zhǎng)(horizontal growing behaviors)行為。

hbox {
    button("Button 1") {
        hboxConstraints {
                marginRight = 20.0
          hGrow = Priority.ALWAYS
      }
    }
    button("Button 2")
}

您可以使用hGrow縮寫(xiě)擴(kuò)展屬性(shorthand extension property)桂对,而不調(diào)用hboxConstraints() 甩卓。

hbox {
    button("Button 1") {
          hGrow = Priority.ALWAYS
    }
  button("Button 2")
}

FlowPane

FlowPane控件從左至右布局控件,并在到達(dá)邊界時(shí)將其轉(zhuǎn)到下一行蕉斜。 例如逾柿,假設(shè)您添加了100個(gè)按鈕到FlowPane (圖7.3)缀棍。你會(huì)注意到它只是從左到右布置按鈕,當(dāng)它耗盡空間時(shí)机错,它移動(dòng)到“下一行”爬范。

flowpane {
   for (i in 1..100) {
        button(i.toString()) {
            setOnAction { println("You pressed button $i") }
        }
   }
}
圖7.3

請(qǐng)注意,當(dāng)您調(diào)整窗口大小時(shí)弱匪, FlowLayout將重新布局按鈕青瀑,以使它們都可以適合(圖7.4)

圖7.4

FlowLayout不經(jīng)常使用,因?yàn)樘幚泶罅靠丶ǔJ呛?jiǎn)單的萧诫,但它可以在某些情況下派上用場(chǎng)斥难,也可以在其他布局中使用。

BorderPane

BorderPane是一個(gè)非常有用的布局帘饶,將控件分為5個(gè)區(qū)域: top 哑诊, leftbottom 及刻, rightcenter 镀裤。 可以使用這些區(qū)域的兩個(gè)或更多來(lái)來(lái)保存控件,很容易地構(gòu)建許多UI(圖7.5)缴饭。

borderpane {
    top = label("TOP") {
        useMaxWidth = true
        style {
            backgroundColor = Color.RED
        }
    }

    bottom = label("BOTTOM") {
        useMaxWidth = true
        style {
            backgroundColor = Color.BLUE
        }
    }

    left = label("LEFT") {
        useMaxWidth = true
        style {
            backgroundColor = Color.GREEN
        }
    }

    right = label("RIGHT") {
        useMaxWidth = true
        style {
            backgroundColor = Color.PURPLE
        }
    }

    center = label("CENTER") {
        useMaxWidth = true
        style {
            backgroundColor = Color.YELLOW
        }
    }
}
圖7.5

您會(huì)注意到topbottom區(qū)域占據(jù)整個(gè)水平空間暑劝,而leftcenter 颗搂, right必須共享可用的水平空間担猛。 但center有權(quán)獲得任何額外的可用空間(垂直和水平),使其成為像TableView這樣的大型控件的理想選擇峭火。 例如毁习,您可以在left區(qū)域中垂直堆疊一些按鈕,并將TableView放在center區(qū)域(圖7.6)卖丸。

borderpane {
    left = vbox {
        button("REFRESH")
        button("COMMIT")
    }

    center  = tableview<Person> {
        items = listOf(
                Person("Joe Thompson", 33),
                Person("Sam Smith", 29),
                Person("Nancy Reams", 41)
        ).observable()

        column("NAME",Person::name)
        column("AGE",Person::age)
    }
}
圖7.6

BorderPane是您可能想要經(jīng)常使用的布局纺且,因?yàn)樗?jiǎn)化了許多復(fù)雜的UI。top區(qū)域通常用于保存MenuBar 稍浆, bottom區(qū)域通常保持某種狀態(tài)欄载碌。 您已經(jīng)看到center保持焦點(diǎn)控制,如TableView 衅枫, leftright保持側(cè)面板與任何不適合放在MenuBar中的外圍控件(如按鈕或工具欄) 嫁艇。 本節(jié)稍后將介紹菜單。

表單生成器

TornadoFX有一個(gè)有用的Form控件來(lái)處理大量的用戶輸入弦撩。 擁有多個(gè)輸入字段以獲取用戶信息是常見(jiàn)的步咪,JavaFX沒(méi)有內(nèi)置的解決方案來(lái)簡(jiǎn)化此操作。 為了解決這個(gè)問(wèn)題益楼,TornadoFX有一個(gè)構(gòu)建器來(lái)聲明具有任意數(shù)量字段的Form (圖7.7)猾漫。

form {
    fieldset("Personal Info") {
        field("First Name") {
            textfield()
        }
        field("Last Name") {
            textfield()
        }
        field("Birthday") {
            datepicker()
        }
    }
    fieldset("Contact") {
        field("Phone") {
            textfield()
        }
        field("Email") {
            textfield()
        }
    }
    button("Commit") {
        action { println("Wrote to database!")}
    }
}
圖7.7-1
圖7.7-2

是不是很棒? 您可以為每個(gè)字段指定一個(gè)或多個(gè)控件点晴, Form將為您呈現(xiàn)分組和標(biāo)簽。

您也可以選擇在輸入字段之上布置標(biāo)簽:

fieldset("FieldSet", labelPosition = VERTICAL)

每個(gè)field都包含一個(gè)內(nèi)有標(biāo)簽的容器悯周,另一個(gè)容器用于在其中添加的輸入字段粒督。 默認(rèn)情況下,輸入字段的容器是HBox 禽翼,這意味著單個(gè)字段中的多個(gè)輸入將彼此水平相鄰布置屠橄。 您可以指定一個(gè)字段的orientation參數(shù),使其在多個(gè)輸入之間相互上下排列闰挡。 垂直取向的另一個(gè)用例是允許輸入隨著垂直方向的擴(kuò)展而增長(zhǎng)锐墙。 這對(duì)于在表單中顯示TextAreas非常方便:

form {
    fieldset("Feedback Form", labelPosition = VERTICAL) {
        field("Comment", VERTICAL) {
            textarea {
                prefRowCount = 5
                vgrow = Priority.ALWAYS
            }
        }
        buttonbar {
            button("Send")
        }
    }
}
圖7.8

上面的示例還使用buttonbar構(gòu)建器創(chuàng)建一個(gè)沒(méi)有標(biāo)簽的特殊字段,同時(shí)保留標(biāo)簽縮進(jìn)解总,使按鈕在輸入框下排列贮匕。

您將每個(gè)輸入綁定到一個(gè)模型(model)姐仅,您可以將控件布局的渲染留給Form花枫。 因此,如果可能掏膏,您可能希望在GridPane上使用它劳翰,接下來(lái)我們將介紹。

Form內(nèi)嵌套布局(Nesting layouts inside a Form)

您可以使用您選擇的任何布局容器來(lái)包裝fieldetsfields馒疹,以創(chuàng)建復(fù)雜的表單布局佳簸。

form {
    hbox(20) {
        fieldset("Left FieldSet") {
            hbox(20) {
                vbox {
                    field("Field l1a") { textfield() }
                    field("Field l2a") { textfield() }
                }
                vbox {
                    field("Field l1b") { textfield() }
                    field("Field l2b") { textfield() }
                }
            }
        }
        fieldset("Right FieldSet") {
            hbox(20) {
                vbox {
                    field("Field r1a") { textfield() }
                    field("Field r2a") { textfield() }
                }
                vbox {
                    field("Field r1b") { textfield() }
                    field("Field r2b") { textfield() }
                }
            }
        }
    }
}
圖7.9

GridPane

如果你想對(duì)控件的布局進(jìn)行細(xì)致的管理, GridPane會(huì)給你很多的颖变。 當(dāng)然生均,它需要更多的配置和代碼樣板。 在繼續(xù)使用GridPane之前腥刹,您可能需要考慮使用為您抽象了布局配置的Form或其他布局马胧。

使用GridPane的一種方法是聲明每row的內(nèi)容。 對(duì)于任何給定的Node您可以調(diào)用其gridpaneConstraints來(lái)配置該Node的各種GridPane行為衔峰,例如margincolumnSpan (圖7.10)

 gridpane {
     row {
         button("North") {
             useMaxWidth = true
             gridpaneConstraints {
                 marginBottom = 10.0
                 columnSpan = 2
             }
         }
     }
    row {
        button("West")
        button("East")
    }
    row {
        button("South") {
            useMaxWidth = true
            gridpaneConstraints {
                marginTop = 10.0
                columnSpan = 2
            }
        }
    }
}
圖7.11

請(qǐng)注意佩脊,在每行之間,如果在其gridpaneConstraints內(nèi)分別為“North”和“South”按鈕的marginBottommarginTop聲明了每行之間的距離為10.0 垫卤。

或者威彰,您可以顯式指定每個(gè)Node的列/行索引位置,而不是聲明每row的控件穴肘。 這將完成我們之前建立的精確布局歇盼,但是使用列/行索引來(lái)規(guī)范。 它有點(diǎn)冗長(zhǎng)评抚,但它可以更加明確地控制控件的位置豹缀。

gridpane {
     button("North") {
         useMaxWidth = true
         gridpaneConstraints {
             columnRowIndex(0,0)
             marginBottom = 10.0
             columnSpan = 2
         }
     }
    button("West").gridpaneConstraints {
        columnRowIndex(0,1)
    }
    button("East").gridpaneConstraints {
        columnRowIndex(1,1)
    }

    button("South") {
        useMaxWidth = true
        gridpaneConstraints {
            columnRowIndex(0,2)
            marginTop = 10.0
            columnSpan = 2
        }
    }
}

這些都是您可以在給定Node上修改的gridpaneConstraints屬性赵讯。 一些表示為可以賦值的簡(jiǎn)單屬性,而其他屬性可以通過(guò)函數(shù)賦值耿眉。

屬性 描述
columnIndex:Int 給定控件的列索引
rowIndex:Int 給定控件的行索引
columnRowIndex(columnIndex:Int边翼,rowIndex:Int) 指定行和列索引
columnSpan:Int 控件占用的列數(shù)
rowSpan:Int 控制占用的行數(shù)
hGrow:Priority 水平增長(zhǎng)優(yōu)先
vGrow:Priority 垂直成長(zhǎng)優(yōu)先
vhGrow:Priority 為vGrow和hGrow指定相同的優(yōu)先級(jí)
fillHeight:Boolean 設(shè)置Node是否填充其區(qū)域的高度
fillWidth:Boolean 設(shè)置Node是否填充其區(qū)域的寬度
fillHeightWidth:Boolean 設(shè)置Node是否填充高度和寬度的區(qū)域
hAlignment:HPos 水平對(duì)齊政策
vAlignment:VPos 垂直對(duì)齊策略
margin:Int Node所有四邊的邊距
marginBottom:Int Node底部的邊距
marginTop:Int Node頂端的邊距
marginLeft:Int Node左側(cè)的左邊距
marginRight:Int Node右側(cè)的右邊距
marginLeftRight:Int Node的右邊距和左邊距
marginTopBottom:Int Node的頂部和底部邊距

另外,如果需要配置ColumnConstraints鸣剪,可以在GridPane本身的GridPane Node上調(diào)用gridpaneColumnConstraints 组底,也可以調(diào)用constraintsForColumn(columnIndex)

gridpane {
    row {
        button("Left") {
            gridpaneColumnConstraints {
                percentWidth = 25.0
            }
        }

        button("Middle")
        button("Right")
    }
    constraintsForColumn(1).percentWidth = 50.0
}

StackPane

一個(gè)StackPane是一個(gè)布局筐骇,您將不太經(jīng)常使用债鸡。 對(duì)于您添加的每個(gè)控件,它將逐字地堆疊在一起(literally stack them )铛纬,而不是像VBox厌均,但是字面上覆蓋它們(literally overlay them)。

例如告唆,您可以創(chuàng)建一個(gè)“BOTTOM” Button并在其頂部放置一個(gè)“TOP” Button 棺弊。 您聲明控件的順序?qū)⒁韵嗤捻樞驈牡撞康巾敳刻砑铀鼈儯▓D7.10)。

class MyView: View() {

    override val root =  stackpane {
        button("BOTTOM") {
           useMaxHeight = true
           useMaxWidth = true
           style {
               backgroundColor += Color.AQUAMARINE
               fontSize = 40.0.px
           }
        }

        button("TOP") {
            style {
                backgroundColor += Color.WHITE
            }
        }
    }
}
圖7.11

TabPane

TabPane創(chuàng)建一個(gè)用“tab”分隔的不同屏幕的UI擒悬。 這允許通過(guò)點(diǎn)擊相應(yīng)的選項(xiàng)卡快速輕松地切換不同的屏幕(圖7.11)模她。 您可以聲明一個(gè)tabpane(),然后根據(jù)需要聲明盡可能多的tab()實(shí)例懂牧。 對(duì)于每個(gè)tab()函數(shù)侈净,通過(guò)Tab的名稱和父Node控件來(lái)填充它。

 tabpane {
    tab("Screen 1", VBox()) {
        button("Button 1")
        button("Button 2")
    }
    tab("Screen 2", HBox()) {
        button("Button 3")
        button("Button 4")
    }
}
圖7.12

TabePane是分隔屏幕并組織大量控件的有效工具僧凤。 語(yǔ)法有些簡(jiǎn)潔畜侦,足以在tab()塊中聲明像TableView這樣的復(fù)雜控件(圖7.13)。

tabpane {
  tab("Screen 1", VBox()) {
      button("Button 1")
      button("Button 2")
  }
  tab("Screen 2", HBox()) {
      tableview<Person> {

          items = listOf(
              Person(1,"Samantha Stuart",LocalDate.of(1981,12,4)),
              Person(2,"Tom Marks",LocalDate.of(2001,1,23)),
              Person(3,"Stuart Gills",LocalDate.of(1989,5,23)),
              Person(3,"Nicole Williams",LocalDate.of(1998,8,11))
          ).observable()

          column("ID",Person::id)
          column("Name", Person::name)
          column("Birthday", Person::birthday)
          column("Age",Person::age)
      }
  }
}
圖7.13

像許多構(gòu)建器一樣躯保, TabPane有幾個(gè)屬性可以調(diào)整其選項(xiàng)卡的行為旋膳。 例如,您可以調(diào)用tabClosingPolicy來(lái)去掉選項(xiàng)卡上的“X”按鈕吻氧,從而無(wú)法關(guān)閉溺忧。

class MyView: View() {
    override val root =  tabpane {
        tabClosingPolicy = TabPane.TabClosingPolicy.UNAVAILABLE

        tab("Screen 1", VBox()) {
            button("Button 1")
            button("Button 2")
        }
        tab("Screen 2", HBox()) {
            button("Button 3")
            button("Button 4")
        }
    }
}

菜單構(gòu)建器

以嚴(yán)格面向?qū)ο蟮姆绞綐?gòu)建菜單可能很麻煩。 但是使用類型安全的構(gòu)建器盯孙,Kotlin的函數(shù)結(jié)構(gòu)可以直觀地聲明嵌套的菜單層次結(jié)構(gòu)鲁森。

MenuBar,Menu和MenuItem

使用導(dǎo)航菜單在用戶界面上保留大量命令并不常見(jiàn)振惰。 例如歌溉, BorderPanetop區(qū)域通常是MenuBar所在的地方。 在那里可以輕松添加菜單和子菜單(圖7.12)。

menubar {
   menu("File") {
       menu("Connect") {
           item("Facebook")
           item("Twitter")
       }
       item("Save")
       item("Quit")
   }
   menu("Edit") {
       item("Copy")
       item("Paste")
   }
}
圖7.14

您還可以選擇提供鍵盤快捷鍵痛垛,圖形以及每個(gè)item()action函數(shù)參數(shù)草慧,以指定選定操作時(shí)的動(dòng)作(圖7.14)。

menubar {
     menu("File") {
         menu("Connect") {
             item("Facebook", graphic = fbIcon).action { println("Connecting Facebook!") }
             item("Twitter", graphic = twIcon).action { println("Connecting Twitter!") }
         }
         item("Save","Shortcut+S").action {
             println("Saving!")
         }
         menu("Quit","Shortcut+Q").action {
             println("Quitting!")
         }
     }
     menu("Edit") {
         item("Copy","Shortcut+C").action {
             println("Copying!")
         }
         item("Paste","Shortcut+V").action {
             println("Pasting!")
         }
     }
 }

分隔線(Separators)

您可以在Menu的兩個(gè)items之間聲明一個(gè)separator()來(lái)創(chuàng)建一個(gè)分隔線匙头。 這有助于給Menu分組命令并將它們分開(kāi)(圖7.15)漫谷。

 menu("File") {
     menu("Connect") {
         item("Facebook")
         item("Twitter")
     }
     separator()
     item("Save","Shortcut+S") {
         println("Saving!")
     }
     item("Quit","Shortcut+Q") {
         println("Quitting!")
     }
 }
圖7.15

上下文菜單(ContextMenu)

JavaFX中的大多數(shù)控件都有一個(gè)contextMenu屬性,您可以在其中指定ContextMenu實(shí)例蹂析。 這是一個(gè)在右鍵單擊控件時(shí)彈出的Menu舔示。

一個(gè)ContextMenu有函數(shù)可以添加MenuMenuItem實(shí)例,就像MenuBar一樣 电抚。 例如惕稻,將一個(gè)ContextMenu添加到TableView<Person>是有幫助的,并提供要在表格記錄上完成的命令(圖7.16)蝙叛。 有一個(gè)名為contextmenu的構(gòu)建器將構(gòu)建一個(gè)ContextMenu并將其賦值給控件的contextMenu屬性俺祠。

tableview(persons) {
     column("ID", Person::id)
     column("Name", Person::name)
     column("Birthday", Person::birthday)
     column("Age", Person::age)

     contextmenu {
         item("Send Email").action {
             selectedItem?.apply { println("Sending Email to $name") }
         }
         item("Change Status").action {
             selectedItem?.apply { println("Changing Status for $name") }
         }
     }
 }
圖7.16

注意還有可用的RadioMenuItemCheckMenuItem這些MenuItem變體。

當(dāng)菜單被選為op塊參數(shù)時(shí)借帘,menuitem構(gòu)建器采取動(dòng)作來(lái)執(zhí)行蜘渣。 不幸的是,這破壞了其他構(gòu)建器姻蚓,其中op塊對(duì)構(gòu)建器創(chuàng)建的元素進(jìn)行操作宋梧。 因此匣沼,引入item構(gòu)建器作為替代狰挡,您可以在item本身上操作,因此您必須調(diào)用setOnAction來(lái)賦值動(dòng)作释涛。menuitem構(gòu)建器沒(méi)有被棄用加叁,因?yàn)樗员?code>item構(gòu)建器更簡(jiǎn)潔的方式解決了常見(jiàn)情況。

ListMenu

TornadoFX帶有一個(gè)列表菜單(ListMenu)唇撬,其行為和看起來(lái)更像是一個(gè)典型的基于ul/li的HTML5菜單它匕。

以下代碼示例顯示如何使用構(gòu)建器模式的ListMenu

listmenu(theme = "blue") {
    item(text = "Contacts", graphic = Styles.contactsIcon()) {
        // Marks this item as active.
        activeItem = this
        whenSelected { /* Do some action */ }
    }
    item(text = "Projects", graphic = Styles.projectsIcon())
    item(text = "Settings", graphic = Styles.settingsIcon())
}

以下屬性可用于配置ListMenu :

Css屬性(Css Properties)

偽類(Pseudo Classes)

看看ListMenu的默認(rèn)樣式表。

項(xiàng)目(Item)

item構(gòu)建器允許以非常方便的方式為ListMenu創(chuàng)建items窖认。 支持以下語(yǔ)法:

item("SomeText", graphic = SomeNode, tag = SomeObject) {
    // Marks this item as active.
    activeItem = this

    // Do some action when selected
    whenSelected { /* Action */ }
}

填充父容器(Filling the parent container)

useMaxWidth屬性可用于水平填充父容器豫柬。 useMaxHeight屬性將垂直填充父容器。 這些屬性實(shí)際上適用于所有節(jié)點(diǎn)扑浸,但對(duì)ListMenu特別有用烧给。

Squeezebox

JavaFX具有手風(fēng)琴(Accordion)控件,可讓您將一組TilePanes組合在一起喝噪,形成手風(fēng)琴控件(accordion of controls)础嫡。 JavaFX手風(fēng)琴(Accordion)只允許您一次打開(kāi)單個(gè)手風(fēng)琴折疊(a single accordion fold),并且還有一些其他缺點(diǎn)。 為了解決這個(gè)問(wèn)題榴鼎,TornadoFX附帶了SqueezeBox組件伯诬,其行為看起來(lái)非常類似于手風(fēng)琴(Accordion),同時(shí)提供了一些增強(qiáng)功能巫财。

squeezebox {
    fold("Customer Editor", expanded = true) {
        form {
            fieldset("Customer Details") {
                field("Name") { textfield() }
                field("Password") { textfield() }
            }
        }
    }
    fold("Some other editor", expanded = true) {
        stackpane {
            label("Nothing here")
        }
    }
}
圖7.17

一個(gè)Squeezebox顯示兩個(gè)折疊盗似,兩者都默認(rèn)擴(kuò)展。

您可以通過(guò)將multiselect = false傳遞給構(gòu)建器構(gòu)造函數(shù)平项,使SqueezeBox僅允許在任何給定時(shí)間展開(kāi)單個(gè)折疊桥言。

您可以選擇通過(guò)單擊標(biāo)題窗格右側(cè)的十字架(clicking a cross in the right corner of the title pane)而允許折疊成為可關(guān)閉的(allow folds to be closable)。 您可以通過(guò)將closeable = true傳遞給fold構(gòu)建器葵礼,從而以每折為單位啟用關(guān)閉按鈕(enable the close buttons on a per fold basis)号阿。

squeezebox {
    fold("Customer Editor", expanded = true, closeable = true) {
        form {
            fieldset("Customer Details") {
                field("Name") { textfield() }
                field("Password") { textfield() }
            }
        }
    }
    fold("Some other editor", closeable = true) {
        stackpane {
            label("Nothing here")
        }
    }
}
圖7.18

這個(gè)SqueezeBox有可關(guān)閉的折疊(closeable folds)。

closeable屬性當(dāng)然可以結(jié)合expanded鸳粉。

SqueezeBoxAccordion之間的另一個(gè)重要區(qū)別就是分配空間(distributes overflowing space)的方式扔涧。 手風(fēng)琴(Accordion)將垂直延伸以填充其父容器,并將當(dāng)前打開(kāi)的任何折疊推至底部届谈。 如果父容器非常大枯夜,這將創(chuàng)建一個(gè)不自然的查看視圖。 在這方面艰山,擠壓框(SqueezeBox)可能默認(rèn)就是您想要的湖雹,但您可以添加fillHeight = true以獲得類似于Accordion的外觀。

您可以像您一樣創(chuàng)建一個(gè)TitlePane樣式一樣來(lái)創(chuàng)建SqueezeBox樣式曙搬。 關(guān)閉按鈕有一個(gè)名為close-buttoncss類摔吏,容器有一個(gè)名為squeeze-boxcss類。

Drawer

抽屜(Drawer)是一個(gè)非常像TabPane的導(dǎo)航組件纵装,但它在父容器的任一側(cè)的垂直或水平放置的按鈕欄中組織每個(gè)抽屜項(xiàng)目龙助。 它類似于許多流行的業(yè)務(wù)應(yīng)用程序和IDE中發(fā)現(xiàn)的工具抽屜(tool drawers)抠藕。 當(dāng)選擇項(xiàng)目時(shí)桦卒,項(xiàng)目的內(nèi)容將顯示在跨越控件的高度或?qū)挾鹊膬?nèi)容區(qū)域中的按鈕旁邊或上方/下方鳖敷,以及內(nèi)容的首選寬度或高度,具體取決于是否將其屯彀Γ靠在父級(jí)的垂直或水平方面滤祖。 在多重選擇(multiselect)模式下,您甚至可以同時(shí)打開(kāi)多個(gè)抽屜物品瓶籽,讓它們共享它們之間的空間匠童。 它們將始終按照相應(yīng)按鈕的順序打開(kāi)。

class DrawerView : View("TornadoFX Info Browser") {
    override val root = drawer {
        item("Screencasts", expanded = true) {
            webview {
                prefWidth = 470.0
                engine.userAgent = iPhoneUserAgent
                engine.load(TornadoFXScreencastsURI)
            }
        }
        item("Links") {
            listview(links) {
                cellFormat { link ->
                    graphic = hyperlink(link.name) {
                        setOnAction {
                            hostServices.showDocument(link.uri)
                        }
                    }
                }
            }
        }
        item("People") {
            tableview(people) {
                column("Name", Person::name)
                column("Nick", Person::nick)
            }
        }
   }

   class Link(val name: String, val uri: String)
   class Person(val name: String, val nick: String)

   // Sample data variables left out (iPhoneUserAgent, TornadoFXScreencastsURI, people and links)
}
圖7.19

抽屜可以配置為顯示右側(cè)的按鈕棘劣,您可以選擇同時(shí)支持打開(kāi)多個(gè)抽屜物品俏让。 當(dāng)以多重選擇模式運(yùn)行時(shí),內(nèi)容上方會(huì)出現(xiàn)一個(gè)標(biāo)題,這將有助于區(qū)分內(nèi)容區(qū)域中的項(xiàng)目首昔。 您可以使用布爾的showHeader參數(shù)控制標(biāo)題外觀寡喝。 當(dāng)啟用多重選擇時(shí),它將默認(rèn)為true勒奇,否則為false预鬓。

drawer(side = Side.RIGHT, multiselect = true) {
    // Everything else is identical
}
圖7.20

帶有右側(cè)按鈕的抽屜,多選模式和標(biāo)題窗格赊颠。

當(dāng)抽屜被添加到某物的旁邊時(shí)格二,您可以選擇抽屜的內(nèi)容區(qū)域是否應(yīng)替換其旁邊的節(jié)點(diǎn)(默認(rèn))或浮動(dòng)。 floatingContent屬性默認(rèn)為false竣蹦,導(dǎo)致Drawer替換其旁邊的內(nèi)容顶猜。

您可以使用DrawermaxContentSizefixedContentSize屬性進(jìn)一步控制內(nèi)容區(qū)域的大小。 根據(jù)dockingSide痘括,這些屬性將限制內(nèi)容區(qū)域的寬度或高度长窄。

Workspace功能內(nèi)置支持抽屜控件。 任何WorkspaceleftDrawer纲菌, rightDrawerbottomDrawer屬性將允許您將抽屜項(xiàng)目bottomDrawer在其中挠日。 在“工作區(qū)(Workspace)”一章中了解更多信息。

轉(zhuǎn)換可觀察列表項(xiàng)并綁定到布局(Converting observable list items and binding to layouts)

TODO

總結(jié)

到目前為止翰舌,您應(yīng)該擁有能力可以使用布局嚣潜,標(biāo)簽窗格以及其他控件來(lái)管理控件。 將這些與數(shù)據(jù)控件結(jié)合使用椅贱,您應(yīng)該可以在一小部分時(shí)間內(nèi)轉(zhuǎn)換UI懂算。

當(dāng)涉及到構(gòu)建器時(shí),您已經(jīng)達(dá)到了頂峰(top of the peak)夜涕,并擁有所需要的所有成效犯犁。 剩下的所有內(nèi)容都是圖表和形狀(charts and shapes),我們將在接下來(lái)的兩章中介紹女器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市住诸,隨后出現(xiàn)的幾起案子驾胆,更是在濱河造成了極大的恐慌,老刑警劉巖贱呐,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丧诺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奄薇,警方通過(guò)查閱死者的電腦和手機(jī)驳阎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人呵晚,你說(shuō)我怎么就攤上這事蜘腌。” “怎么了饵隙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵撮珠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我金矛,道長(zhǎng)芯急,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任驶俊,我火速辦了婚禮娶耍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饼酿。我一直安慰自己伺绽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布嗜湃。 她就那樣靜靜地躺著奈应,像睡著了一般。 火紅的嫁衣襯著肌膚如雪购披。 梳的紋絲不亂的頭發(fā)上杖挣,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音刚陡,去河邊找鬼惩妇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛筐乳,可吹牛的內(nèi)容都是我干的歌殃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蝙云,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼氓皱!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起勃刨,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤波材,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后身隐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體廷区,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年贾铝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隙轻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埠帕。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖玖绿,靈堂內(nèi)的尸體忽然破棺而出敛瓷,到底是詐尸還是另有隱情,我是刑警寧澤镰矿,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布琐驴,位于F島的核電站,受9級(jí)特大地震影響秤标,放射性物質(zhì)發(fā)生泄漏绝淡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一苍姜、第九天 我趴在偏房一處隱蔽的房頂上張望牢酵。 院中可真熱鬧,春花似錦衙猪、人聲如沸馍乙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丝格。三九已至,卻和暖如春棵譬,著一層夾襖步出監(jiān)牢的瞬間显蝌,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工订咸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留曼尊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓脏嚷,卻偏偏與公主長(zhǎng)得像骆撇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子父叙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容