Проблема с графикой в Java JPanel, но она работает в кадре

1

Я новичок в этом, и я изучаю книгу Big Java от Cay Hortsmann. Для одного из ранних проектов я делаю игру tic-tac-toe с графическим интерфейсом, но у меня возникают проблемы с тем, что совет появится на JPanel. Он отображается отлично только в кадре, но, поскольку я хочу добавить кнопки, я пытаюсь поместить плату в JPanel, а затем добавить JPanel к кадру. К сожалению, это не работает. Буду признателен за любые отзывы, которые вы можете дать:

import javax.swing.JFrame;
import javax.swing.JPanel;



public class TicTacToePlayer {

    private static final int FRAME_WIDTH = 400;
    private static final int FRAME_HEIGHT = 400;

    private int count = 0;

    public static void main(String[] args) {

        final JFrame frame = new JFrame();

        // create board
        final LinesComponent boardLines = new LinesComponent();     
        JPanel panel = new JPanel();
        panel.add(boardLines);
        frame.add(panel);
        frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);

    }

}
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JComponent;


public class LinesComponent extends JComponent {
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;

        Lines boardLines = new Lines();
        boardLines.draw(g2);
    }

}
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;

import javax.swing.JComponent;


public class Lines extends JComponent {
    public Lines() {
        setPreferredSize(new Dimension(400, 400));

    }

    public void draw(Graphics2D g2) {
        Line2D.Double leftVertLine = new Line2D.Double(150, 50, 150, 350);
        Line2D.Double rightVertLine = new Line2D.Double(250, 50, 250, 350);
        Line2D.Double topHorizLine = new Line2D.Double(50, 150, 350, 150);
        Line2D.Double bottomHorizLine = new Line2D.Double(50, 250, 350, 250);
        g2.draw(leftVertLine);
        g2.draw(rightVertLine);
        g2.draw(topHorizLine);
        g2.draw(bottomHorizLine);
    }
}
Теги:
swing
jpanel
tic-tac-toe

2 ответа

5
Лучший ответ

Давайте начнем с очевидного...

  • JPanel использует FlowLayout который чтит preferredSize FlowLayout компонентов, которые добавлены к нему. JComponent имеет размер по умолчанию 0x0, а это означает, что когда вы добавляете к нему boardLines, он становится невидимым и никогда не нарисован. Вы можете создать это, используя panel BorderLayout на panel, например JPanel panel = new JPanel(new BorderLayout());
  • В то время как технически не требуется, это всегда хорошая pratice, чтобы вызвать super.paintComponent при выполнении пользовательской живописи
  • Вы не должны создавать непродолжительные объекты в paintComponent, особенно если состояние не изменяется каким-либо образом (то есть результат создания нового экземпляра такой же, как и для предыдущего экземпляра, который вы создали). paintComponent можно вызывать многократно и в короткие сроки, это может привести к ненужной нагрузке на вашу систему
  • Lines не нужно распространяться от JComponent, серьезно, это просто ничего не делает...
  • Используйте pack вместо setSize на JFrame, это сделает жизнь намного проще...
  • Пользовательский интерфейс Swing должен создаваться и управляться из контекста Диспетчерского потока событий

В качестве примера:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TicTacToePlayer {

    private static final int FRAME_WIDTH = 400;
    private static final int FRAME_HEIGHT = 400;

    private int count = 0;

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

    public TicTacToePlayer() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                // create board
                final LinesComponent boardLines = new LinesComponent();
                JPanel panel = new JPanel(new BorderLayout());
                panel.add(boardLines);
                frame.add(panel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class LinesComponent extends JComponent {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            draw(g2);
        }

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

        public void draw(Graphics2D g2) {
            Line2D.Double leftVertLine = new Line2D.Double(150, 50, 150, 350);
            Line2D.Double rightVertLine = new Line2D.Double(250, 50, 250, 350);
            Line2D.Double topHorizLine = new Line2D.Double(50, 150, 350, 150);
            Line2D.Double bottomHorizLine = new Line2D.Double(50, 250, 350, 250);
            g2.draw(leftVertLine);
            g2.draw(rightVertLine);
            g2.draw(topHorizLine);
            g2.draw(bottomHorizLine);
        }
    }
}

Вы должны рассмотреть возможность просмотра пользовательской живописи для более подробной информации.

2

Диспетчер компоновки по умолчанию для панели - это FlowLayout который упорядочивает элементы слева направо. Поскольку вы хотите, чтобы сетка отображалась в главном представлении, вы можете использовать другой менеджер компоновки, такой как BorderLayout. Если вы используете BorderLayout, вы можете поместить сетку в центральную область, чтобы сетка заполнила основной вид:

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import java.awt.BorderLayout;

public class TicTacToePlayer {

    private static final int FRAME_WIDTH = 400;
    private static final int FRAME_HEIGHT = 400;

    private int count = 0;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame();

                // create board
                final LinesComponent boardLines = new LinesComponent();
                JPanel panel = new JPanel();
                panel.setLayout(new BorderLayout(0, 0));
                panel.add(boardLines, BorderLayout.CENTER);
                frame.getContentPane().add(panel);
                frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

Чтобы добавить кнопки в окно при использовании BorderLayout, вы можете добавить кнопки в другой JPanel и добавить эту панель в BorderLayout в одной из других областей (например: BorderLayout.NORTH, BorderLayout.SOUTH и т.д.).

Кроме того, обратите внимание, что в приведенном выше исправленном коде код Swing GUI помещается в Thread Dispatch Thread (EDT). Вы никогда не должны запускать какой-либо код Swing вне EDT, потому что большинство методов объекта Swing не являются потокобезопасными.

Дополнительные сведения о менеджерах макетов см. В "Визуальном руководстве для менеджеров макетов".

Ещё вопросы

Сообщество Overcoder
Наверх
Меню