如何在javafx 2.2中创建可编辑标签

时间:2014-08-29 16:29:33

标签: text javafx controls

我希望在我正在编写的窗格上的任意位置创建一个可编辑的标签。我的印象是TextField或TextArea对象是我可以用来实现该功能的。显然还有更多,因为我不知道在创建对象时如何定位对象。我在" Chaotic Java"上找到了一个例子。网站,但我需要做更多的工作,以了解那里发生了什么。 http://chaoticjava.com/posts/another-javafx-example-the-editable-label/

我正在寻找这个群体的更多意见。

(没有错误,因为我没有写任何代码。)

2 个答案:

答案 0 :(得分:5)

我对如何实现这一点感到好奇,所以我试了一下。这就是我想出来的。

lonely

使用的方法与詹姆斯在评论中提出的方法非常相似:

  

我会从Pane开始。 。 。,TextFields在编辑时表示文本。使用Pane和Text对象注册鼠标侦听器,并使用layoutX和layoutY属性来定位事物。 。 。只是为了使用文本字段,并使用CSS使它们在未聚焦时看起来像标签,在聚焦时看起来像文本字段。

唯一非常棘手的部分是如何正确调整文本字段的大小,因为文本字段中的文本不会通过公共API公开,以允许您收听它的布局边界。您也许可以使用css查找函数来获取随附的Text,但我选择使用私有sun FontMetrics API(将来可能会弃用),以获取文本的大小。在将来使用Java 9时,您应该能够在不使用私有API的情况下执行任务。

该解决方案不会尝试做任何棘手的事情,比如处理多格式或多行文本,它只是用于可以放置在场景上的几个单词的简短单行注释。

TextCreator.java

// ## CAUTION: beware the com.sun imports...
import com.sun.javafx.tk.FontMetrics;
import com.sun.javafx.tk.Toolkit;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 * Displays a map of the lonely mountain upon which draggable, editable labels can be overlaid.
 */
public class TextCreator extends Application {
    private static final String MAP_IMAGE_LOC =
            "http://images.wikia.com/lotr/images/archive/f/f6/20130209175313!F27c_thorins_map_from_the_hobbit.jpg";

    public static void main(String[] args) throws Exception {
        launch(args);
    }

    @Override
    public void start(final Stage stage) throws Exception {
        Pane pane = new Pane();

        pane.setOnMouseClicked(event -> {
            if (event.getTarget() == pane) {
                pane.getChildren().add(
                        new EditableDraggableText(event.getX(), event.getY())
                );
            }
        });

        EditableDraggableText cssStyled = 
                new EditableDraggableText(439, 253, "Style them with CSS");
        cssStyled.getStyleClass().add("highlighted");

        pane.getChildren().addAll(
                new EditableDraggableText(330, 101, "Click to add a label"),
                new EditableDraggableText(318, 225, "You can edit your labels"),
                cssStyled,
                new EditableDraggableText(336, 307, "And drag them"),
                new EditableDraggableText(309, 346, "Around The Lonely Mountain")
        );

        StackPane layout = new StackPane(
            new ImageView(
                    new Image(
                            MAP_IMAGE_LOC
                    )
            ),
            pane
        );

        Scene scene = new Scene(layout);
        scene.getStylesheets().add(getClass().getResource(
            "editable-text.css"
        ).toExternalForm());

        stage.setScene(scene);
        stage.setResizable(false);
        stage.show();
    }

    /**
     * A text field which has no special decorations like background, border or focus ring.
     *   i.e. the EditableText just looks like a vanilla Text node or a Label node.
     */
    class EditableText extends TextField {
        // The right margin allows a little bit of space
        // to the right of the text for the editor caret.
        private final double RIGHT_MARGIN = 5;

        EditableText(double x, double y) {
            relocate(x, y);
            getStyleClass().add("editable-text");

            //** CAUTION: this uses a non-public API (FontMetrics) to calculate the field size
            //            the non-public API may be removed in a future JavaFX version.
            // see: https://javafx-jira.kenai.com/browse/RT-8060
            //      Need font/text measurement API
            FontMetrics metrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(getFont());
            setPrefWidth(RIGHT_MARGIN);
            textProperty().addListener((observable, oldTextString, newTextString) ->
                setPrefWidth(metrics.computeStringWidth(newTextString) + RIGHT_MARGIN)
            );

            Platform.runLater(this::requestFocus);
        }
    }

