Я пытаюсь рисовать прямоугольник с помощью JPanel. Проблема в том, что когда я нажимаю на элемент меню, он должен нарисовать новый прямоугольник. НО, когда я это делаю, только часть его расписана. Вот что я имею в виду:
rectangleMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
Является ли это:
но когда я помещаю squares.addSquare(...)
НАРУШЕНИЕ из ActionListener, он дает правильную форму (просто не тогда, когда я этого хочу)
squares.addSquare(10, 10, 100, 100);
rectangleMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
//nothing on click...
}
});
и вот правильный прямоугольник:
любая идея, почему она не рисует правильно, когда я помещаю ее в ActionListener? Благодарю.
Полный код
class UMLWindow extends JFrame {
Squares squares = new Squares();
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
}
public void addMenus() {
getContentPane().add(squares);
//squares.addSquare(10, 10, 100, 100);
JMenuBar menubar = new JMenuBar();
JMenu file = new JMenu("File");
file.setMnemonic(KeyEvent.VK_F);
JMenu shapes = new JMenu("Shapes");
file.setMnemonic(KeyEvent.VK_F);
JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
rectangleMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
shapes.add(rectangleMenuItem);
menubar.add(shapes);
setJMenuBar(menubar);
setTitle("UML Editior");
setSize(300, 200);
setLocationRelativeTo(null);
}
}
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
private List<Rectangle> squares = new ArrayList<Rectangle>();
public void addSquare(int x, int y, int width, int height) {
Rectangle rect = new Rectangle(x, y, width, height);
squares.add(rect);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rect : squares) {
g2.draw(rect);
}
}
}
Swing использует пассивный движок рендеринга, поэтому он не знает, когда он должен обновлять/перекрашивать компонент, если вы не сообщите об этом.
Начните с вызова repaint
из вашего метода addSquare
...
public void addSquare(int x, int y, int width, int height) {
Rectangle rect = new Rectangle(x, y, width, height);
squares.add(rect);
repaint();
}
Это сделает запрос менеджеру перерисовывания, сообщив, что текущий компонент необходимо перекрасить.
Вы также должны указать подсказку для размера для своего компонента, чтобы менеджеры макетов установили размер до 0x0
.
Рассмотрите возможность переопределения метода getPreferredSize
класса Squares
и возврата соответствующего размера по умолчанию...
@Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
Это также означает, что вы можете вызвать pack
в главном окне, и он будет "упаковывать" себя вокруг содержимого, очень аккуратно.
Кроме того, не вызывайте методы, которые либо изменят состояние компонента, либо другие компоненты, либо планируют дополнительный запрос на repaint
, так как это может создать бесконечный цикл ретрансляции, который истощит ваш компьютер ресурсов....
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// This is bad...
//this.setOpaque(true);
// This is bad...
//this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rect : squares) {
g2.draw(rect);
}
}
Такие вещи, как непрозрачность и цвет, должны быть определены за пределами цикла краски. Кроме того, компоненты, которые простираются от JPanel
, по умолчанию непрозрачны;)