Strict Standards: Declaration of Walker_Page::start_el() should be compatible with Walker::start_el(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 596

Strict Standards: Declaration of Walker_Page::end_el() should be compatible with Walker::end_el(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 596

Strict Standards: Declaration of Walker_PageDropdown::start_el() should be compatible with Walker::start_el(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 613

Strict Standards: Declaration of Walker_Category::start_lvl() should be compatible with Walker::start_lvl(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 707

Strict Standards: Declaration of Walker_Category::end_lvl() should be compatible with Walker::end_lvl(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 707

Strict Standards: Declaration of Walker_Category::start_el() should be compatible with Walker::start_el(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 707

Strict Standards: Declaration of Walker_Category::end_el() should be compatible with Walker::end_el(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 707

Strict Standards: Declaration of Walker_CategoryDropdown::start_el() should be compatible with Walker::start_el(&$output) in /home/citiz68/public_html/javafxblogs/wp-includes/classes.php on line 730
JavaFX : 许愿树

JavaFX多用户在线应用实例:许愿树联机版

分类: JavaFX编程, 许愿树.
标签: ,

JavaFX作为RIA(Rich Internet Application)的开发语言,主要的特点在’R'和’I'两点上,即丰富的客户端和联网的应用。最近我把原来用JavaFX1.1写的许愿树程序改成了联机版,成为了一个3层架构应用,如下图所示,展现层是JavaFX实现的富客户端,中间层是Web Server和PHP,数据库层是MySQL。

这个新的许愿树程序可以让不同的人在许愿树上悬挂自己的愿望星,所有人看到的是同一棵树。你可以点击下图来试试这个最新联网版的许愿树啦,注意要输入email地址,以便以后修改愿望,每个人请最多留一个星星:

在JavaFX客户端的GUI部分,代码基本上和单机版的一样,已经在前面几篇文章中介绍了(参见文末的链接)。在和server通信的过程中采用了JavaFX 1.2的HttpRequest类来实现。Server的数据采用了JSON的格式(一种基于JavaScript的对象表达方式),在客户端可用javafx提供的PullParser类来解析。

首先,我们来看看客户端启动后首次加载数据的代码。在Main.fx中,我们有如下部分的代码:

var parser = PullParser {
    documentType: PullParser.JSON;

    onEvent: function(event: Event) {
      var content: String;
      var name: String;
      var id: String;
      var email: String;
      var color: Integer;
      var time: String;
      var location: String;

      // parse the JSON data and populate object
      if(event.type == PullParser.END_VALUE) {

        if(event.name == "name") {
          star.name = event.text;
        }
        else if ( event.name == "content" ){
          star.wish = event.text;
        }
        else if ( event.name == "id" ){
          star.id = event.text;
         }
        else if ( event.name == "color" ){
          star.whichColor = event.integerValue;
        }
        else if ( event.name == "time" ){
          star.time = event.text;
        }
        else if ( event.name == "location" ) {
          var pos = event.text.indexOf( "_" );
          var x = event.text.substring(0,pos);
          var y = event.text.substring( pos+1 );

          star.translateX = Float.parseFloat(x);
          star.translateY = Float.parseFloat(y);
        }
        else if ( event.name == "Star" ){
          star.changeStatus();
          star.onMousePressed = handleClick;

          insert star after stage.scene.content[currentIndex++];
          star = Star {};
        }

      }
    }
  }

  function getAllStar() {
    request = HttpRequest {
      method: HttpRequest.GET
      location: ServerConnector.serverURL1
      onInput: function(is: InputStream) {
        try {
          parser.input = is;
          parser.parse();
        } finally {
            is.close();
          }
      }
    }

    request.start();
 }

我们创建了一个PullParser的实例,用来分析JSON格式的数据(见后),然后在函数getAllStar()中,HttpRequest实例采用了GET的放式从服务器端获取幸运星的数据,代码很简单,只需要在onInput中把输入流传给parser即可。从Web服务器上传回的JSON数据格式如下,其中id是Server端数据库中唯一的标识,location是星星在窗口中的位置,color是星星的颜色编码,其它属性都比较一目了然:

{
 "Star": {
   "name" : "John Smith",
   "content" : "This is a wish",
   "id" : "2",
   "location" : "300.0_240.0",
   "time": "2009-08-21 17:38:37",
   "color": 3
 },
 "Star": {
   "name" : "Homer Simpson",
   "content" : "This is another wish",
   "id" : "3",
   "location" : "288.0_238.0",
   "time": "2009-07-18 18:38:28",
   "color": 4
 }
}

当客户端从Server数据库中获得了这些星星的数据之后,就可以把之前保存的星星和愿望在树上显示出来了。在用户增加一颗星星之后,客户端会把愿望的数据传到服务器保存起来,采用的是HTTP的POST方式,把相关数据发送出去。代码如下,

/*
 * ServerConnector.fx
 */
package wishtree;

import java.net.URLEncoder;
import wishtree.Star;
import java.io.*;
import javafx.io.http.*;

/**
 * @author Henry Zhang   http://www.javafxblogs.com
 */

var baseURL = "http://localhost:8888/wishtree/";

public var serverURL1 = "{baseURL}getallstar.php";
var serverURL2 = "{baseURL}savestar.php";

public class ServerConnector extends HttpRequest {

  var star : Star = null;
  var paramString : String ;

  override var onOutput = function( os: java.io.OutputStream): Void {
        try {
            os.write(paramString.getBytes());
        } finally {
            os.close();
        }
    };

  override var onInput = function(is: java.io.InputStream) {
     try {
           var br = new BufferedReader( new InputStreamReader( is ) );
           var line: String;

           while ( ( line=br.readLine() ) != null )
           {
             if ( (star != null) and (star.id == "")  )
                star.id = line;
            }
        } finally {
            is.close();
        }
  };

public function saveStar( s: Star ) {
  if ( s.id.length() == 0 )
    star = s;

  paramString = encode("name", s.name);
  paramString += "&{encode("id", s.id)}&" ;
  paramString += encode("location", "{s.translateX}_{s.translateY}" );
  paramString += "&{encode("content", s.wish )}";
  paramString += "&{encode("email", s.email)}" ;
  paramString += "&{encode("time", s.time)}&" ;
  paramString += encode("color", "{s.whichColor}" );

  headers = [
        HttpHeader {
            name: HttpHeader.CONTENT_LENGTH;
            value: "{paramString.getBytes().length}";
        }
       ];

  method = POST;
  location = serverURL2;
  start();
 }
}

function encode( k: String, v:String): String {
    var result = URLEncoder.encode( k, "UTF-8");
    var value = URLEncoder.encode( v, "UTF-8");
    result += "={value}"
}

服务器端的php代码比较简单,这里就不一一介绍了,有兴趣的可以下载代码看看,注意README.txt的说明。如果有什么问题,请留言。

相关文章:

JavaFX技巧:Java调用JavaFX例子

在JavaFX中的菜单

Swing应用中使用JavaFX的功能

用纯Java代码调用JavaFX的功能

JavaFX Multi-User Game Demo

其它:

NASCAR Diecast Car and Drivers Collectibles

NASCAR Driver Tony Stewart Diecast Car

NASCAR Diecast Car Jimmie Johnson NASCAR Diecast Car Dale Earnhardt

New US Citizenship Test Questions

Best Online US Citizenship Practice Test

US Citizenship Application Forms

评论 (0) 2009年08月18日

JavaFX许愿树程序:Effect功能的应用(3)

分类: JAVAFX技术, 许愿树.
标签:

在上篇文章中,我们可以在许愿树上悬挂许多愿望星了。现在,我们给程序增加些功能,可以在星星上记录许愿者的名字和愿望。你可以点击以下画面启动许愿树,点击许愿树后,可以看到一个弹出的窗口来输入姓名和愿望,点击“OK”确认。如果再次点击同一颗星星,就可以修改愿望的内容,快来试试看吧:(JDK1.5+或JDK 1.6)



点击启动许愿树程序


点击启动许愿树程序3

我们来看看如何增加一个半透明的输入窗口,代码在Dialog类中。首先,我们定义一个CustomNode。javafx.scene.CustomNode提供了用户自定义图形结点(Node)的方法,程序中需要继承该类,然后实现create()函数并返回自定义的Node。我们的CustomNode由一个Group实例来实现,包括一个蓝色半透明的外围框,两个输入框,两个文本以及一个“OK”按钮。Dialog.fx的代码如下:

( JAVAFX参考文章:
用JavaFX的Effect实现许愿树(1)
用JavaFX的Effect功能编写许愿树(1)
JavaFX 1.1和1.0的兼容性 http://blog.sina.com.cn/javafxcenter
JavaFX和Java之间的互操作性
Java和JavaFX的互操作性


/*
 * Dialog.fx
 *
 * http://www.javafxblogs.com
 */

package wishtree;

import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingTextField;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.layout.VBox;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.stage.Stage;

/**
 * @author Henry Zhang
 */

public class Dialog extends CustomNode {

  public var stage: Stage;
  public var star : Star;

  var w = bind stage.width;
  var h = bind stage.height;

  public var nameField : SwingTextField;
  public var wishField : SwingTextField;

  public function show( s: Star): Void {
    star = s;
    wishField.text = s.wish;
    nameField.text = s.name;
    visible = true;
  }

  public override function create(): Node {
    Group {
      blocksMouse: true;
      translateX: bind w / 6
      translateY: bind h / 4

      content: [
        Rectangle {
          stroke: Color.WHITE
          strokeWidth: 3
          width: bind w * 2 / 3
          height: bind h / 2
          fill : Color.BLUE;
          opacity: 0.5
          arcHeight: 10
          arcWidth: 10
        },
        // 本文发表自 http://www.javafxblogs.com
        VBox {
          spacing: 15
          translateX: bind w / 6
          translateY: bind h / 8
          content: [
            Text {
              content: "名字:"
              textOrigin: TextOrigin.TOP
              fill: Color.YELLOW
              font: Font.font ( null, FontWeight.BOLD, 20);
            },
            nameField = SwingTextField {
              text: "your name"
              width: bind w / 4
            },
            Text {
              content: "愿望:"
              textOrigin: TextOrigin.TOP
              fill: Color.YELLOW
              font: Font.font ( null, FontWeight.BOLD, 20);
            },
            wishField = SwingTextField {
              text:  "我希望..."
              width: bind w * .4
            },
            SwingButton {
              text: "OK"
              action: function():Void {
               star.name = nameField.text;
               star.wish = wishField.text;

               visible = false;
              }
            }
          ]
         },
     ]
   }
  }
}



在create()函数中,我们定义了圆角矩形的透明度为0.5,即可产生半透明的效果,arcHeight和arcWidth两个变量决定了圆角的大小。JavaFX技巧、编程和技术JavaFX Guy技术应用博客

 // http://www.javafxblogs.com
 Rectangle {
          stroke: Color.WHITE
          strokeWidth: 3
          width: bind w * 2 / 3
          height: bind h / 2
          fill : Color.BLUE;
          opacity: 0.5
          arcHeight: 10
          arcWidth: 10
        },

VBox的作用是把UI元素在垂直排列,相当于LayoutManager的作用。当点击OK按钮时,我们把对话框隐藏,并且把输入值赋给相关的Star对象。Group的属性中,我们设置了blocksMouse的属性,从而防止鼠标事件传导到下层的图形元素中。


接下来,我们把Main.fx略作修改,在生成Star实例时加入事件处理逻辑,使得星星在点击时可以弹出修改对话框,代码如下:

var tree : ImageView = ImageView {
  x: 80
  y: 0
  image: Image {
           url: "{__DIR__}images/tree.png"
           width: 640
           preserveRatio: true
         }
  onMousePressed:
    function(e:MouseEvent) : Void {

      // do nothing when the dialog is enabled
      if ( dialog.visible ) return;

      if ( e.y < 343 ) {
        currentStar = Star{
                  translateX: e.x
                  translateY: e.y

                  onMousePressed: function(e:MouseEvent) : Void {
                    // do nothing when the dialog is enabled
                    if ( dialog.visible ) return;

                    var self = e.node as Star;
                    dialog.show(self);
                  }
                 } ;

        dialog.show(currentStar);
        insert currentStar after stage.scene.content[currentIndex++];
      }
    }
};


至此,单机版的许愿树就完成了。代码可以在这里下载:许愿树代码。下一步,我们会把这个单机版的改成联网版,这样不同的用户可以通过网页在同一棵许愿树上许愿了。需要做的工作包括把用户的许愿请求发送到网站,然后存入数据库中,再通过许愿树展现出来。服务器端的代码可重用原来PHP版本的代码,或者新写都可以。

评论 (0) 2009年05月17日

JavaFX许愿树程序:Effect功能的应用(2)

分类: JavaFX编程, 许愿树.
标签: ,

在上篇文章“JavaFX许愿树程序:Effect功能的应用(1)”中,我们介绍了用JavaFX作出白云蓝天绿树的构图。接下来我们制作冒泡的动画,使得在白云中不断有气泡升起。然后我们再画上星星。


JAVAFX相关参考文章:
JavaFX 多维数组
用JavaFX的Effect功能编写许愿树(1)
JavaFX 1.1和1.0的兼容性
JavaFX和Java之间的互操作性


我们创建一个JavaFX类Bubble.fx,它是javafx.scene.shape.Circle类的子类,然后加上动画就可以实现气泡升腾的效果,我们一起来看看代码:


/*
 * Bubble.fx
 *
 * JavaFX技术交流  http://www.javafxblogs.com
 *
 */
package wishtree;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import java.lang.Math.*;

/**
 * @author Henry Zhang
 * JavaFX编程技巧及应用 http://www.javafxblogs.com
 */
public class Bubble extends Circle {

  var counter = 0;
  var startCounter = 10.0;

  var radiusDelta = -0.15;
  var destinationY:Double = 200;

  var timeline : Timeline =
    Timeline {
      repeatCount:   Timeline.INDEFINITE
      keyFrames : [
        KeyFrame {
          time : 100ms
          action: function () {
                    moveOneStep();
                  }
          }
      ]
    }

  init {
    effect = GaussianBlur { radius: 3}
    fill = Color.WHITE;

    radius = 4;
    resetStatus();
    timeline.play();
  }

  function moveOneStep() : Void {

    counter++;

    if ( counter > startCounter) {
      if ( not visible ) visible = true;

      centerY -= 2;
      radius += radiusDelta;

      if ( radius < 1 or radius > 4 )
        radiusDelta = -radiusDelta;
    }

    if ( centerY < destinationY+100 ) {
      visible = false;
      if ( opacity > 0.03 )
        opacity -= 0.03
      else
        opacity = 0;

      visible = true;
    }

    if ( centerY < destinationY ) {
      resetStatus();
    }
 } // JavaFX Game Demo   http://www.javafxgame.com

 function resetStatus(): Void {
   visible = false;
   centerY = 500 + random()*50;
   centerX = 200 + random()*350;
   counter = 0;
   startCounter = 80 + random()*400;

   destinationY = 150+random()*30;
   radius = 1 + random()*2;
   opacity = 0.6;
 }
}

首先,在init块和resetStatus()函数中我们初始化气泡的大小,透明度,位置等变量。我们在resetStatus()里调用了java.lang.Math里的随机函数random(),使得气泡的出现位置是随机的。从这可以看出JavaFX调用Java的便易性。在确定了初始状态后,我们定义一个动画的时间线(timeline)实例,里面只有一个关键帧(KeyFrame)。相关代码如下:

  var timeline : Timeline =
    Timeline {
      repeatCount:   Timeline.INDEFINITE
      keyFrames : [
        KeyFrame {
          time : 100ms
          action: function () {
                    moveOneStep();
                  }
          }
      ]
    }

. . . . 

  function moveOneStep() : Void {

    counter++;

    if ( counter > startCounter) {
      if ( not visible ) visible = true;

      centerY -= 2;
      radius += radiusDelta;

      if ( radius < 1 or radius > 4 )
        radiusDelta = -radiusDelta;
    }

    if ( centerY < destinationY+100 ) {
      visible = false;
      if ( opacity > 0.03 )
        opacity -= 0.03
      else
        opacity = 0;

      visible = true;
    }

    if ( centerY < destinationY ) {
      resetStatus();
    }
 }


每隔100ms,timeline都会调用处理函数moveOneStep(),从而可以不断改变气泡的状态,产生动画效果。在这个函数中,我们需要改变的实例变量有centerY,opacity,visible,radius。counter变量则是作为时间计数器,以控制状态的变化。这样,一个若隐若现、一张一弛不断上升的气泡就完成了。


下一步,我们再创建一个Star类,用来显示悬挂的“愿望星”。每颗星星可以看作是个10个结点连线组成的多边形,所以我们通过扩展Polygon类来编制星星的代码,一起来看看:

/*
 * Star.fx
 *
 * http://www.javafxblogs.com
 * http://developers.sun.com.cn/blog/henry
 */

package wishtree;

import javafx.scene.effect.DropShadow;
import javafx.scene.paint.*;
import javafx.scene.shape.Polygon;
import java.lang.Math.*;

/**
 * @author Henry Zhang
 *
 * http://www.javafxblogs.com
 *
 */

public class Star extends Polygon {
  def r1 : Double = 18;
  def r2 : Double = r1 / 1.6;

  var r = [r1, r2];

  var strokeColor =
    [ Color.PINK, Color.YELLOW, Color.GOLDENROD, Color.CYAN,
      Color.PURPLE, Color.BLUEVIOLET, Color.CORAL, Color.CRIMSON ];
  var fillColor =
    [ Color.GOLD, Color.BLUE, Color.RED, Color.DARKSLATEBLUE,
      Color.DARKORANGE, Color.MAGENTA, Color.BROWN, Color.CHOCOLATE ];

  init {
    // compute the coordinates of the star polygon
    points = for ( i in [0..9] ) [
      r[i mod 2] * cos( toRadians(i*36) ),
      r[i mod 2] * sin( toRadians(i*36) )
    ];

    blocksMouse = true;
    strokeWidth = 1; // 本文 发 表 于 http://www.javafxblogs.com/
    var which : Integer = random() * sizeof(strokeColor) as Integer;

    stroke = strokeColor[which];

    fill = LinearGradient {
             startX: 0 startY: 0  endX: 0  endY: 1.0
             proportional: true
             stops: [
                     Stop { offset: 0.1 color: Color.WHITE }
                     Stop { offset: 1.0 color: fillColor[which] }
                    ]
           };
    effect = DropShadow { color: Color.WHITE };
  }
}

在init块中,我们用for语句来计算星星的10个接点,每次循环产生一个节点的(x,y)坐标,共计10组,最后产生的一个sequence赋值给points变量,我们可以看到JavaFX的代码非常简洁:

    points = for ( i in [0..9] ) [
      r[i mod 2] * cos( toRadians(i*36) ),
      r[i mod 2] * sin( toRadians(i*36) )
    ];


fill变量我们再次采用了LinearGradient的渐变效果,填充的颜色是随机选择的。我们还增加了DropShadow的影子效果,在星星的背后加上了白色的影子,增强了视觉效果。blocksMouse属性是屏蔽鼠标事件传给星星下面的其他图形。


这个星星类产生的实例位置都是在坐标(0,0)的位置,在下面的程序中,我们会通过改变translateX和translateY的值,动态地把星星挂在树上。


我们对Main.fx略为修改一下,首先把数个泡泡加入scene中:

     for ( i in [1..5] ) Bubble { }


然后在许愿树的图形上增加鼠标点击的处理,每当点击到树叶和树枝时,会在该处挂上一颗星星,在鼠标事件(MouseEvent)中,x,y属性可以告诉我们点击的位置,然后我们就可以设置星星出现的地方。通过insert操作,我们把新创建的星星增加到scene中。注意,这里采用了currentIndex计数器来确保星星是出现在云层下方,我们可以看到被云层遮掩的星星:

var tree : ImageView = ImageView {
  x: 80
  y: 0
  image: Image {
           url: "{__DIR__}images/tree.png"
           width: 640
           preserveRatio: true
         }
  onMousePressed:
    function(e:MouseEvent) : Void {
      if ( e.y < 343 ) {
        var s = Star{ translateX: e.x translateY: e.y } ;
        insert s after stage.scene.content[currentIndex++];
      }
    }
};


完整的代码如下:

/*
 * Main.fx
 *
 * http://www.javafxblogs.com
 *
 */
package wishtree;

import javafx.scene.effect.GaussianBlur;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;

/**
 * @author Henry Zhang
 * JavaFX编程 http://javafxguy.javaeye.com
 * JavaFX技术交流应用 http://www.javafxblogs.com
 */

var xx = 170;
var yy = 430;

var currentIndex = 0;

var tree : ImageView = ImageView {
   x: 80
   y: 0
   image: Image {
            url: "{__DIR__}images/tree.png"
            width: 640
            preserveRatio: true
          }
  onMousePressed:
    function(e:MouseEvent) : Void {
      if ( e.y < 343 ) {
        var s = Star{ translateX: e.x translateY: e.y } ;
        insert s after stage.scene.content[currentIndex++];
      }
    }
  };

var tree2 =
  Image {
    url: "{__DIR__}images/tree2.png"
    width: 130
    preserveRatio: true
 };

var stage =
  Stage {
    title: "Wish Tree"
    width: 830
    height: 620
    resizable: false

    scene: Scene {
             fill: LinearGradient {
                     startX: 0,
                     startY: 0.0,
                     endX: 0,
                     endY: 1.0
                     proportional: true
                     stops: [ Stop { offset: 0.0 color: Color.web("#14acf8") },
                              Stop { offset: 1.0 color: Color.web("#a4e6f8") }]
                   }

    content: [
      ImageView {
        x: 50
        y: 280
        image: Image {
                 url: "{__DIR__}images/cloud2.png"
                 preserveRatio: true
                 width: 160
               }
        effect: GaussianBlur { radius: 25 }
      },
      ImageView {
        x: 520
        y: 300
        image: Image {
            url: "{__DIR__}images/cloud2.png"
            preserveRatio: true
            width: 150
            }
        effect:  GaussianBlur { radius: 33 }
      }, 

      ImageView {
        x: 560
        y: 500
        image: tree2
      },
      ImageView {
        x: 460
        y: 470
        image: tree2
      }, // JavaFX Sample code http://www.javafxgame.com
      ImageView {
        x: 60
        y: 500
        image: tree2
      },
      ImageView {
        x: 190
        y: 470
        image: tree2
      },
      tree,
      ImageView {
        x: 120
        y: 360
        image: Image {
          url: "{__DIR__}images/sign.png"
          width: 180
          preserveRatio: true
        }
      },
      ImageView {
        x: 20
        y: 60
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 250
        }
        effect: GaussianBlur { radius: 40 }
      }, // United States American Citizenship Test
      ImageView {
        x: 520
        y: 90
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 100
        }
        effect:  GaussianBlur { radius: 20}
      }, // United States Citizenship Application
      ImageView {
        x: 620
        y: 190
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 180
        }
        effect:  GaussianBlur { radius: 25}
      },
      ImageView {
        x: 600
        y: 380
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 150
        }
        effect:  GaussianBlur { radius: 33 }
      },
      ImageView {
        x: 0
        y: 490
        image: Image {
          url: "{__DIR__}images/cloud.png"
        }
        effect:  GaussianBlur { radius: 30 }
      },
      for ( i in [1..5] ) Bubble { },
      Text {
        x:170
        y: 450
        content: "许愿树"
        fill: Color.YELLOW
        font: Font.font ( null, FontWeight.BOLD, 30);
        effect: PerspectiveTransform {
                  ulx: xx    uly: yy
                  urx: xx+80 ury: yy-10
                  llx: xx-2  lly: yy+40
                  lrx: xx+80 lry: yy+38
                }
        }
      ]
    }
}

//用JavaFX的Effect功能编写许愿树程序
var idx =
  for ( n in stage.scene.content )
    if ( n == tree ) indexof n
    else [] ;

currentIndex = idx[0];

现在代码基本完整了,每次点击愿望树,都会生成一个五彩的星星,快来点击下面点击下面截图挂颗愿望星吧(JDK 1.5以上的环境,最好是JDK1.6 U10以上):



点击启动许愿树程序


点击启动许愿树程序



在下一篇文章中,我们介绍如何把愿望写在星星上。

( 未完待续, 完整代码可在下一篇文章中下载 )

评论 (0) 2009年05月15日

JavaFX许愿树程序:Effect功能的应用(1)

分类: JAVAFX技术, 许愿树.
标签: ,

看了同事用写了个许愿树程序(C/S架构的),其中client端的图形界面是由flash来实现。那么如果这个界面用JavaFX来写会怎样呢?我想这一定是很有趣的事情,于是就决定动手写个程序,比较一下。


JavaFX博客:http://www.javafxblogs.com/


首先写个主程序,把画面的Scene做出来。在这个画面中,蓝天背景和白云都需要使用图形效果(effect),在JavaFX中支持了多种图形效果,Lighting,Blur,Shadow等等,可以大大丰富我们的用户界面。我们的程序采用线性渐变(LinearGradient)效果来画蓝天,用高斯模糊(GaussianBlur)效果来画白云,程序如下:

/*
 * Main.fx
 *
 * http://www.javafxblogs.com
 *
 */
package wishtree;

import javafx.scene.effect.GaussianBlur;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;

/**
 * @author Henry Zhang
 * JavaFX编程  http://developers.sun.com.cn/blog/henry
 * JavaFX技术交流应用  http://www.javafxblogs.com
 */

var xx = 170;
var yy = 430;

var tree : ImageView = ImageView {
   x: 80
   y: 0
   image: Image {
            url: "{__DIR__}images/tree.png"
            width: 640
            preserveRatio: true
          }
   };

var tree2 =
  Image {
    url: "{__DIR__}images/tree2.png"
    width: 130
    preserveRatio: true
 };

var stage =
  Stage {
    title: "Wish Tree"
    width: 830
    height: 620
    resizable: false

    scene: Scene {
           fill: LinearGradient {
                  startX: 0,
                  startY: 0.0,
                  endX: 0,
                  endY: 1.0
                  proportional: true
                  stops: [
                    Stop { offset: 0.0
                           color: Color.web("#14acf8") },
                    Stop { offset: 1.0
                           color: Color.web("#a4e6f8") }
                         ]
                   }

    content: [
      ImageView {
        x: 50
        y: 280
        image: Image {
                 url: "{__DIR__}images/cloud2.png"
                 preserveRatio: true
                 width: 160
               }
        effect: GaussianBlur { radius: 25 }
      },
      ImageView {
        x: 520
        y: 300
        image: Image {
            url: "{__DIR__}images/cloud2.png"
            preserveRatio: true
            width: 150
            }
        effect:  GaussianBlur { radius: 33 }
      }, // http://www.javafxgame.com  JavaFX Demo Game

      ImageView {
        x: 560
        y: 500
        image: tree2
      },
      ImageView {
        x: 460
        y: 470
        image: tree2
     }, // http://www.compare-review-information.com JavaFX Sample code
      ImageView {
        x: 60
        y: 500
        image: tree2
      },
      ImageView {
        x: 190
        y: 470
        image: tree2
      },

      tree,

      ImageView {
        x: 120
        y: 360
        image: Image {
          url: "{__DIR__}images/sign.png"
          width: 180
          preserveRatio: true
        }
      },
      ImageView {
        x: 20
        y: 60
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 250
        }
        effect: GaussianBlur { radius: 40 }
      },
      ImageView {
        x: 520
        y: 90
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 100
        }
        effect:  GaussianBlur { radius: 20}
      },
      ImageView {
        x: 620
        y: 190
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 180
        }
        effect:  GaussianBlur { radius: 25}
      },
      ImageView {
        x: 600
        y: 380
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 150
        }
        effect:  GaussianBlur { radius: 33 }
      },
      ImageView {
        x: 0
        y: 490
        image: Image {
          url: "{__DIR__}images/cloud.png"
        }
        effect:  GaussianBlur { radius: 30 }
      },
      Text {
        x:170
        y: 450
        content: "许愿树"
        fill: Color.YELLOW
        font: Font.font ( null, FontWeight.BOLD, 30);
        effect: PerspectiveTransform {
                  ulx: xx    uly: yy
                  urx: xx+80 ury: yy-10
                  llx: xx-2  lly: yy+40
                  lrx: xx+80 lry: yy+38
                }
        }
      ]
    }
}


