Java fx 坐标揭秘

scene graph
一张呈现为树结构的数据结构,java fx 的渲染系统 是通过该数据结构来完成图形的渲染。

  • 所有在树结构的每一个节点称为一个node
  • 根节点(root)是唯一一个没有父母节点的node
  • 没有子节点的node的称为leaf node(叶子节点)

1 . 在scene graph中的每一个node,都有自己的笛卡尔坐标系

Paste_Image.png

2 . 每个node 的视图“呈现”除了几何图形外,其实还包括effect(特效),clip(裁剪),transformation(变换)等特性


3 . Fx 针对每个node 对所包含的特性,提供了不同bound框来囊括这些特性,不同bound的范围

  • layoutBounds : node geometry
  • boundsInLocal: node geometry + effects + clip
  • boundsInParent: node geometry + effects + clip + transformations
Paste_Image.png
public class BoundTest extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox root  = new VBox();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        
        // 1. only a simple button
        System.out.println("only a simple button");
        Button button1 = new Button();
        System.out.println("button1=" + button1.getLayoutBounds());
        System.out.println("button1=" + button1.getBoundsInLocal());
        System.out.println("button1=" + button1.getBoundsInParent());
        
        System.out.println();
        System.out.println("add shadow");
        // 2. add shadow
        Button button2 = new Button();
        button2.setEffect(new BoxBlur());
        System.out.println("button2=" + button2.getLayoutBounds());
        System.out.println("button2=" + button2.getBoundsInLocal());
        System.out.println("button2=" + button2.getBoundsInParent());
        
        System.out.println();
        System.out.println("add transformation");
        // 3. add transformation
        Button button3 = new Button();
        //button3.setRotate(60);
        button3.getTransforms().add(new Translate(150, 75));
        System.out.println("button3=" + button3.getLayoutBounds());
        System.out.println("button3=" + button3.getBoundsInLocal());
        System.out.println("button3=" + button3.getBoundsInParent());
        
        root.getChildren().addAll(button1, new Group(button2), button3);
        //primaryStage.show();
    }
}

4 . 别名

  • Layout Bounds: 逻辑bound(logic bound)
  • BoundsInLocal: 现实上的bound(physical bound) (希望当前node包含Effect,Clip时候用的,另一种情况检测两个node有无碰撞)
  • BoundsInParent: 虚幻的bound(visual bound) (较少在code中使用到)

5 .这些Bound 处于不同的坐标系中
Layout Bounds: 在该node 的坐标系
BoundsInLocas:在该node 的坐标系
BoundsInParent:在该node 的parent的坐标系
了解这些不同的Bounds


6 . 每一个node 都有一对LayoutX和LayoutY以及一对translateX和translateY 来描述他们在坐标系中的位置
LayoutX 和LayoutY : 固定(stable) layout
translateX和translateY: 动态(dynamic) layout (animaiton中)
finalTranslationX = layoutX + translateX
finalTranslationY = layoutY + translateY
但是layoutX and layoutY 并不是就是直接确定了node的位置,它通常需要转为node的坐标系中的位置,通常需要补偿一个layoutBound.minX和layoutBound.minY

layoutX = finalX - node.getLayoutBounds().getMinX();
layoutY = finalY - node.getLayoutBounds().getMinY();

7 . 由于Bound 的存在,当你想设置坐标时候,记得加上边框左上角的位置的横纵坐标,推荐使用relocate() ,它会帮你补偿minX 和minY of layoutBound,自动完成6中提到的转换

8 . 当Parenty 为region 类时候,父节点有自己的位置策略,这时候忽略node layoutX,layoutY. 事实上只有当你的parent节点(layout manager)为Group或着Pane的时候,通过修改layoutX以及layoutY才能修改Node的位置