    /**
     * An EditableText (a text field which looks like a label), which can be dragged around
     * the screen to reposition it.
     */
    class EditableDraggableText extends StackPane {
        private final double PADDING = 5;
        private EditableText text = new EditableText(PADDING, PADDING);

        EditableDraggableText(double x, double y) {
            relocate(x - PADDING, y - PADDING);
            getChildren().add(text);
            getStyleClass().add("editable-draggable-text");

            // if the text is empty when we lose focus,
            // the node has no purpose anymore
            // just remove it from the scene.
            text.focusedProperty().addListener((observable, hadFocus, hasFocus) -> {
                if (!hasFocus && getParent() != null && getParent() instanceof Pane &&
                    (text.getText() == null || text.getText().trim().isEmpty())) {
                    ((Pane) getParent()).getChildren().remove(this);
                }
            });

            enableDrag();
        }

        public EditableDraggableText(int x, int y, String text) {
            this(x, y);
            this.text.setText(text);
        }

        // make a node movable by dragging it around with the mouse.
        private void enableDrag() {
            final Delta dragDelta = new Delta();
            setOnMousePressed(mouseEvent -> {
                this.toFront();
                // record a delta distance for the drag and drop operation.
                dragDelta.x = mouseEvent.getX();
                dragDelta.y = mouseEvent.getY();
                getScene().setCursor(Cursor.MOVE);
            });
            setOnMouseReleased(mouseEvent -> getScene().setCursor(Cursor.HAND));
            setOnMouseDragged(mouseEvent -> {
                double newX = getLayoutX() + mouseEvent.getX() - dragDelta.x;
                if (newX > 0 && newX < getScene().getWidth()) {
                    setLayoutX(newX);
                }
                double newY = getLayoutY() + mouseEvent.getY() - dragDelta.y;
                if (newY > 0 && newY < getScene().getHeight()) {
                    setLayoutY(newY);
                }
            });
            setOnMouseEntered(mouseEvent -> {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    getScene().setCursor(Cursor.HAND);
                }
            });
            setOnMouseExited(mouseEvent -> {
                if (!mouseEvent.isPrimaryButtonDown()) {
                    getScene().setCursor(Cursor.DEFAULT);
                }
            });
        }

        // records relative x and y co-ordinates.
        private class Delta {
            double x, y;
        }
    }    
}

可编辑-text.css

.editable-text {
    -fx-background-color: transparent;
    -fx-background-insets: 0;
    -fx-background-radius: 0;
    -fx-padding: 0;
}

.editable-draggable-text:hover .editable-text {
    -fx-background-color: yellow;
}

.editable-draggable-text {
    -fx-padding: 5;
    -fx-background-color: rgba(152, 251, 152, 0.2); // translucent palegreen
}

.editable-draggable-text:hover {
    -fx-background-color: orange;
}

.highlighted {
    -fx-background-color: rgba(255, 182, 93, 0.3);  // translucent mistyrose
    -fx-border-style: dashed;
    -fx-border-color: firebrick;
}

如果您有时间,可以清理示例实施并将其捐赠给ControlsFX项目。

答案 1 :(得分:1)

您可以使用标签功能:setGraphic()

这是我的代码:

public void editableLabelTest(Stage stage){
    Scene scene = new Scene(new VBox(new EditableLabel("I am a label"),
                                    new EditableLabel("I am a label too")));
    stage.setScene(scene);
    stage.show();
}

class EditableLabel extends Label{
    TextField tf = new TextField();
    /***
     * backup is used to cancel when press ESC...
     */
    String backup = "";
    public EditableLabel(){
        this("");
    }
    public EditableLabel(String str){
        super(str);
        this.setOnMouseClicked(e -> {
            if(e.getClickCount() == 2){
                tf.setText(backup = this.getText());
                this.setGraphic(tf);
                this.setText("");
                tf.requestFocus();
            }
        });
        tf.focusedProperty().addListener((prop, o, n) -> {
            if(!n){
                toLabel();
            }
        });
        tf.setOnKeyReleased(e -> {
            if(e.getCode().equals(KeyCode.ENTER)){
                toLabel();
            }else if(e.getCode().equals(KeyCode.ESCAPE)){
                tf.setText(backup);
                toLabel();
            }
        });
    }

    void toLabel(){
        this.setGraphic(null);
        this.setText(tf.getText());
    }

}