在图像上方画一个 X 字母

draw a X letter above image

我正在为一个大学项目编写 java 中的一个软件 - javafx 但我在以图形方式实现投票功能时遇到了一些问题。

我有一个这样的政党图标:

我想在 java 中在此图标上方放一个 X 字母,以获得如下内容:

我的解决方案可能是在图像上方画一条线和另一条线,但我不知道该怎么做。

package unimi.sysvotes.elettore;

import java.io.File;
import java.io.IOException;

import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;

public class VotoController {
    @FXML Pane listaUnoCandidatoUninominale;
    @FXML ImageView listaUnoSimboloUno;

    @FXML
    private void initialize() {
        File fileUno = new File("C:\Users\Administrator\Desktop\progettazione\simboli\popoloDellaLiberta.jpg");
        
        Image imageUno = new Image("file:///"+fileUno.getAbsolutePath());
        
        listaUnoSimboloUno.setImage(imageUno);

    }

    @FXML
    private void listaUnoSimboloUnoAction(MouseEvent me) throws IOException {
        System.out.println("Votato");

        /*
           How put Two line above ImageView popolo?
         */

        Line lineOne = new Line(10, 10, 80, 80);
        lineOne.setFill(null);
        lineOne.setStroke(Color.BLACK);
        lineOne.setStrokeWidth(2);

        Line lineTwo = new Line(80, 10, 10, 80);
        lineTwo.setFill(null);
        lineTwo.setStroke(Color.BLACK);
        lineTwo.setStrokeWidth(2);

    }
    

}

我用了StackPane.

我把一个canvas和ImageView放在堆栈里,然后在canvas画线。我还更改了堆栈窗格中节点的顺序。

public class VotoController {
    @FXML Pane listaUnoCandidatoUninominale;
    @FXML StackPane listaUnoSimboloUnoStack;

    @FXML ImageView listaUnoSimboloUno;
    @FXML Canvas listaUnoSimboloUnoX;
    
    @FXML
    private void initialize() {
        File fileUno = new File("C:\Users\Administrator\Desktop\progettazione\simboli\popoloDellaLiberta.jpg");
        
        Image imageUno = new Image("file:///"+fileUno.getAbsolutePath());
        
        listaUnoSimboloUno.setImage(imageUno);

    }
    
    @FXML
    private void votoCandidatoUninominalAction(MouseEvent me) throws IOException {
        System.out.println("Votato");
        listaUnoCandidatoUninominale.setStyle("-fx-background-image: url('file:C:/Users/Administrator/Desktop/progettazione/voto/backgroundcandidatouninominale.jpg');");
    }

    @FXML
    private void listaUnoSimboloUnoAction(MouseEvent me) throws IOException {
        System.out.println("Votato");

        GraphicsContext gc = listaUnoSimboloUnoX.getGraphicsContext2D();
        
        Line lineOne = new Line(10, 10, 40, 40);
        lineOne.setFill(null);
        lineOne.setStroke(Color.BLACK);
        lineOne.setStrokeWidth(2);

        gc.beginPath();
        gc.setLineWidth(5);
        gc.moveTo(0, 0);
        gc.lineTo(0, 0);
        gc.lineTo(50, 50);

        gc.stroke();

        gc.beginPath();
        gc.setLineWidth(5);
        gc.moveTo(50, 0);
        gc.lineTo(50, 0);
        gc.lineTo(0, 50);

        gc.stroke();

        ObservableList<Node> childs = listaUnoSimboloUnoStack.getChildren();

        if (childs.size() > 1) {
            //
            Node topNode = childs.get(childs.size()-1);
            topNode.toBack();
        }
    }
}

这是基于窗格的替代解决方案。

使用窗格而不是组,以便可以使用 CSS 设置窗格的样式。如果不需要,则可以使用组代替。

分层由 JavaFX 用于渲染的 painter's algorithm 隐式完成。此算法将稍后添加到列表中的项目绘制在列表中较早添加的项目之上。

该解决方案实现了 select 和 deselect 图像的能力,这并不是真正提出的问题,但似乎暗示在您的问题文本中。

