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 липсва repaint()   -  person mKorbel    schedule 04.12.2014
comment
@mKorbel Painting се очаква да се случи в MarkLabel.drawMark(Mark), който също съдържа извикване на repaint().   -  person lassekokholm    schedule 04.12.2014
comment
За по-добра помощ по-рано, публикувайте MCVE (Minimal Complete Verifiable Primer) или 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