永远不要在绘画方法中调用重绘,因为这会导致"穷人"动画发生.而是在JButton的ActionListener中调用它.此外,不要在绘制方法中随机化,而是在ActionListener中执行此操作.绘画方法不在您的控制之下,您不希望使用它来更改对象的状态,而只是显示它.
其他建议:
您的代码仍需要设置JFrame的setDefaultCloseOperation
并且仍然需要将JFrame设置为可见
你从不建议在代码中调整大小.我自己,我建议覆盖public Dimension getPreferredSize()
你的JPanel并pack()
在添加JPanel之后但在显示它之前调用JFrame.
我将重命名您的类,以便名称不会与核心Java类冲突,并导致您的教师,我们或您未来的自我混淆.
不要在for循环中继续重新创建一个新的Random对象.相反,为什么不简单地给类一个Random字段,创建一次,但重复使用该对象.
您需要将颜色与形状/ Ellipse2D相关联.对于一对一的通信,请考虑使用诸如a之类的Map HashMap
.
例如:
import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.geom.Ellipse2D; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import javax.swing.*; @SuppressWarnings("serial") public class Panel2 extends JPanel { // preferred size constants private static final int PREF_W = 600; private static final int PREF_H = PREF_W; // map to hold circles and colors private MapshapeColorMap = new LinkedHashMap<>(); public Panel2() { add(new JButton(new RandomColorAction())); } @Override public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } return new Dimension(PREF_W, PREF_H); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; // create *smooth* drawings g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); paintStuff(g2); } private void paintStuff(Graphics2D g2) { // iterate through our map extracting all circles and colors // and drawing them for (Entry entry : shapeColorMap.entrySet()) { Shape shape = entry.getKey(); Color color = entry.getValue(); g2.setColor(color); g2.fill(shape); } } // listener for our button private class RandomColorAction extends AbstractAction { private static final int CIRC_WIDTH = 100; private Random random = new Random(); private int count = 0; public RandomColorAction() { super("Random Circle: 0"); putValue(MNEMONIC_KEY, KeyEvent.VK_R); } @Override public void actionPerformed(ActionEvent e) { // create our random ellipses int x = random.nextInt(getWidth() - CIRC_WIDTH); int y = random.nextInt(getHeight() - CIRC_WIDTH); Shape shape = new Ellipse2D.Double(x, y, CIRC_WIDTH, CIRC_WIDTH); // create our random color using HSB for brighter colors float hue = random.nextFloat(); float saturation = (float) (0.8 + random.nextFloat() * 0.2); float brightness = (float) (0.8 + random.nextFloat() * 0.2); Color color = Color.getHSBColor(hue, saturation, brightness); shapeColorMap.put(shape, color); // increment count, place items into map, repaint count++; putValue(NAME, "Random Circle: " + count); repaint(); } } private static void createAndShowGui() { Panel2 mainPanel = new Panel2(); JFrame frame = new JFrame("Panel2"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> createAndShowGui()); } }
在评论中,Camickr敏锐地指出:
绘制方法应绘制组件的当前状态.通过使用HashMap,您可以引入随机性的可能性.无法保证通过地图的迭代顺序.因此,当新条目添加到地图时,每个Shape的绘制顺序可能会发生变化.通常不是问题,但如果两个随机形状重叠,那么结果很好的是翻转,其形状被涂在彼此之上.
当然,他是绝对正确的,因为没有保证HashMap的顺序.幸运的是,变量本身被声明为Map类型,因此为了保持顺序,所有人需要做的是将实际的对象类型从HashMap更改为LinkedHashMap,这是一个根据其API的类:
此实现使其客户端免受HashMap(和Hashtable)提供的未指定的,通常混乱的排序,而不会导致与TreeMap相关的成本增加.
所以对于TLDR,改变这个:
private MapshapeColorMap = new HashMap<>();
对此:
private MapshapeColorMap = new LinkedHashMap<>();
编辑修复颜色计算.
只是为了它的乐趣,它引入了Path2D和AffineTransform与MouseListener/MouseMotionListener以允许拖动圆圈:
import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Path2D; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import javax.swing.*; @SuppressWarnings("serial") public class Panel2 extends JPanel { // preferred size constants private static final int PREF_W = 600; private static final int PREF_H = PREF_W; // map to hold circles and colors private MapshapeColorMap = new LinkedHashMap<>(); public Panel2() { add(new JButton(new RandomColorAction())); MyMouse myMouse = new MyMouse(); addMouseListener(myMouse); addMouseMotionListener(myMouse); } @Override public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } return new Dimension(PREF_W, PREF_H); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; // create *smooth* drawings g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); paintStuff(g2); } private void paintStuff(Graphics2D g2) { // iterate through our map extracting all circles and colors // and drawing them for (Entry entry : shapeColorMap.entrySet()) { Shape shape = entry.getKey(); Color color = entry.getValue(); g2.setColor(color); g2.fill(shape); } } private class MyMouse extends MouseAdapter { private Entry selected = null; private Path2D path; private Point p = null; @Override public void mousePressed(MouseEvent e) { Set > entrySet = shapeColorMap.entrySet(); // get Shape pressed for (Entry entry : entrySet) { if (entry.getKey().contains(e.getPoint())) { selected = entry; } } if (selected != null) { path = new Path2D.Double(selected.getKey()); // move it to the top entrySet.remove(selected); shapeColorMap.put(path, selected.getValue()); p = e.getPoint(); repaint(); } } @Override public void mouseReleased(MouseEvent e) { if (selected != null) { moveSelected(e); } selected = null; } @Override public void mouseDragged(MouseEvent e) { if (selected != null) { moveSelected(e); } } private void moveSelected(MouseEvent e) { int x = e.getX() - p.x; int y = e.getY() - p.y; p = e.getPoint(); AffineTransform at = AffineTransform.getTranslateInstance(x, y); path.transform(at); repaint(); } } // listener for our button private class RandomColorAction extends AbstractAction { private static final int CIRC_WIDTH = 100; private Random random = new Random(); private int count = 0; public RandomColorAction() { super("Random Circle: 0"); putValue(MNEMONIC_KEY, KeyEvent.VK_R); } @Override public void actionPerformed(ActionEvent e) { // create our random ellipses int x = random.nextInt(getWidth() - CIRC_WIDTH); int y = random.nextInt(getHeight() - CIRC_WIDTH); Shape shape = new Ellipse2D.Double(x, y, CIRC_WIDTH, CIRC_WIDTH); // create our random color using HSB for brighter colors float hue = random.nextFloat(); float saturation = (float) (0.8 + random.nextFloat() * 0.2); float brightness = (float) (0.8 + random.nextFloat() * 0.2); Color color = Color.getHSBColor(hue, saturation, brightness); shapeColorMap.put(shape, color); // increment count, place items into map, repaint count++; putValue(NAME, "Random Circle: " + count); repaint(); } } private static void createAndShowGui() { Panel2 mainPanel = new Panel2(); JFrame frame = new JFrame("Panel2"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> createAndShowGui()); } }