【问题标题】:Keeping draw graphics - removing super.paintComponent保持绘制图形 - 删除 super.paintComponent
【发布时间】:2012-09-30 09:01:39
【问题描述】:

我有一个名为 Foo 的类,它扩展了一个名为 Bar 的类,该类扩展了 JPanel 并实现了 ActionListener。当我选择Circle并单击draw按钮时,我画了一个圆,当我按下rectangle并单击draw时,它会擦除​​以前的形状并绘制一个矩形。

但是,我想保留 JPanel 上的所有形状,直到我选择单击擦除按钮。所以我删除了 super.paintComponent(g) 并且它可以工作,但它也会导致 Bar 类的按钮以故障方式重新出现。我怎样才能阻止按钮再次绘画? 我在考虑不要扩展 Bar 并让 Foo 扩展 JPanel。

  public class Bar extends JPanel implements ActionListener
    {
    public void actionPerformed(ActionEvent e)
    {

        if (e.getActionCommand() == "Draw")
        {
            this.requestDraw = true;
            repaint();
        }
            if (e.getActionCommand() == "Circle")
            {
                requestRectangle = false;
                requestTriangle = false;
                requestCircle = true;
            }
            if (e.getActionCommand() == "Rectangle")
            {
                requestCircle = false;
                requestTriangle = false;
                requestRectangle = true;
            }
            if (e.getActionCommand() == "Right Triangle")
            {
                requestCircle = false;
                requestRectangle = false;
                requestTriangle = true;
            }
    }


    public class Foo extends Bar
    {    
        @Override
        public void paintComponent(Graphics g)
        {
            //super.paintComponent(g);
            if(RequestDraw())
            {

                if(RequestCircle())
                    circle.draw(g);
                if(RequestRectangle())
                    rectangle.draw(g);
                if(RequestTriangle())
                    rightTriangle.draw(g);



            }

            if(!RequestDraw())
            {                    


                g.setColor(getBackground());
                g.fillRect(0,0,getWidth(), getHeight());
            }        
        }
    }
}