select可用图像的概念类似于 ToggleButton 或 CheckBox。因此,可以改用 ToggleButton 并根据需要使用 CSS 设置样式。例如,X 可以通过 CSS 中的 svgpath 背景层实现,这就是 in-built 控件(如单选按钮)的工作方式。但是,我没有在这里应用这种方法(它比我现在介绍的要复杂一些)。

该示例扩展自 Pane,但如果您愿意,可以使用聚合而不是扩展。

使用的图片在这个问题的答案中提供:

使用以下代码将 X 添加到图像顶部的窗格子项。

public SelectableImageView(Image image) {
    imageView = new ImageView(image);
    getChildren().add(imageView);

    selectionMark = createSelectionMark();

    // ... additional listener and initialization code here.
}


private Node[] createSelectionMark() {
    double w = imageView.getImage().getWidth();
    double h = imageView.getImage().getHeight();

    Line l1 = new Line(INSET, INSET, w - INSET, h - INSET);
    l1.setStrokeWidth(INSET * .75);
    l1.setStrokeLineCap(StrokeLineCap.ROUND);
    Line l2 = new Line(INSET, h - INSET, w - INSET, INSET);
    l2.setStrokeWidth(INSET * .75);
    l2.setStrokeLineCap(StrokeLineCap.ROUND);

    return new Node[] { l1, l2 };
}

private void addSelection() {
    getChildren().addAll(selectionMark);
}

可执行示例

import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.shape.*;
import javafx.stage.Stage;

import java.util.*;
import java.util.stream.Collectors;

public class SelectableImagesApp extends Application {

    private static final double PADDING = 20;

    @Override
    public void start(Stage stage) throws Exception {
        TilePane imageTiles = new TilePane(PADDING, PADDING);
        imageTiles.setPadding(
                new Insets(PADDING)
        );
        imageTiles.getChildren().addAll(
                createSelectableImages()
        );
        imageTiles.setPrefColumns(ImageNames.values().length);

        stage.setScene(new Scene(imageTiles));
        stage.show();
    }

    public enum ImageNames {
        Medusa,
        Dragon,
        Treant,
        Unicorn
    }

    private List<SelectableImageView> createSelectableImages() {
        return Arrays.stream(ImageNames.values())
                .map(m ->
                        new SelectableImageView(
                                new Image(
                                        Objects.requireNonNull(
                                                SelectableImagesApp.class.getResource(
                                                        m + "-icon.png"
                                                )
                                        ).toExternalForm()
                                )
                        )
                ).collect(Collectors.toList());
    }

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

class SelectableImageView extends Pane {
    private final ImageView imageView;

    private final Node[] selectionMark;

    private BooleanProperty selected = new SimpleBooleanProperty(false);

    private final double INSET = 10;

    public SelectableImageView(Image image) {
        imageView = new ImageView(image);
        getChildren().add(imageView);

        selectionMark = createSelectionMark();

        selected.addListener((observable, wasSelected, isSelected) -> {
            if (isSelected) {
                addSelection();
            } else {
                removeSelection();
            }
        });

        this.setStyle("-fx-background-color: lightblue;");
        this.setPickOnBounds(true);
        this.setOnMouseClicked(e -> {
            selected.set(!selected.get());
        });
    }

    private Node[] createSelectionMark() {
        double w = imageView.getImage().getWidth();
        double h = imageView.getImage().getHeight();

        Line l1 = new Line(INSET, INSET, w - INSET, h - INSET);
        l1.setStrokeWidth(INSET * .75);
        l1.setStrokeLineCap(StrokeLineCap.ROUND);
        Line l2 = new Line(INSET, h - INSET, w - INSET, INSET);
        l2.setStrokeWidth(INSET * .75);
        l2.setStrokeLineCap(StrokeLineCap.ROUND);

        return new Node[] { l1, l2 };
    }

    private void addSelection() {
        getChildren().addAll(selectionMark);
    }

    private void removeSelection() {
        getChildren().removeAll(selectionMark);
    }

    public boolean isSelected() {
        return selected.get();
    }

    public BooleanProperty selectedProperty() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected.set(selected);
    }
}