123
返回列表 发新帖
楼主: Sky-Tiger

Jump into JavaFX, Part 3: The basic APIs

[复制链接]
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
21#
 楼主| 发表于 2009-2-24 22:51 | 只看该作者
Playing with Shapes

The javafx.scene.geometry package includes an abstract Shape class that serves as the base class for various geometric shape subclasses (such as Circle, Line, and Path). Because this class subclasses Node, it inherits Node's attributes and functions, while introducing the following attributes of its own:

    * fill (of type Paint) specifies this shape's background paint -- the default value is null.
    * smooth (of type Boolean) specifies whether or not antialiasing hints are used for this shape -- the default value is true (use antialiasing hints).
    * stroke (of type Paint) specifies this shape's outline paint -- the default value is null.
    * strokeDashArray (of type Number[]) specifies a sequence representing the lengths of the dash segments. Alternate sequence entries represent the user space lengths of the opaque and transparent segments of the dashes. As the pen moves along the outline of this shape, the user space distance that the pen travels is accumulated. The distance value is used to index into the dash sequence. The pen is opaque when its current cumulative distance maps to an even element of the dash sequence, and transparent otherwise. The default value is [1.0] (a one-element sequence containing 1.0).
    * strokeDashOffset (of type Number) specifies a distance in user coordinates that represents an offset into the dashing pattern -- the point in the dashing pattern that will correspond to the beginning of the stroke. The default value is 0.0.
    * strokeLineCap (of type javafx.scene.geometry.StrokeLineCap) specifies the end cap style of this shape as one of StrokeLineCap.BUTT (end unclosed subpaths and dash segments with no added decoration), StrokeLineCap.ROUND (end unclosed subpaths and dash segments with a round decoration that has a radius equal to half of the width of the pen), and StrokeLineCap.SQUARE (end unclosed subpaths and dash segments with a square projection that extends beyond the end of the segment to a distance equal to half of the line width). The default value is StrokeLineCap.SQUARE.
    * strokeLineJoin (of type javafx.scene.geometry.StrokeLineJoin) specifies the decoration applied where path segments meet as one of StrokeLineJoin.BEVEL (join path segments by connecting the outer corners of their wide outlines with a straight segment), StrokeLineJoin.MITER (join path segments by extending their outside edges until they meet), and StrokeLineJoin.ROUND (join path segments by rounding off the corner at a radius of half the line width). The default value is StrokeLineJoin.MITER.
    * strokeMiterLimit (of type Number) specifies the limit for the StrokeLineJoin.MITER line join style -- the default value is 10.0.
    * strokeWidth (of type Number) specifies a square pen line width -- the default value is 1.0.

Along with Circle, Line, and Path, and additional shape classes such as Arc, CubicCurve, Polygon, and Rectangle, javafx.scene.geometry provides PathElement. This abstract class is the base class for LineTo, MoveTo, and other classes that describe a Path's elements.

Let's play with some of these shape classes. Start NetBeans and use the New Project wizard to introduce a new APIDemo3 project with apidemo3.Shapes as the project's main file. Then replace the skeletal Shapes.fx's // place your code here line with the script in Listing 10.
Listing 10. Shapes.fx

/*
* Shapes.fx
*
*/

package apidemo3;

/**
* @author Jeff Friesen
*/

import java.lang.Math;
import java.lang.System;

import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;

import javafx.application.Frame;
import javafx.application.Stage;

import javafx.scene.geometry.Arc;
import javafx.scene.geometry.ArcType;
import javafx.scene.geometry.Circle;
import javafx.scene.geometry.CubicCurve;
import javafx.scene.geometry.Ellipse;
import javafx.scene.geometry.Line;
import javafx.scene.geometry.QuadCurve;
import javafx.scene.geometry.Rectangle;
import javafx.scene.geometry.Shape;

import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;

class Model
{
    attribute width: Integer;
    attribute height: Integer;
    attribute shape: Shape;
   
    private attribute opacity: Number;

    private function rnd (limit: Integer): Integer
    {
        Math.random ()*limit as Integer
    }
   
    private function arc (): Shape
    {
        Arc
        {
            centerX: rnd (width)
            centerY: rnd (height)
            radiusX: rnd (Math.min (width/2, height/2))
            radiusY: rnd (Math.min (width/2, height/2))
            startAngle: rnd (180)
            length: rnd (180)
            type: ArcType.ROUND
            fill: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity      
        }
    }

    private function circle (): Shape
    {
        Circle
        {
            centerX: rnd (width)
            centerY: rnd (height)
            radius: rnd (Math.min (width/2, height/2))
            fill: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity
        }
    }

    private function cubicCurve (): Shape
    {
        CubicCurve
        {
            startX: rnd (width)
            startY: rnd (height)
            controlX1: rnd (width)
            controlY1: rnd (width)
            controlX2: rnd (width)
            controlY2: rnd (width)
            endX: rnd (width/2)
            endY: rnd (height/2)
            fill: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity
        }
    }

