Я новичок в этом, и я изучаю книгу 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);
}
}
Давайте начнем с очевидного...
JPanel
использует FlowLayout
который чтит preferredSize
FlowLayout
компонентов, которые добавлены к нему. JComponent
имеет размер по умолчанию 0x0
, а это означает, что когда вы добавляете к нему boardLines
, он становится невидимым и никогда не нарисован. Вы можете создать это, используя panel
BorderLayout
на panel
, например JPanel panel = new JPanel(new BorderLayout());
super.paintComponent
при выполнении пользовательской живописиpaintComponent
, особенно если состояние не изменяется каким-либо образом (то есть результат создания нового экземпляра такой же, как и для предыдущего экземпляра, который вы создали). paintComponent
можно вызывать многократно и в короткие сроки, это может привести к ненужной нагрузке на вашу системуLines
не нужно распространяться от JComponent
, серьезно, это просто ничего не делает...pack
вместо setSize
на JFrame
, это сделает жизнь намного проще...В качестве примера:
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);
}
}
}
Вы должны рассмотреть возможность просмотра пользовательской живописи для более подробной информации.
Диспетчер компоновки по умолчанию для панели - это 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 не являются потокобезопасными.
Дополнительные сведения о менеджерах макетов см. В "Визуальном руководстве для менеджеров макетов".