Node 的Size

  1. 当一个node可以随着父节点layout的调整自己的大小,该节点就是可以resize的.Node 的分为resizable和non-resize node.

    • re-sizable node: Regions, Controls, andWebView
    • non-resizable node : Group, Text, and Shapes
  2. 三个“大小”

    • Preferred size 最合适大小,当layout 足够满足的时候呈现的size
    • Minimum size 最小的大小,当resize的时候的最小大小
    • Maximum size 最大的大小, 当resize的时候的最大大小
  3. 对着三个size可以设置两个常量,
    USE_COMPUTED_SIZE :依据的node的内容和属性计算
    USE_PREF_SIZE:通常用来设置Minimum size和Maximum size保持和Preferred size一致

  4. 每个node也有6个属性来对应这个三个特性,这些属性的默认值为:USE_COMPUTED_SIZE
    prefWidth,prefHeight
    minWidth,minHeight
    maxWidth,maxHeight

  5. 不要相信getXXX(getMaxWidth,getMinWidth,getPre...),通常这些方法返回的值并没有真实反映node的大小

  6. 使用以下方法:

double prefWidth(double height) 
double prefHeight(double width)
double minWidth(double height)
double minHeight(double width)
double maxWidth(double height)
double maxHeight(double width) 

通过传入-1来获得content bias 边的长度,再根据该长度算得相应的宽或者高


Paste_Image.png
public class NodeSizes extends Application {
    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        Button btn = new Button("Hello JavaFX!");

        HBox root = new HBox();
        root.getChildren().addAll(btn);

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Sizes of a Node");
        stage.show();

        // Print button's sizes
        System.out.println("Before changing button properties:");
        printSizes(btn);

        // Change button's properties
        btn.setWrapText(true);
        btn.setPrefWidth(80);
        stage.sizeToScene();

        // Print button's sizes
        System.out.println("\nAfter changing button properties:");
        printSizes(btn);

    }

    public void printSizes(Button btn) {
        System.out.println("btn.getContentBias() = " + btn.getContentBias());

        System.out.println("btn.getPrefWidth() = " + btn.getPrefWidth() + 
                           ", btn.getPrefHeight() = " + btn.getPrefHeight());

        System.out.println("btn.getMinWidth() = " + btn.getMinWidth() + 
                           ", btn.getMinHeight() = " + btn.getMinHeight());

        System.out.println("btn.getMaxWidth() = " + btn.getMaxWidth() + 
                           ", btn.getMaxHeight() = " + btn.getMaxHeight());

        double prefWidth = btn.prefWidth(-1);
        System.out.println("btn.prefWidth(-1) = " + prefWidth + 
               ", btn.prefHeight(prefWidth) = " + btn.prefHeight(prefWidth));

        double minWidth = btn.minWidth(-1);
        System.out.println("btn.minWidth(-1) = " + minWidth + 
               ", btn.minHeight(minWidth) = " + btn.minHeight(minWidth));

        double maxWidth = btn.maxWidth(-1);
        System.out.println("btn.maxWidth(-1) = " + maxWidth + 
               ", btn.maxHeight(maxWidth) = " + btn.maxHeight(maxWidth));

        System.out.println("btn.getWidth() = " + btn.getWidth() + 
                           ", btn.getHeight() = " + btn.getHeight());
    }
}

7 对于non-resizable node
prefWidth(double h), minWidth(double h), maxWidth(double h) 返回layoutBound的width
prefHeight(double w), minHeight(double w), maxHeight(double w) 返回layoutBound的height
8 关于managed node
当子节点的managedProperty设置为false的时候,父节点在layout布局的时候,就不会帮把计算如layoutboundbox. 这与visibleProperty的区别,尽管它不可见,父节点仍考虑它的布局。

public class SlidingLeftNodeTest extends Application {
    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        Button b1 = new Button("B1");
        Button b2 = new Button("B2");
        Button b3 = new Button("B3");
        Button visibleBtn = new Button("Make Invisible");
        // Add an action listener to the button to make b2 visible
        // if it is invisible and invisible if it is visible
        visibleBtn.setOnAction(e -> b2.setVisible(!b2.isVisible()));
        // Bind the text property of the button to the visible
        // property of the b2 button
        visibleBtn.textProperty().bind(new When(b2.visibleProperty()).then("Make Invisible").otherwise("Make Visible"));
        b2.managedProperty().bind(b2.visibleProperty());
        HBox root = new HBox();
        root.getChildren().addAll(visibleBtn, b1, b2, b3);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Sliding to the Left");
        stage.show();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容