    private function ellipse (): Shape
    {
        Ellipse
        {
            centerX: rnd (width)
            centerY: rnd (height)
            radiusX: rnd (Math.min (width/2, height/2))
            radiusY: rnd (Math.min (width/2, height/2))
            fill: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity
        }
    }

    private function line (): Shape
    {
        Line
        {
            startX: rnd (width)
            startY: rnd (height)
            endX: rnd (width)
            endY: rnd (height)
            stroke: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity
        }
    }

    private function quadCurve (): Shape
    {
        QuadCurve
        {
            startX: rnd (width)
            startY: rnd (height)
            endX: rnd (width)
            endY: rnd (height)
            controlX: rnd (width)
            controlY: rnd (height)
            fill: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity
        }
    }

    function rectangle (): Shape
    {
        Rectangle
        {
            x: rnd (width)
            y: rnd (height)
            width: rnd (width)
            height: rnd (height)
            arcWidth: rnd (10)
            arcHeight: rnd (10)
            fill: Color.rgb (rnd (256), rnd (256), rnd (256))
            opacity: bind opacity
        }
    }

    private attribute shapes = [arc, circle, cubicCurve, ellipse, line,
                                quadCurve, rectangle];
                              
    private attribute timeline = Timeline
    {
        repeatCount: Timeline.INDEFINITE
        keyFrames: for (i in [1..sizeof shapes])
        {
            KeyFrame
            {
                time: 3s*indexof i
                action: function ()
                {
                    shape = shapes [rnd (sizeof shapes)] ()
                }

                timelines:
                [
                    Timeline
                    {
                        autoReverse: true
                        repeatCount: 2
                                 
                        var begin = at (0s)
                        {
                            opacity => 0.0
                        }

                        var end = at (1s)
                        {
                            opacity => 1.0 tween Interpolator.LINEAR
                        }
               
                        keyFrames: [begin, end]
                    }
                ]
            }
        }
    }
   
    attribute animate: Boolean on replace o=n
    {
        if (n == true)
            timeline.start ()
        else
            timeline.stop ()
    }
}

Frame
{
    var model = Model
    {
        width: 300
        height: 300
        animate: true
    }
   
    title: "Shapes Tour"
   
    width: bind model.width with inverse
    height: bind model.height with inverse
   
    stage: Stage
    {
        fill: LinearGradient
        {
            startX: 0.0
            startY: 0.0
            endX: 0.0
            endY: 1.0
            stops:
            [
                Stop { offset: 0.0 color: Color.YELLOW },
                Stop { offset: 1.0 color: Color.CYAN }
            ]
        }
                    
        content: bind [model.shape]
    }

    closeAction: function ()
    {
        model.animate = false;
        System.exit (0)
    }
   
    visible: true
}

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
22#
 楼主| 发表于 2009-2-24 22:51 | 只看该作者
Unlike the Hello, JavaFX! script listing, Listing 10 encapsulates its keyframe animation code in the Model class, and instantiates this class within the Frame literal. These encapsulations lead to a script consisting only of classes, which seems to be preferred (judging from various scripts that you can find on the Internet) over mixing classes and extraneous global code.

The Model class presents a public interface consisting of attributes width, height, shape, and animate. The Frame literal binds its width and height attributes to the first two attributes, specifying with inverse to inform the model whenever the user changes the window's dimensions.

Frame also binds to Model's shape attribute, to be informed when a new Shape subclass instance is assigned to this attribute -- a new instance is assigned every three seconds -- so that the shape can be displayed. To start the animation that's responsible for generating shapes, assign true to animate; assign false instead to terminate the animation.

Model's private implementation demonstrates the useful function pointers language feature. The private attribute shapes = [arc, circle, cubicCurve, ellipse, line, quadCurve, rectangle]; declaration stores pointers to shape-creation functions, which are later invoked via the shape = shapes [rnd (sizeof shapes)] () expression.

The private implementation also demonstrates nested timelines. After the action() function assigns a randomly chosen and created shape to shape, the sub-timeline stored in Timeline's timelines sequence attribute starts to animate opacity from invisible to fully visible, and then in reverse. A shape is faded in, and then faded out before fading in another shape, as Figure 1 reveals.

使用道具 举报

回复
论坛徽章:
350
2006年度最佳版主
日期:2007-01-24 12:56:49NBA大富翁
日期:2008-04-21 22:57:29地主之星
日期:2008-11-17 19:37:352008年度最佳版主
日期:2009-03-26 09:33:53股神
日期:2009-04-01 10:05:56NBA季后赛大富翁
日期:2009-06-16 11:48:01NBA季后赛大富翁
日期:2009-06-16 11:48:01ITPUB年度最佳版主
日期:2011-04-08 18:37:09ITPUB年度最佳版主
日期:2011-12-28 15:24:18ITPUB年度最佳技术原创精华奖
日期:2012-03-13 17:12:05
23#
 楼主| 发表于 2009-2-24 22:52 | 只看该作者
