【问题标题】:paintComponent() vs paint() and JPanel vs Canvas in a paintbrush-type GUI画笔类型 GUI 中的paintComponent() vs paint() 和 JPanel vs Canvas
【发布时间】:2012-08-23 21:36:35
【问题描述】:

我从thisthisthis 的帖子中得到了一些有趣的想法和批评(有关相关 GUI 的代码,请参阅上一篇帖子)。尽管如此,我仍然对一些事情感到很困惑。主要是,显示用户介绍图形的最便宜的方式是什么?

更具体地说,我使用了来自JPanel 类的paintComponent() 方法,方法是在MouseDragged() 方法和paintComponent(getGraphics()) 方法中创建此类的一个对象(相应地是AuxClass2AuxClass1)。

显然,使用 getGraphics()paintComponent() 而不是 repaint() 是坏主意,我怀疑与内存使用有关。每次用户拖动鼠标时调用AuxClass2也是一个坏主意。

此外,JPanel 与 Canvas(即 swing 与 awt)有点令人困惑。什么时候用什么?

我一直在寻找解决方法,但没有找到,尤其是getGraphics() 方法:图形如何添加到面板中?

【问题讨论】:

    标签: java swing user-interface awt jpanel


    【解决方案1】:

    重与轻

    基本上,重量级组件链接到它自己的本地对等点,其中轻量级组件共享一个共同的本地对等点。

    一般来说,混合重量和重量组件并不是一个好主意,因为 z 顺序存在问题,而且根据我的经验(即使现在应该更好),可能会出现绘画问题。

    这就是不鼓励您使用 Canvas 类的原因,可能是因为您试图将它放在轻量级组件上...我猜

    控制的错觉

    对于Swing API 的新手来说,最大的问题之一是错觉,您对绘画过程有某种控制权,而您却没有。接受它更容易。

    您能做的最好的事情就是请求重绘管理器尽早执行更新。

    另外,调用getGraphics 不能保证返回非空值。

    事情的正确顺序

    paintpaintComponent

    这里的问题是paint 做了许多重要的工作,调用paintComponent 只是其中之一。

    在 Swing 中,我们非常鼓励我们在想要执行自定义绘制时使用 paintComponent,这通常是组件上的最低级别,并且在绘制子组件之前调用。

    如果您覆盖paint,然后在调用super.paint 后在Graphics 上绘画,您最终会在所有内容之上绘画,这并不总是理想的结果

    即使是这样,子组件也可以独立于其父容器进行绘制,从而使绘制“覆盖”您可能添加的任何绘制效果

    有用的链接

    分手的想法

    只有实际添加到组件中的组件(附加到本地对等方)才会调用 paint 方法。所以尝试绘制到尚未添加到容器中的组件是毫无意义的......

    【讨论】:

    • 我有点想给这个竖起大拇指,但是“正确的事情顺序”的解释让我有点困惑。关于“paint”的部分对我来说很有意义,但它如何与paintComponent 相适应对我来说不太清楚(除了它发生在子组件被绘制之前编辑:链接“Painting in Swing”已损坏)。我会尝试您提供的链接,看看是否能给我更多的见解。
    • @searchengine27 一般来说,绘制链看起来像paint -> paintComponent -> paintBorder -> paintChildren。很容易错过调用super.paint 并获得奇怪而奇怪的绘画结果,而且,很容易覆盖绘画(通过在您完成自定义绘画之后调用super.paint)或在子组件上完成绘画,这听起来可能是个好主意,但是,每个子组件都可以独立于父容器进行绘制,这意味着您最初在顶部绘制的任何内容都可能被撤消
    • 我肯定遇到过这样的问题,但我无法弄清楚如何解决它。本质上,我有一个带有 BorderLayout 的 JPanel。里面有 3 个 JList,2 个有边框,一个没有边框,所有内容都不同。我需要已经绘制 JList 以初始化 Graphics 对象,以便使用 Graphics 来确定列表内文本的宽度和边框文本的宽度(使用带文本的边框)。问题是,我需要父 JPanel 根据任何列表中任何文本的最大宽度调整大小,但我似乎无法弄清楚顺序
    【解决方案2】:

    ..画笔式图形用户界面..

    使用BufferedImage 作为绘画表面。在JLabel 中显示它。将标签放在JScrollPane 内的面板中心。

    根据需要拨打bufferedImage.getGraphics(),但记得在完成后拨打dispose(),然后拨打label.repaint()

    始终使用 Swing 组件,不要覆盖任何东西。

    这是example of using an image as painting surface

    还有here is a better one

    我并不是说 屏幕截图 更好,而是代码更好。 ;)

    【讨论】:

    • 诚实地为真正的程序员的艺术 +1
    • @AAAlex123 谢谢!我在等待有人这么说。 (检查邮寄日期)幸运的是我没有屏住呼吸等待。 ;)
    【解决方案3】:

    我一直在寻找解决方法,但没有找到, 特别是对于 getGraphics() 方法:图形怎么可能 添加到面板?

    您记住需要绘制的变量并在paintComponent() 中使用它。 例如,您在另一个问题中似乎试图做的事情如下所示:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class PaintRectangle extends JPanel {
    
        private Point mouseLocation;
    
        public PaintRectangle() {
            setPreferredSize(new Dimension(500, 500));
    
            MouseAdapter listener = new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    updateMouseRectangle(e);
                }
    
                private void updateMouseRectangle(MouseEvent e) {
                    mouseLocation = e.getPoint();
                    repaint();
                }
    
                @Override
                public void mouseDragged(MouseEvent e) {
                    updateMouseRectangle(e);
                }
    
                @Override
                public void mouseReleased(MouseEvent e) {
                    mouseLocation = null;
                    repaint();
                }
            };
            addMouseListener(listener);
            addMouseMotionListener(listener);
        }
    
        private Rectangle getRectangle() {
            if(mouseLocation != null) {
                return new Rectangle(mouseLocation.x - 5, mouseLocation.y - 5, 10, 10);
            }
            else {
                return null;
            }
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Rectangle rectangle = getRectangle();
            if(rectangle != null) {
                Graphics2D gg = (Graphics2D) g;
                gg.setColor(Color.BLUE);
                gg.fill(rectangle);
                gg.setColor(Color.BLACK);
                gg.draw(rectangle);
            }
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                    frame.getContentPane().add(new PaintRectangle());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    }
    

    另见http://docs.oracle.com/javase/tutorial/uiswing/painting/

    【讨论】:

    • 谢谢,问题是,调用repaint() 会擦除之前绘制的内容。能避免吗?
    • 不,因为您不知道需要重新绘制的内容(用户可能已经在您的窗口上移动了另一个窗口),因此绘制组件应该重新绘制整个组件(在指定的剪辑内)。您可以使用 Andrew 方法绘制图像并在 paintComponent 中绘制该图像。
    • 这是否意味着,如果我需要保留整个“绘画”,例如要保存到硬盘,我根本不应该使用CanvasJPanel,而是使用BufferedImage
    • 这不应该是公认的答案。几乎没有解释,作为解释一部分的两个句子之一是一个不完整的句子,没有任何意义,也无法理解,因为没有上下文可以推断出任何东西。
    猜你喜欢
    • 2015-02-04
    • 2015-09-30
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 2011-08-18
    • 2011-11-04
    • 2012-03-12
    相关资源
    最近更新 更多