数字类型转换 判断是否属于某个数字类型 val vs var val:值 final val 值名称:类型 = xxx var:变量 var 值名称:类型 = xxx Scala基本数据类型 Byte/Char Short/Int/Long/Float/Double Boolean lazy scala 函数 函数/方法的定义 def 方法名(参数名:参数类型): 返回值类型 = { // 括号内的叫做方法体 // 方法体内的最后一行为返回值,不需要使用return } 默认参数: 在函数定义时,允许指定参数的默认值 $SPARK_HOME/conf/spark-defaults.conf 可变参数: JDK5+ : 可变参数 循环表达式 to Range until 定义与使用 命名参数 调用参数可以顺序不一样 可变参数 即参数可以有任意多个 /** * 可变参数 ,可以有多个 类似 java 的 ... * @param numbers */ def sum(numbers:Int*): Unit ={ 表达式 if else 等 循环表达式 def loop(): Unit ={ var num1 = 1 to 10 var num2 = 1.to(10) // 与 1 to 10 写法一样的 for (elem <- num1) { println(elem) } for( i <- 1 to 10 if i%2 == 0){ println(i) } val courses=Array("语文","数学") for (elem <- courses) { } courses.foreach(f => println(f)) courses.foreach(f => { println(f) } ) } 面向对象
占位符 _ 下划线,即 使用默认值,不赋值
类的定义与使用 package org.train.scala.c4 object SimpleApp { def main(args: Array[String]): Unit = { val person=new People(); person.name="ss" println(person.name+ person.age) person.eat() person.printInfo() } } class People{ //变量,自动生成 get /set 方法 var name="" var nick : String =_ //不可变常量,没有 get/set val age:Int = 10 def eat(): String={ name +"eat ..." } //只能在class 内可以访问,类外不可以 private [this] val gender="male" // [this] 可去掉: private val gender="male" def printInfo(): Unit ={ println(gender) } } 构造器 package com.imooc.scala.course04 object ConstructorApp { def main(args: Array[String]): Unit = { // val person = new Person("zhangsan", 30) // println(person.name + " : " + person.age + " : " + person.school) // // // val person2 = new Person("PK", 18, "M") // println(person2.name + " : " // + person2.age + " : " // + person2.school + " :" // + person2.gender // ) val student = new Student("PK", 18, "Math") println(student.name + " : " + student.major) println(student) } } // 主构造器 class Person(val name:String, val age:Int) { println("Person Constructor enter....") val school = "ustc" var gender:String = _ // 附属构造器 def this(name:String, age:Int, gender:String) { this(name, age) // 附属构造器的第一行代码必须要调用主构造器或者其他附属构造器 this.gender = gender } println("Person Constructor leave....") } class Student(name:String, age:Int, var major:String) extends Person(name, age) { println("Person Student enter....") //重写 override val school = "peking" override def toString: String = "Person: override def toString :" + school println("Person Student leave....") } 抽象类 abstract package org.train.scala.c4 object AbstractApp { def main(args: Array[String]): Unit = { new Stude2().speak; } } /** * 只有定义,没有实现 */ abstract class Person2{ //抽象方法 def speak //抽象的属性 val name:String } class Stude2 extends Person2{ override def speak: Unit = { println("sk") } override val name: String = _ } 伴生类和伴生对象 /** * 伴生类和伴生对象 * 如果有一个class,还有一个与class同名的object * 那么就称这个object是class的伴生对象,class是object的伴生类 */ //伴生类 (相对 object ApplyTest ) class ApplyTest{ } //伴生对象 (相对class ApplyTest 来说的 ) object ApplyTest{ } apply 方法 package com.imooc.scala.course04 object ApplyApp { def main(args: Array[String]): Unit = { // for(i <- 1 to 10) { // ApplyTest.incr // } // // println(ApplyTest.count) // 10 说明object本身就是一个单例对象 val b = ApplyTest() // ==> Object.apply println("~~~~~~~~~~~") val c = new ApplyTest() println(c) c() // 类名() ==> Object.apply // 对象() ==> Class.apply } } /** * 伴生类和伴生对象 * 如果有一个class,还有一个与class同名的object * 那么就称这个object是class的伴生对象,class是object的伴生类 */ class ApplyTest{ def apply() = { println("class ApplyTest apply....") } } object ApplyTest{ println("Object ApplyTest enter....") var count = 0 def incr = { count = count + 1 } // 最佳实践:在Object的apply方法中去new Class def apply():ApplyTest = { println("Object ApplyTest apply....") // 在object中的apply中new class new ApplyTest } println("Object ApplyTest leave....") } case class // 通常用在模式匹配 object CaseClassApp { def main(args: Array[String]): Unit = { println(Dog("wangcai").name) } } // case class不用new case class Dog(name:String) case class Dog2(name: String) trait 接口 Java/Scala OO 封装:属性、方法封装到类中 Person: private int id, String name, Date birthday..... getter/setter eat、sleep.... 继承:父类和子类之间的关系 User extends Person exam..... 多态:***** 父类引用指向子类对象 精髓所在 开发框架的基石 Person person = new Person(); User user = new User(); Person person = new User(); Trait xxx extends ATrait with BTrait class SparkConf(loadDefaults: Boolean) extends Cloneable with Logging with Serializable ... .... 集合 数组 package com.imooc.scala.course05 object ArrayApp extends App{ // val a = new Array[String](5) // a.length // a(1) = "hello" // // val b = Array("hadoop", "spark", "storm") // // val c = Array(2,3,4,5,6,7,8,9) // c.sum // c.min // c.max // // c.mkString(",") // 可变数组 val c = scala.collection.mutable.ArrayBuffer[Int]() c += 1 c += 2 c += (3,4,5) c ++= Array(6,7,8) c.insert(0,0) c.remove(1) c.remove(0,3) c.trimEnd(2) // for(i <- 0 until c.length) { // println(c(i)) // } // for(ele <- c) { // println(ele) // } for(i <- (0 until c.length).reverse) { println(c(i)) } // println(c.toArray.mkString) } List nil 就是 一个空的集合的意思 package com.imooc.scala.course05 object ListApp extends App{ // val l = List(1,2,3,4,5) // // // val l5 = scala.collection.mutable.ListBuffer[Int]()//可变list // l5 += 2 // l5 += (3,4,5) // l5 ++= List(6,7,8,9) // // // l5 -= 2 // l5 -= 3 // l5 -= (1, 4) // l5 --= List(5,6,7,8) // // println(l5) // // // l5.isEmpty // l5.head // l5.tail def sum(nums:Int*):Int = { if(nums.length == 0) { 0 } else { nums.head + sum(nums.tail:_*) // _* 类型自动转换 } } // val set = scala.collection.mutable.Set[Int]() // set += 1 // set += (1,1) println(sum()) println(sum(1,2,3,4)) } head 就是 第一个, 而 tail 就是 除了 第一个数据的 之后的所有数据 set 无序,不可重复的 list ,用法与 list 类似 // val set = scala.collection.mutable.Set[Int]() //可变set // set += 1 // set += (1,1) map
package com.imooc.scala.course05 import scala.collection.mutable object MapApp extends App{ val a = Map("PK" -> 18, "zhangsan" -> 30) val b = Map("PK" -> 18, "zhangsan" -> 30) // val c = mutable.HashMap[String,Int]() // b.getOrElse("PK", 9) // for((key,value) <- b) { // println(key + " : " + value ) // } // for(key <- b.keySet) { // println(key + " : " + b.getOrElse(key, 9)) // } // for(value <- b.values) { // println(value) // } for((key,_) <- b) { println(key + " : " + b.getOrElse(key, 9) ) } } Option , Some, None object OptionApp extends App { val m = Map(1 -> 2) // println(m(1)) // println(m(2)) // println(m.get(1).get) println(m.getOrElse(2, "None")) } /** * case object None extends Option[Nothing] { def isEmpty = true def get = throw new NoSuchElementException("None.get") } final case class Some[+A](x: A) extends Option[A] { def isEmpty = false def get = x } */ Option 是一个抽象类 , 实现类是 Some ,None Some 代表存在, None 代表空 Tuple // 元组:(.......) object TupleApp extends App{ val a = (1,2,3,4,5) for(i <- 0 until(a.productArity)) { println(a.productElement(i)) } //下标从 1 开始,不是 0 val hostPort = ("localhost",8080) hostPort._1 hostPort._2 } 模式匹配 即 match case 类似 java 的 switch case 用法,但是比JAVA的强大很多 模式匹配 Java: 对一个值进行条件判断,返回针对不同的条件进行不同的处理 变量 match { case value1 => 代码1 case value2 => 代码2 ..... case _ => 代码N } 基本模式匹配 def grade(grade : String): Unit = { grade match{ case "A" => println("99。。。") case "B" => println("89。。。") case _ => println("00。。。") } } 条件匹配 def grade(grade : String): Unit = { grade match{ case "A" => println("99。。。") case "B" => println("89。。。") //条件匹配, 双重过滤 case _ if(grade=="C") => println("cc。。。") case _ => println("00。。。") } } Array 模式匹配 def greeting(array:Array[String]): Unit = { array match { case Array("zhangsan") => println("Hi:zhangsan") //数组必须是 zhangsan case Array(x,y) => println("Hi:" + x + " , " + y) // 数组可以有任意的2个 case Array("zhangsan", _*) => println("Hi:zhangsan and other friends...")//多个内容, case _ => println("Hi: everybody...") } } List 模式匹配 def greeting(list:List[String]): Unit = { list match { case "zhangsan"::Nil => println("Hi:zhangsan") //只有 zhangsan case x::y::Nil => println("Hi:" + x + " , " + y) //只有2个 任意元素 case "zhangsan"::tail => println("Hi: zhangsan and other friends...") // 多个 case _ => println("Hi:everybody....") } } 类型匹配 def matchType(obj:Any): Unit = { obj match { case x:Int => println("Int") case x:String => println("String") case m:Map[_,_] => m.foreach(println) // map , key value 任意 case _ => println("other type") } } 异常处理 //IO val file = "test.txt" try{ // open file // use file val i = 10/0 println(i) } catch { case e:ArithmeticException => println("除数不能为0..") case e:Exception => println(e.getMessage) } finally { // 释放资源,一定能执行: close file } case class 模式匹配 def caseclassMatch(person:Person): Unit = { person match { case CTO(name,floor) => println("CTO name is: " + name + " , floor is: " + floor) case Employee(name,floor) => println("Employee name is: " + name + " , floor is: " + floor) case _ => println("other") } } class Person case class CTO(name:String, floor:String) extends Person case class Employee(name:String, floor:String) extends Person case class Other(name:String) extends Person caseclassMatch(CTO("PK", "22")) caseclassMatch(Employee("zhangsan", "2")) caseclassMatch(Other("other")) Some None 模式匹配 val grades = Map("PK"->"A", "zhangsan"-> "C") def getGrade(name:String): Unit = { val grade = grades.get(name) grade match { case Some(grade) => println(name + ": your grade is :" + grade) case None => println("Sorry....") } } 高级函数 字符串高级操作 object StringApp extends App { val s = "Hello:" val name = "PK" // println(s + name) println(s"Hello:$name") val team = "AC Milan" // 插值 println(s"Hello:$name, Welcome to $team") val b = """ |这是一个多行字符串 |hello |world |PK """.stripMargin println(b) } 查值,就是 s + “ xx $属性名 ” , 必须是 s 开头的, 属性名 必须带上 $ 匿名函数 匿名参数传递给 对应函数 匿名函数传递给 add 函数 , 或者 匿名函数传递给变量都是可以的 currying 函数 // 将原来接收两个参数的一个函数,转换成2个 def sum(a:Int, b:Int) = a+b println(sum(2,3)) // 改成 currying 函数 def sum2(a:Int)(b:Int) = a + b println(sum2(2)(3)) 即将参数给 拆开 高阶函数
package com.imooc.scala.course07 /** * 匿名函数: 函数是可以命名的,也可以不命名 * (参数名:参数类型...) => 函数体 */ object FunctionApp extends App { // def sayHello(name:String): Unit = { // println("Hi: " + name) // } // // sayHello("PK") // // 将原来接收两个参数的一个函数,转换成2个 // def sum(a:Int, b:Int) = a+b // println(sum(2,3)) // // def sum2(a:Int)(b:Int) = a + b // println(sum2(2)(3)) val l = List(1, 2, 3, 4, 5, 6, 7, 8) //map: 逐个去操作集合中的每个元素 // l.map((x: Int) => x + 1) // l.map((x) => x * 2) // l.map(x => x * 2) // l.map(_ * 2).foreach(println) // l.map(_ * 2).filter(_ > 8).foreach(println) // 1+2 3+3 6+4 10+5 // l.reduce(_+_) // 即 求和 sum l.reduceLeft(_-_) l.reduceRight(_-_) l.fold(0)(_-_) println( l.fold(100)(_ + _)) // 从 100 开始 以此 求和 val f = List(List(1,2),List(3,4),List(5,6)) f.flatten // // 压扁 合成一个 List // flatMap f.map(_.map(_*2)) //每个元素 乘以2 f.flatMap(_.map(_*2)) // 并合成一个 List, 每个元素 乘以2 , val txt = scala.io.Source.fromFile("/Users/rocky/imooc/hello.txt").mkString // println(txt) val txts = List(txt) // 如何使用scala来完成wordcount统计 // 链式编程:Hibernate、Spark txts.flatMap(_.split(",")).map(x => (x,1)) //... .foreach(println) } 偏函数 /** * 偏函数:被包在花括号内没有match的一组case语句 */ object PartitalFunctionApp extends App { val names = Array("Akiho Yoshizawa", "YuiHatano", "Aoi Sola") val name = names(Random.nextInt(names.length)) name match { case "Akiho Yoshizawa" => println("吉老师...") case "YuiHatano" => println("波老师....") case _ => println("真不知道你们在说什么....") } // A 输入参数类型 B 输出参数类型 // A 输入参数类型 第一个参数, B 输出参数类型 第二个参数, def sayChinese:PartialFunction[String,String] = { case "Akiho Yoshizawa" => "吉老师..." case "YuiHatano" => "波老师...." case _ => "真不知道你们在说什么...." } println(sayChinese("Akiho Yoshizawa")) } 隐式转换 即类似 java 的 动态代理 AOP 需求:为一个已存在的类添加一个新的方法 Java:动态代理 Scala:隐式转换 双刃剑 Spark/Hive/MR.... 调优 import java.io.File //import ImplicitAspect._ object ImplicitApp { // 定义隐式转换函数即可 // implicit def man2superman(man:Man):Superman = new Superman(man.name) // val man = new Man("PK") // man.fly() // implicit def file2RichFile(file: File): RichFile = new RichFile(file) // val file = new File("/Users/rocky/imooc/hello.txt") // val txt = file.read() // println(txt) } //class Man(val name: String) { // def eat(): Unit = { // println(s"man[ $name ] eat ..... ") // } //} // //class Superman(val name: String) { // def fly(): Unit = { // println(s"superman[ $name ] fly ..... ") // } //} class RichFile(val file: File) { def read() = { scala.io.Source.fromFile(file.getPath).mkString } } 隐式参数 object ImplicatParam extends App { def testParam(implicit name:String): Unit ={ println(name) } // testParam("aa") // implicit val name2 ="bb" // testParam// 不会报错,默认使用 name2 // implicit val s1="s1" // implicit val s2="s2" // testParam // 多个 会报错,不知道用 s1 还是 s2 } 隐式类 object ImplicitClassApp extends App { implicit class Calcuotor(x:Int){ def add(a:Int) = a+x } println(1.add(3)) // 可以使用调用 Calcuotor 方法 } scala 操作外部数据 读取文件及网络数据 import scala.io.Source object FileApp { def main(args: Array[String]): Unit = { val file = Source.fromFile("/Users/rocky/imooc/hello.txt")(scala.io.Codec.ISO8859) def readLine(): Unit ={ for(line <- file.getLines()){ println(line) } } //readLine() def readChar(): Unit ={ for(ele <- file) { println(ele) } } // readChar() def readNet(): Unit ={ val file = Source.fromURL("http://www.baidu.com") for(line <- file.getLines()){ println(line) } } readNet() } } 读取mysql package org.train.scala.c9 import java.sql.{Connection, DriverManager} object MySQLApp { def main(args: Array[String]): Unit = { val url = "jdbc:mysql://localhost:3306/mysql" val username = "root" val password = "123456" var connection:Connection = null try{ // make the connection classOf[com.mysql.jdbc.Driver] connection = DriverManager.getConnection(url, username, password) // create the statement, and run the select query val statement = connection.createStatement() val resultSet = statement.executeQuery("select host,user from user") while(resultSet.next()){ val host = resultSet.getString("host") val user = resultSet.getString("user") println(s"$host, $user") } } catch { case e:Exception => e.printStackTrace() } finally { // free if(connection == null) { connection.close() } } } } 与java 写法一样的 操作xml FIX4.2Test package org.train.scala.c9 import java.io.{FileInputStream, InputStreamReader} import scala.xml.XML object XMLApp { def main(args: Array[String]): Unit = { // loadXML() // readXMLAttr() updateXML() } def updateXML(): Unit ={ val xml = XML.load(this.getClass.getClassLoader.getResource("books.xml")) // println(xml) val bookMap = scala.collection.mutable.HashMap[String,String]() (xml \ "book").map(x => { val id = (x \ "@id").toString() val name = (x \ "name").text.toString bookMap(id) = name }) // for((key,value) <- bookMap) { // println(s"$key : $value") // } val newXml = {bookMap.map(updateXmlFile)} // println(newXml) XML.save("newBooks.xml", newXml) } def updateXmlFile(ele:(String,String)) = { val (id, oldName) = ele {oldName + " Programming"} } def readXMLAttr(): Unit = { val xml = XML.load(this.getClass.getClassLoader.getResource("pk.xml")) // println(xml) // header/field // val headerField = xml \ "header" \ "field" // println(headerField) // all field // val fields = xml \\ "field" // for (field <- fields) { // println(field) // } // header/field/name // val fieldAttributes = (xml \ "header" \ "field").map(_ \ "@name") // val fieldAttributes = (xml \ "header" \ "field" \\ "@name") // for (fieldAttribute <- fieldAttributes) { // println(fieldAttribute) // } //name="Logon" message // val filters = (xml \\ "message") // .filter(_.attribute("name").exists(_.text.equals("Logon"))) // val filters = (xml \\ "message") // .filter(x => ((x \ "@name").text).equals("Logon")) // for (filter <- filters) { // println(filter) // } // header/field content (xml \ "header" \ "field") .map(x => (x \ "@name", x.text, x \ "@required")) .foreach(println) } def loadXML(): Unit = { // val xml = XML.load(this.getClass.getClassLoader.getResource("test.xml")) // println(xml) // val xml = XML.load(new FileInputStream("/Users/rocky/source/scala-train/src/main/resources/test.xml")) // println(xml) val xml = XML.load( new InputStreamReader( new FileInputStream("/Users/rocky/source/scala-train/src/main/resources/test.xml") ) ) println(xml) } } scala 结合spring boot 参考 https://gitee.com/odilil/boot-scala?_from=gitee_search bean 注入 还可以: @Autowired val metaTableRepository : MetaTableRepository = null // 不能使用 _ 下划线 占位符来标识
JavaFX初探(菜单) 本节我们介绍如何创建菜单、菜单栏、增加菜单项、为菜单分类,创建子菜单、设置菜单上下文。你可以使用下面的类来创建菜单。 MenuBar MenuItem Menu CheckMenuItem RadioMenuItem CustomMenuItem SeparatorMenuItem ContextMenu 下图是一个典型的菜单的使用: 在应用中构建菜单 一个菜单就是一系列可操作的项目,可以根据用户的需要来表现。当一个菜单可见的时候,用户可以在某一时刻选中其中一个,在用户选中某一项时,这个菜单变成隐藏模式。通过使用菜单,我们可以节省用户界面的空间,因为有一些功能某些时间并不是总要现实出来的。 菜单在菜单栏中被分组,你需要使用下面的菜单项类,当你构建一个菜单的时候。 MenuItem 创建可选项 Menu 创建子菜单 RadioButtonItem 创建一个单选项 CheckMenuItem 这个菜单项可以在选择被无选择之间转换。 为了给菜单分类,可以使用SeparatorMenuItem 类。 菜单通常在窗口的顶部,并且这些菜单是隐藏的,我们可以通过鼠标点击上下文来打开菜单。 创建菜单栏 尽管菜单栏可以放在用户界面的任何地方,但是一般情况我们放到窗口的顶部。并且菜单栏可已自动的改变自己的大小。默认情况下,每一个菜单栏中的菜单像一个按钮一样呈现出来。 想想一个这样的一个应用,他显示植物的名称,图片,以及简单的描述信息。我们创建3个菜单项,:File,Edit,View.并给这三项添加菜单项。代码如下所示: import java.util.AbstractMap.SimpleEntry; import java.util.Map.Entry; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.effect.DropShadow; import javafx.scene.effect.Effect; import javafx.scene.effect.Glow; import javafx.scene.effect.SepiaTone; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class MenuSample extends Application { final PageData[] pages = new PageData[] { new PageData( "Apple" , "The apple is the pomaceous fruit of the apple tree, species Malus " + "domestica in the rose family (Rosaceae). It is one of the most " + "widely cultivated tree fruits, and the most widely known of " + "the many members of genus Malus that are used by humans. " + "The tree originated in Western Asia, where its wild ancestor, " + "the Alma, is still found today." , "Malus domestica" ), new PageData( "Hawthorn" , "The hawthorn is a large genus of shrubs and trees in the rose " + "family, Rosaceae, native to temperate regions of the Northern " + "Hemisphere in Europe, Asia and North America. " + "The name hawthorn was " + "originally applied to the species native to northern Europe, " + "especially the Common Hawthorn C. monogyna, and the unmodified " + "name is often so used in Britain and Ireland." , "Crataegus monogyna" ), new PageData( "Ivy" , "The ivy is a flowering plant in the grape family (Vitaceae) native to" + " eastern Asia in Japan, Korea, and northern and eastern China. " + "It is a deciduous woody vine growing to 30 m tall or more given " + "suitable support, attaching itself by means of numerous small " + "branched tendrils tipped with sticky disks." , "Parthenocissus tricuspidata" ), new PageData( "Quince" , "The quince is the sole member of the genus Cydonia and is native to " + "warm-temperate southwest Asia in the Caucasus region. The " + "immature fruit is green with dense grey-white pubescence, most " + "of which rubs off before maturity in late autumn when the fruit " + "changes color to yellow with hard, strongly perfumed flesh." , "Cydonia oblonga" ) }; final String[] viewOptions = new String[] { "Title" , "Binomial name" , "Picture" , "Description" }; final Entry[] effects = new Entry[] { new SimpleEntry<>( "Sepia Tone" , new SepiaTone()), new SimpleEntry<>( "Glow" , new Glow()), new SimpleEntry<>( "Shadow" , new DropShadow()) }; final ImageView pic = new ImageView(); final Label name = new Label(); final Label binName = new Label(); final Label description = new Label(); public static void main (String[] args) { launch(args); } @Override public void start (Stage stage) { stage.setTitle( "Menu Sample" ); Scene scene = new Scene( new VBox(), 400 , 350 ); MenuBar menuBar = new MenuBar(); // --- Menu File Menu menuFile = new Menu( "File" ); // --- Menu Edit Menu menuEdit = new Menu( "Edit" ); // --- Menu View Menu menuView = new Menu( "View" ); menuBar.getMenus().addAll(menuFile, menuEdit, menuView); ((VBox) scene.getRoot()).getChildren().addAll(menuBar); stage.setScene(scene); stage.show(); } private class PageData { public String name; public String description; public String binNames; public Image image; public PageData (String name, String description, String binNames) { this .name = name; this .description = description; this .binNames = binNames; image = new Image(getClass().getResourceAsStream(name + ".jpg" )); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 和其他的UI控件不同,Menu类和其他的扩展MenuItem的类都不是扩展自Node类。所以他们不能直接添加到场景中,需要先添加到MenuBar中然后在添加到场景中。运行如下图所示: 你可以使用键盘的方向键来浏览菜单,然而当你选中一个菜单的时候,什么都没有发生,那是因为我们还没有指定行为。 添加菜单项 为文件菜单添加功能。 Shuffle 加载植物的参考信息 Clear 删除参考信息并清空场景 Separator 分离菜单项 Exit 退出应用 使用MenuItem创建了一个Shuffle菜单,并添加了一个图片组件。我们可以为MenuItem指定文本和图片。我们可以通过setAction方法来指定该菜单被点击时候的事件,这个Button是一样的。代码如下: import java.util.AbstractMap.SimpleEntry; import java.util.Map.Entry; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.effect.DropShadow; import javafx.scene.effect.Effect; import javafx.scene.effect.Glow; import javafx.scene.effect.SepiaTone; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.TextAlignment; import javafx.stage.Stage; public class MenuSample extends Application { final PageData[] pages = new PageData[] { new PageData( "Apple" , "The apple is the pomaceous fruit of the apple tree, species Malus " + "domestica in the rose family (Rosaceae). It is one of the most " + "widely cultivated tree fruits, and the most widely known of " + "the many members of genus Malus that are used by humans. " + "The tree originated in Western Asia, where its wild ancestor, " + "the Alma, is still found today." , "Malus domestica" ), new PageData( "Hawthorn" , "The hawthorn is a large genus of shrubs and trees in the rose " + "family, Rosaceae, native to temperate regions of the Northern " + "Hemisphere in Europe, Asia and North America. " + "The name hawthorn was " + "originally applied to the species native to northern Europe, " + "especially the Common Hawthorn C. monogyna, and the unmodified " + "name is often so used in Britain and Ireland." , "Crataegus monogyna" ), new PageData( "Ivy" , "The ivy is a flowering plant in the grape family (Vitaceae) native" + " to eastern Asia in Japan, Korea, and northern and eastern China." + " It is a deciduous woody vine growing to 30 m tall or more given " + "suitable support, attaching itself by means of numerous small " + "branched tendrils tipped with sticky disks." , "Parthenocissus tricuspidata" ), new PageData( "Quince" , "The quince is the sole member of the genus Cydonia and is native" + " to warm-temperate southwest Asia in the Caucasus region. The " + "immature fruit is green with dense grey-white pubescence, most " + "of which rubs off before maturity in late autumn when the fruit " + "changes color to yellow with hard, strongly perfumed flesh." , "Cydonia oblonga" ) }; final String[] viewOptions = new String[] { "Title" , "Binomial name" , "Picture" , "Description" }; final Entry[] effects = new Entry[] { new SimpleEntry<>( "Sepia Tone" , new SepiaTone()), new SimpleEntry<>( "Glow" , new Glow()), new SimpleEntry<>( "Shadow" , new DropShadow()) }; final ImageView pic = new ImageView(); final Label name = new Label(); final Label binName = new Label(); final Label description = new Label(); private int currentIndex = - 1 ; public static void main (String[] args) { launch(args); } @Override public void start (Stage stage) { stage.setTitle( "Menu Sample" ); Scene scene = new Scene( new VBox(), 400 , 350 ); scene.setFill(Color.OLDLACE); name.setFont( new Font( "Verdana Bold" , 22 )); binName.setFont( new Font( "Arial Italic" , 10 )); pic.setFitHeight( 150 ); pic.setPreserveRatio( true ); description.setWrapText( true ); description.setTextAlignment(TextAlignment.JUSTIFY); shuffle(); MenuBar menuBar = new MenuBar(); final VBox vbox = new VBox(); vbox.setAlignment(Pos.CENTER); vbox.setSpacing( 10 ); vbox.setPadding( new Insets( 0 , 10 , 0 , 10 )); vbox.getChildren().addAll(name, binName, pic, description); // --- Menu File Menu menuFile = new Menu( "File" ); MenuItem add = new MenuItem( "Shuffle" , new ImageView( new Image( "menusample/new.png" ))); add.setOnAction((ActionEvent t) -> { shuffle(); vbox.setVisible( true ); }); menuFile.getItems().addAll(add); // --- Menu Edit Menu menuEdit = new Menu( "Edit" ); // --- Menu View Menu menuView = new Menu( "View" ); menuBar.getMenus().addAll(menuFile, menuEdit, menuView); ((VBox) scene.getRoot()).getChildren().addAll(menuBar, vbox); stage.setScene(scene); stage.show(); } private void shuffle () { int i = currentIndex; while (i == currentIndex) { i = ( int ) (Math.random() * pages.length); } pic.setImage(pages[i].image); name.setText(pages[i].name); binName.setText( "(" + pages[i].binNames + ")" ); description.setText(pages[i].description); currentIndex = i; } private class PageData { public String name; public String description; public String binNames; public Image image; public PageData (String name, String description, String binNames) { this .name = name; this .description = description; this .binNames = binNames; image = new Image(getClass().getResourceAsStream(name + ".jpg" )); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 当用户选择Shuffle菜单的时候,会就调用我们指定的setOnAction方法,显示出植物的名字、图片和简单描述。 Clear菜单用来抹除场景中的内容。我们可以使用下面的方法: MenuItem clear = new MenuItem( "Clear" ); clear.setAccelerator(KeyCombination.keyCombination( "Ctrl+X" )); clear.setOnAction ((ActionEvent t) -> { vbox.setVisible( false ); }) ; 1 2 3 4 5 6 Exit菜单实现如下: MenuItem exit = new MenuItem( "Exit" ); exit.setOnAction ((ActionEvent t) -> { System.exit( 0 ); }) ; 1 2 3 4 5 我们使用getItems方法来添加这些菜单项,如下: menuFile .getItems () .addAll ( add , clear, new SeparatorMenuItem(), exit) ; 1 2 编译运行应用,如下图所示: View菜单可以来用指定,显示植物的部分信息, // --- Creating four check menu items within the start method CheckMenuItem titleView = createMenuItem ( "Title" , name); CheckMenuItem binNameView = createMenuItem ( "Binomial name" , binName); CheckMenuItem picView = createMenuItem ( "Picture" , pic); CheckMenuItem descriptionView = createMenuItem ( "Description" , description); menuView.getItems().addAll(titleView, binNameView, picView, descriptionView); ... // The createMenuItem method private static CheckMenuItem createMenuItem (String title, final Node node){ CheckMenuItem cmi = new CheckMenuItem(title); cmi.setSelected(true); cmi.selectedProperty().addListener( (ObservableValue extends Boolean> ov, Boolean old_val, Boolean new_val) -> { node.setVisible(new_val); }); return cmi; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 CheckMenuItem 是MenuItem类的扩展,他可以选中和非选中,如果被选中了,会显示出一个标记。编译运行如下图所示: 创建子菜单 Edit菜单中定义了两个菜单项:图片效果和无效果。图片效果菜单项有子菜单项。 使用RadioMenuItem 来创建子菜单,代码如下: // Picture Effect menu Menu menuEffect = new Menu( "Picture Effect" ); final ToggleGroup groupEffect = new ToggleGroup(); for (Entry effect : effects) { RadioMenuItem itemEffect = new RadioMenuItem(effect.getKey()); itemEffect.setUserData(effect.getValue()); itemEffect.setToggleGroup(groupEffect); menuEffect.getItems().add(itemEffect); } // No Effects menu final MenuItem noEffects = new MenuItem( "No Effects" ); noEffects.setOnAction ((ActionEvent t) -> { pic.setEffect( null ); groupEffect.getSelectedToggle().setSelected( false ); }) ; // Processing menu item selection groupEffect . selectedToggleProperty () . addListener ( new ChangeListener() { public void changed(ObservableValue extends Toggle> ov, Toggle old_toggle, Toggle new_toggle) { if (groupEffect.getSelectedToggle() != null ) { Effect effect = (Effect) groupEffect.getSelectedToggle().getUserData(); pic.setEffect(effect); } } }) ; groupEffect . selectedToggleProperty () . addListener ( (ObservableValue extends Toggle> ov, Toggle old_toggle, Toggle new_toggle) -> { if (groupEffect.getSelectedToggle() != null ) { Effect effect = (Effect) groupEffect.getSelectedToggle().getUserData(); pic.setEffect(effect); } }) ; // Adding items to the Edit menu menuEdit . getItems () . addAll (menuEffect, noEffects) ; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 如下所示: 我们可以使用setDisable来禁用某一个菜单。 Menu menuEffect = new Menu( "Picture Effect" ); final ToggleGroup groupEffect = new ToggleGroup(); for (Entry effect : effects) { RadioMenuItem itemEffect = new RadioMenuItem(effect.getKey()); itemEffect.setUserData(effect.getValue()); itemEffect.setToggleGroup(groupEffect); menuEffect.getItems().add(itemEffect); } final MenuItem noEffects = new MenuItem( "No Effects" ); noEffects.setDisable( true ); noEffects.setOnAction ((ActionEvent t) -> { pic.setEffect( null ); groupEffect.getSelectedToggle().setSelected( false ); noEffects.setDisable( true ); }) ; groupEffect . selectedToggleProperty () . addListener ( (ObservableValue extends Toggle> ov, Toggle old_toggle, Toggle new_toggle) -> { if (groupEffect.getSelectedToggle() != null ) { Effect effect = (Effect) groupEffect.getSelectedToggle().getUserData(); pic.setEffect(effect); noEffects.setDisable( false ); } else { noEffects.setDisable( true ); } }) ; menuEdit . getItems () . addAll (menuEffect, noEffects) ; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 添加上下文菜单 如果你的界面中没有为菜单的控件,那么你可以使用上下文菜单。当点击鼠标的时候,弹出上下文菜单。 final ContextMenu cm = new ContextMenu(); MenuItem cmItem1 = new MenuItem( "Copy Image" ); cmItem1.setOnAction ((ActionEvent e) -> { Clipboard clipboard = Clipboard.getSystemClipboard(); ClipboardContent content = new ClipboardContent(); content.putImage(pic.getImage()); clipboard.setContent(content); }) ; cm . getItems () . add (cmItem1) ; pic . addEventHandler (MouseEvent.MOUSE_CLICKED, (MouseEvent e) -> { if (e.getButton() == MouseButton.SECONDARY) cm.show(pic, e.getScreenX(), e.getScreenY()); }) ; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15