Along with the previous classes, javafx.scene.geometry provides the SVGPath class, which was revealed in the shapes.fx, shapes.fxd, and shapesUI.fx listings from Jump into JavaFX, Part 1. This node and shape class describes a Scalable Vector Graphics path via an encoded string that corresponds to the specification at http://www.w3.org/TR/SVG/paths.html.

By now, it should be obvious that Text is a Shape subclass (in addition to being a Node subclass -- JavaFX Script supports multiple inheritance).
Images

In addition to text and shapes, JavaFX supports images, via the javafx.scene.image package and its Image and ImageView classes. You'll use the former class to load an image, and the latter class to view the image. Because ImageView subclasses Node, anything you can generally do to nodes also applies to images. Listing 11 demonstrates these classes.
Listing 11. ScaledImage.fx

/*
* ScaledImage.fx
*
*/

package apidemo4;

/**
* @author Jeff Friesen
*/

import javafx.animation.Interpolator;
import javafx.animation.Timeline;

import javafx.application.Frame;
import javafx.application.Stage;

import javafx.input.MouseEvent;

import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

import javafx.scene.paint.Color;

class Model
{
    attribute url: String;
    attribute scale: Number = 1.0;
   
    private attribute timelineShrinkEnlarge = Timeline
    {
        toggle: true
                        
        var begin = at (0s)
        {
            scale => 1.0
        }

        var end = at (1s)
        {
            scale => 0.1 tween Interpolator.LINEAR
        }
                       
        keyFrames: [begin, end]
    }
  
    function animate ()
    {
        timelineShrinkEnlarge.start ()
    }
}

Frame
{
    var model = Model
    {
        url: "{__DIR__}3frogs.jpg"
    }
   
    width: 400
    height: 400
   
    var stageRef: Stage
    stage: stageRef = Stage
    {
        fill: Color.BLACK
        
        content: ImageView
        {
            var imageRef: Image
            image: bind imageRef = Image
            {
                url: model.url
            }
                             
            x: bind (stageRef.width-imageRef.width)/2
            y: bind (stageRef.height-imageRef.height)/2

            scaleX: bind model.scale
            scaleY: bind model.scale
                     
            onMouseEntered: function (me: MouseEvent)
            {
                model.animate ()
            }
        }
    }
        
    visible: true
}

This script declares a Model class whose url and scale attributes respectively identify the location and name of an image to load, and a scaling percentage for shrinking/enlarging the image -- scale is initialized to 1.0 so that the loaded image is initially displayed at normal size. The animate() function performs an animation cycle each time it's invoked.

The url attribute's initializer relies on a special precreated variable named __DIR__ (of type java.net.URL) to return the absolute path of the directory containing the script's equivalent classfile, and appends 3frogs.jpg to this location. For example, on my Windows XP platform this image's complete URL (which I've split across two lines for readability) is

file:/C:/Documents%20and%20Settings/Jeff%20Friesen/My%20Documents/NetBeansProjects/
  APIDemo4/build/classes/apidemo4/3frogs.jpg

Moving on, the script creates the user interface window, and creates and stores a single ImageView component in this window's stage's content sequence attribute. (The [] delimiter characters don't need to appear around one-element sequences.)

ImageView's image attribute binds to an Image instance that identifies the image to be displayed. Whenever Image's String-based url attribute changes, such as when Model's url attribute is first initialized, a new Image instance is created and loads the model-specified image, and the resulting image appears.

The anonymous function assigned to the onMouseEntered attribute is invoked each time the mouse cursor enters the image view node. This function starts an animation that, because true is assigned to Timeline's toggle attribute, alternates between shrinking the loaded image to 10% of its initial size, and enlarging the image back to its initial size.

Create an APIDemo4 project, specifying apidemo4 as the package name, and ScaledImage as the filename. After replacing the skeletal script with the contents of Listing 11, copy the 3frogs.jpg file from this article's code archive into the directory identified by __DIR__. Compile and run ScaledImage.fx. Figure 2 reveals the unshrunken image.

Move the mouse cursor over the image to shrink it toward the upper-left corner.
Figure 2. Move the mouse cursor over the image to shrink it toward the upper-left corner. (Click to enlarge.)

Images are loaded in the background. Although this process is quite fast for disk-based images, it tends to be slower when loading over a network. To help with slow image loading, Image provides backgroundLoading and placeholder attributes (for displaying an alternate image until the image has fully loaded), and a progress attribute (for determining how much is left to load).
In conclusion

JavaFX provides a good assortment of APIs for creating scenes that are visually compelling. Now that you're acquainted with some of these APIs, you should find it easier to understand what's going on with the Hello, World! script from "Jump into JavaFX, Part 1."

There still are more APIs to explore, so be sure to look for the fourth installment in this series, where I'll introduce the JavaFX APIs for dealing with effects, layouts, media, Swing components, and more.

使用道具 举报

回复
论坛徽章:
2
24#
发表于 2009-3-16 00:10 | 只看该作者
辛苦了,顶一下吧

使用道具 举报

回复
论坛徽章:
4
25#
发表于 2009-3-16 14:50 | 只看该作者
不可以乱顶的

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表