在程序中,我们把Stage的scene属性用渐变效果填充,可以得到蓝天的效果。在这个渐变效果中,startX和endX的值一样,startY和endY的值不同,因此是沿着Y方向(竖直)的渐变方式。起始颜色为深蓝(”#14acf8″),结束颜色为浅蓝(”#a4e6f8″),在整个Stage的Y方向均匀变化,相关代码如下:

       fill: LinearGradient {
               startX: 0
               startY: 0
               endX: 0
               endY: 1.0
               proportional: true
               stops: [ Stop { offset: 0.0 color: Color.web("#14acf8") },
                        Stop { offset: 1.0 color: Color.web("#a4e6f8") }]
             }

白云的模糊效果是通过高斯算法来获取,例如在下面代码中,ImageView的effect属性:
GaussianBlur{ radius: 40 },radius表示模糊渐变发范围的大小,数值越大,模糊度越大。

      ImageView {
        x: 20
        y: 60
        image: Image {
          url: "{__DIR__}images/cloud2.png"
          preserveRatio: true
          width: 250
        }
        effect:  GaussianBlur { radius: 40 }
      }


由于我们需要显示几朵不同大小的白云,我们需要把白云Image(cloud2.png)作大小调整,上面码中,我们通过改变width的值,并设置preserveRatio=true来保证长宽比例不变,这样就“创造”出不同大小的云块。相关代码段如下:( 本文见:http://www.javafxblogs.com )


  preserveRatio: true
  width: 250


还有要说明的一点是,在木牌子上的“许愿树”3个字,我们采用了维度变换( PerspectiveTransform )的效果,可以把2维的文字变成具有3维感觉的字形,呈梯形状。在PerspectiveTransform需要定义梯形的4个角的坐标,从而把原来长方形的区域映射成变形的字体。相关代码如下:

 Text {
        x:170
        y: 450
        content: "许愿树"
        fill: Color.YELLOW
        font: Font.font ( null, FontWeight.BOLD, 30);
        effect: PerspectiveTransform {
                  ulx: xx    uly: yy
                  urx: xx+80 ury: yy-10
                  llx: xx-2  lly: yy+40
                  lrx: xx+80 lry: yy+38
                }
        }


好了,到这里许愿树程序的界面就完成了,在下一篇文章中会继续介绍如何在树上“悬挂”愿望星星。如果你有JDK 1.5以上的环境(JDK1.6更佳),快点击下图来看看这个JavaFX版本的“愿望树”吧。当你的浏览器首次运行JavaFX程序时,会有一些等待下载的时间。



点击启动WishTree程序

点击启动程序

参见:http://www.javafxblogs.com( 未完待续, 完整代码可在下一篇文章中下载 )

评论 (0) 2009年05月12日