JLabels в GridLayout не всегда рисуются

Я создаю базовое приложение Tic-Tac-Toe в Java Swing, и для этой цели я начал исследовать рисование. Однако я столкнулся с проблемой, когда подкласс JPanel содержит более одного экземпляра подкласса JLabel, который переопределяет метод paintComponent(Graphic), в формате массива GridLayout, встроенного в него самого. Проблема в том, что закрашивается только первый элемент в этом массиве.

Пример для настроенного подкласса JLabel:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JLabel;

@SuppressWarnings("serial")
public class DrawLabel extends JLabel {

    private boolean hasPaint;

    public DrawLabel() {
        super();
        this.hasPaint = false;
    }

    public void draw() {
        hasPaint = true;
        repaint();
    }

    public void clear() {
        hasPaint = false;
        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;

        if (hasPaint) {
            g2.drawOval(getX(), getY(), getWidth(), getHeight());
        } else {
            g2.clearRect(getX(), getY(), getWidth(), getHeight());
        }
    }
}

Пример для настроенного подкласса JPanel:

import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class DrawPanel extends JPanel {

    private Dimension size;
    private DrawLabel[][] fields;

    public DrawPanel(Dimension d) {
        super();
        this.size = d;
        this.fields = new DrawLabel[size.height][size.width];

        this.setLayout(new GridLayout(size.height, size.width));

        for (int i = 0; i < size.height; i++) {
            for (int j = 0; j < size.width; j++) {
                this.fields[i][j] = new DrawLabel();
                this.add(fields[i][j]);
            }
        }
    }

    public void draw(int row, int col) {
        fields[row][col].draw();
    }

    public void clear() {
        for (int i = 0; i < size.height; i++) {
            for (int j = 0; j < size.width; j++) {
                fields[i][j].clear();
            }
        }
    }
}

Пример сценария, вызывающего проблему:

import java.awt.BorderLayout;
import java.awt.Dimension;

import javax.swing.JFrame;

public class Driver {

    public static void main(String[] args) {
        DrawPanel board = new DrawPanel(new Dimension(2, 1));

        JFrame canvas = new JFrame();
        canvas.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        canvas.add(board, BorderLayout.CENTER);
        canvas.pack();

        canvas.setLocationRelativeTo(null);
        canvas.setVisible(true);

        board.draw(0, 0); // This gets painted.
        board.draw(0, 1); // This does not get painted!
    }

}

Любопытно, что если DrawPanel.draw(int, int) изменить, чтобы установить текст элемента, а не использовать его, обновится только второй элемент, а первый нет, что является полной противоположностью исходной проблеме.

Я пытался найти другие проблемы и вопросы, связанные с отрисовкой подкомпонентов, но мне еще предстоит найти проблему, подобную этой, где кажется, что каждый экземпляр после первого в GridLayout не рисуется одинаково. Что может быть не так?

Спасибо за ваше время и усилия!


person lassekokholm    schedule 04.12.2014    source источник
comment
MarkLabel отсутствует перерисовка ()   -  person mKorbel    schedule 04.12.2014
comment
@mKorbel Ожидается, что рисование произойдет в MarkLabel.drawMark(Mark), который также содержит вызов repaint().   -  person lassekokholm    schedule 04.12.2014
comment
Чтобы быстрее получить помощь, опубликуйте MCVE (минимальный полный проверяемый пример) или SSCCE (краткий, автономный, правильный пример).   -  person Andrew Thompson    schedule 04.12.2014
comment
..Я обязательно сделаю это в будущем! Тогда мне нужно будет убедиться, что я уделю внимание вашим вопросам ..в будущем. Конечно, можно отредактировать этот вопрос прямо сейчас.   -  person Andrew Thompson    schedule 04.12.2014
comment
@AndrewThompson Мои извинения. Я минимизировал область отображаемого экземпляра проблемы, сохранив при этом то же разделение. Есть ли что-нибудь еще, что я могу сделать?   -  person lassekokholm    schedule 04.12.2014


Ответы (1)


g2.drawOval(getX(), getY(), getWidth(), getHeight());

Отрисовка компонента выполняется относительно компонента, а не панели, в которой он закрашивается. Методы getX/Y() возвращают расположение компонента относительно родительского компонента.

Итак, если размер каждого компонента равен (200, 200), то овал первого компонента будет окрашен с использованием

g2.drawOval(0, 0, 200, 200);

Второй компонент будет окрашен в:

g2.drawOval(200, 0, 200, 200);

но поскольку компонент имеет ширину всего 200 пикселей, начальное значение x, равное 200, находится за пределами компонента, поэтому рисовать нечего.

Вместо этого просто используйте:

g2.drawOval(0, 0, getWidth(), getHeight());
person camickr    schedule 04.12.2014
comment
Это решает проблему замены getX() и getY() нулями! Я понял, что эти методы возвращают исходную точку дочернего компонента относительно его родительского компонента, однако я не понимал, что рисование должно быть относительно самого дочернего компонента, а не родителя. Благодарю вас! - person lassekokholm; 04.12.2014