【问题讨论】:

    标签: java swing graphics repaint paintcomponent


    【解决方案1】:

    在气垫船的所有 cmets 旁边

    图形上下文在组件之间共享。 super.paintComponent 的任务之一是在绘制之前“清理”图形上下文。

    这就是为什么您会看到两个版本的按钮...

    我也会做一些不同的事情。随着时间的推移,这应该有助于可扩展性和重用性,同时也会稍微减少逻辑。

    我会...

    • 将形状抽象为具有最低要求的基本“形状”类,例如填充和描边颜色、位置、大小、描边等,并且知道如何绘制自身。
    • 我将创建某种模型,使您能够分离和定义责任边界。 “管理”形状不是组件的责任,它只关心在其表面上绘制它们。同样,组件并不关心“形状”是什么,它只想知道如何绘制它们......
    • 我会使用Actions 来简单地创建这些形状并将它们添加到模型中...

    我只创建了一个三角形(除了位置和大小之外,它没有其他属性),但我相信你会明白的……(ps 你需要提供自己的三角形图标为了行动;))

    public class DrawMe {
    
        public static void main(String[] args) {
            new DrawMe();
        }
    
        public DrawMe() {
    
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException ex) {
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
    
                    DrawModel model = new DefaultDrawModel();
                    model.addElement(new Triangle(new Rectangle(10, 10, 100, 100)));
                    DrawPane drawPane = new DrawPane(model);
    
                    JToolBar toolBar = new JToolBar();
                    toolBar.add(new AddTriangleAction(model));
                    frame.add(toolBar, BorderLayout.NORTH);
    
                    frame.add(drawPane);
                    frame.setSize(400, 400);
                    frame.setVisible(true);
                }
            });
    
        }
    
        /**
         * Simple action used to add triangles to the model...the model acts
         * as a bridge between the action and the UI.
         */
        protected class AddTriangleAction extends AbstractAction {
    
            private DrawModel model;
    
            public AddTriangleAction(DrawModel model) {
                // Supply your own icon
                putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/shape_triangle.png")));
                this.model = model;
            }
    
            public DrawModel getModel() {
                return model;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                // Randomly add the triangles...
                int x = (int)(Math.random() * 400);
                int y = (int)(Math.random() * 400);
                model.addElement(new Triangle(new Rectangle(x, y, 100, 100)));
            }
    
        }
    
        /**
         * This is the background pane, from which the draw pane extends...
         */
        protected class BackgroundPane extends JPanel {
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                int x = getWidth() / 2;
                int y = getHeight() / 2;
    
                Graphics2D g2d = (Graphics2D) g.create();
                RadialGradientPaint rgp = new RadialGradientPaint(
                        new Point(x, y),
                        Math.max(getWidth(), getHeight()),
                        new float[]{0f, 1f},
                        new Color[]{Color.GRAY, Color.WHITE}
                        );
    
                g2d.setPaint(rgp);
                g2d.fill(new Rectangle(0, 0, getWidth(), getHeight()));
    
                g2d.setBackground(Color.BLACK);
                g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
    
                g2d.dispose();
            }
        }
    
        /**
         * This is a simple model, I stole the list model because it was quicker
         * and easier to demonstrate (don't need to write all the listeners)
         */
        public interface DrawModel extends ListModel<DrawMeShape> {
            public void addElement(DrawMeShape shape);
            public void removeElement(DrawMeShape shape);
        }
    
        /**
         * A default implementation of the DrawModel...
         */
        public class DefaultDrawModel extends DefaultListModel<DrawMeShape> implements DrawModel {
            @Override
            public void removeElement(DrawMeShape shape) {
                removeElement((Object)shape);
            }
        }
    
        /**
         * The actually "canvas" that shapes are rendered to
         */
        protected class DrawPane extends BackgroundPane {
    
            // Should provide ability to setModel...
            private DrawModel model;
    
            public DrawPane(DrawModel model) {
                this.model = model;
                model.addListDataListener(new ListDataListener() {
    
                    @Override
                    public void intervalAdded(ListDataEvent e) {
                        repaint();
                    }
    
                    @Override
                    public void intervalRemoved(ListDataEvent e) {
                        repaint();
                    }
    
                    @Override
                    public void contentsChanged(ListDataEvent e) {
                        repaint();
                    }
                });
            }
    
            public DrawModel getModel() {
                return model;
            }
    
            @Override
            protected void paintComponent(Graphics g) {
    
                super.paintComponent(g);
    
                // Draw the shapes from the model...
                Graphics2D g2d = (Graphics2D) g.create();
                DrawModel model = getModel();
                for (int index = 0; index < model.getSize(); index++) {
                    DrawMeShape shape = model.getElementAt(index);
                    shape.paint(g2d, this);
                }
    
                g2d.dispose();
    
            }
    
        }
    
        /**
         * A abstract concept of a shape.  Personally, if I was doing it, I would
         * generate an interface first, but this is just a proof of concept...
         */
        public abstract class DrawMeShape {
    
            private Rectangle bounds;
    
            public void setBounds(Rectangle bounds) {
                this.bounds = bounds;
            }
    
            public Rectangle getBounds() {
                return bounds;
            }
    
            protected abstract Shape getShape();
    
            /**
             * The shape knows how to paint, but it needs to know what to paint...
             * @param g2d
             * @param parent 
             */
            public void paint(Graphics2D g2d, JComponent parent) {
                g2d = (Graphics2D) g2d.create();
                Rectangle bounds = getBounds();
                Shape shape = getShape();
                g2d.translate(bounds.x, bounds.y);
                g2d.setColor(Color.DARK_GRAY);
                g2d.fill(shape);
                g2d.setColor(Color.BLACK);
                g2d.draw(shape);
                g2d.dispose();
            }
    
        }
    
        /**
         * An implementation of a Triangle shape...
         */
        public class Triangle extends DrawMeShape {
    
            public Triangle(Rectangle bounds) {
                setBounds(bounds);
            }
    
            @Override
            protected Shape getShape() {
                // This should be cached ;)
                Path2D path = new Path2D.Float();
                Rectangle bounds = getBounds();
    
                path.moveTo(bounds.width / 2, 0);
                path.lineTo(bounds.width, bounds.height);
                path.lineTo(0, bounds.height);
                path.lineTo(bounds.width / 2, 0);
                path.closePath();
    
                return path;
            }
        }
    }
    

    画得好……

    【讨论】:

    • "(ps 你需要为动作提供你自己的三角形图标;))" 或者可能使用金属PLAF的OptionPane.warningIcon(其中基本上是三角形的)。 ;)
    【解决方案2】:

    建议:

    • 请勿删除 super.paintComponent(g),因为它有必要发挥重要作用。
    • 为什么不绘制到 BufferedImage,然后在 paintComponent(...) 方法覆盖中绘制该 BufferedImage。
    • 然后,如果您想擦除绘制的图像,只需创建一个新的 BufferedImage,或在其上绘制。

    顺便说一句,不要使用== 比较字符串。请改用equals(...)equalsIgnoreCase(...) 方法。了解 == 检查两个 objects 是否相同,这不是您感兴趣的。另一方面,这些方法检查两个字符串是否具有相同顺序的相同字符,并且这就是这里重要的事情。所以不是

    if (fu == "bar") {
      // do something
    }
    

    做,

    if ("bar".equals(fu)) {
      // do something
    }
    

    或者,

    if ("bar".equalsIgnoreCase(fu)) {
      // do something
    }
    

    【讨论】:

    • Graphics 上下文在组件之间共享。 super.paintComponent 的任务之一是在绘制之前“清理”图形上下文...这就是为什么您会看到两个版本的按钮...
    • @HovercraftFullOfEels 有趣的旁注,无论出于何种原因,JVM 都会重用字符串常量——所以如果你输入“String”==“String”,它通常会出现。我想这就是为什么它对 Dog “工作” - 但你 100% 正确,这不是正确的检查方法。只是一个有趣的侧边栏。
    • @Nick:JVM 会尝试将字符串存储在字符串池中,以便它们可以重复使用,但不能保证这一点,事实上,虽然它在当前代码中工作,但非常相似的设置会失败。因此,最好使用equals(...)equalsIgnoreCase(...)
    • 酷,以前从未听说过缓冲图像。我刚刚通过在我的代码中遵循这篇帖子stackoverflow.com/questions/6105393/… 来实现它,现在我可以在以前的形状不消失的情况下进行绘制:)
    • @Dog 您可以覆盖FoosetSize 方法,也可以将BufferedImage 替换为不同大小的新图像,然后将旧图像绘制到新图像上。跨度>
    猜你喜欢
    • 2021-04-21
    • 2020-04-14
    • 2017-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多