JLabel дублирует себя, если размер окна изменяется - PullRequest
0 голосов
/ 05 ноября 2018

Я программирую простую диаграмму, с помощью которой вы можете отображать некоторые точки по осям x, y.

public class GraphPlotter extends JPanel {

    private static final long serialVersionUID = 1L;

    /** Default frame size X for frame in pixels */
    private final int DEFAULT_FRAME_SIZE_X = 800;
    /** Default frame size Y for frame in pixels */
    private final int DEFAULT_FRAME_SIZE_Y = 600;
    /** Padding to Frame */
    private final int PAD = 30;
    /** Radius of dot */
    private final int DOT_RADIUS = 3;
    /** Padding of label */
    private final int LABEL_PAD = 10;
    /** Height of label */
    private final int LABEL_HEIGHT = 10;
    /** Width of label */
    private final int LABEL_WIDTH = 100;

    /** Max value for x to print */
    private int maxValueForX;
    /** Scale factor depending to y*/
    private int maxValueForY;
    /** Label for the x axis */
    private String labelForX = "time";
    /** Label for the y axis */
    private String labelForY;

    /**
     * List with points to draw. It holds the y coordinates of points. x
     * coordinates are spaced
     */
    private List<Integer> dataPoints = new ArrayList<>();

    /**
     * 
     * Constructor of this class
     * 
     */
    public GraphPlotter(ArrayList<Integer> dataPoints, String labelForY) {
        this.dataPoints = dataPoints;
        this.maxValueForX = dataPoints.size();
        this.maxValueForY = Collections.max(dataPoints);
        this.labelForY = labelForY;

        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(this);
        f.setSize(this.DEFAULT_FRAME_SIZE_X + PAD, this.DEFAULT_FRAME_SIZE_Y + PAD);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    /** method that draws the points and lines between the points */
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        int w = getWidth();
        int h = getHeight();

        // add labels
        JLabel jLabelY = new JLabel(labelForY);
        JLabel jLabelX = new JLabel(labelForX);

        jLabelY.setSize(LABEL_WIDTH, LABEL_HEIGHT);
        jLabelY.setLocation(LABEL_PAD, LABEL_PAD);
        add(jLabelY);

        jLabelX.setSize(LABEL_WIDTH, LABEL_HEIGHT);
        jLabelX.setLocation(w - LABEL_WIDTH - LABEL_PAD, h - LABEL_HEIGHT - LABEL_PAD);
        jLabelX.setHorizontalAlignment(SwingConstants.RIGHT);
        add(jLabelX);

        // add axis
        g2.drawLine(PAD, PAD, PAD, h - PAD);
        g2.drawLine(PAD, h - PAD, w - PAD, h - PAD);

        double xScale = (w - 2 * PAD) / (dataPoints.size() + 1);
        double yScale = (h - 2 * PAD) / this.maxValueForY;

        int x0 = PAD;
        int y0 = h - PAD;

        // draw the points as small circles
        g2.setPaint(Color.blue);
        for (int i = 0; i < dataPoints.size(); i++) {
            int x = x0 + (int) (xScale * (i + 1));
            int y = y0 - (int) (yScale * dataPoints.get(i));
            g2.fillOval(x - DOT_RADIUS, y - DOT_RADIUS, 2 * DOT_RADIUS,
                    2 * DOT_RADIUS);
        }
    }


    /** Size of List */
    private int getSizeOfDataPoints() {
        return dataPoints.size();
    }

    /** Deletes last DataPoint in List */
    private void deleteLastDataPoint() {
        dataPoints.remove(0);
    }

    /**
     * Ad point and repaint graph.
     * 
     * @param point to draw (y-coord)
     */
    public void add(int point) {
        if (getSizeOfDataPoints() > this.maxValueForX) {
            deleteLastDataPoint();
        }
        dataPoints.add(point);
        this.repaint();
    }

    public static void main(String[] args) {
        ArrayList<Integer> scores = new ArrayList<>();
        Random random = new Random();

        for (int i = 0; i < 50; i++) {
            scores.add(random.nextInt(10));
        }

        String labelForY = "throughput";

        GraphPlotter graphPlotter = new GraphPlotter(scores, labelForY);
        graphPlotter.add(5);
    }

Метки отображаются при запуске приложения. Если я изменю размер окна приложения, метки будут отображаться по всему окну. (см. скриншоты)

до изменения размера

после изменения размера

Как я могу избежать дублирования ярлыков? Я предполагаю, что программа добавляет те же метки в панель, после того как окно перекрашено. Если я напишу

add(jLabelY);
repaint();

в методе paintComponent() эта ошибка возникает при запуске приложения. Я также попытался поместить панель в FlowLayout, но не внес никаких изменений.

1 Ответ

0 голосов
/ 05 ноября 2018
Swing может вызывать

paintComponent() в почти произвольные моменты времени (всякий раз, когда необходимо перерисовать компонент; например, при изменении размера). Поэтому, как правило, он не должен иметь побочных эффектов. Однако вы используете метки от paintComponent до add() как дочерние элементы вашей панели, которые вы никогда не удаляете. Поэтому каждый раз, когда ваша панель перерисовывается, к ее дочерним элементам добавляются две метки. super.paintComponent() затем нарисует их все.

Одним из решений этой проблемы будет сохранение двух меток в качестве полей панели и обновление их местоположения только в paintComponent (до вызова super.paintComponent())

...