使用 JPanel 的 addMouseListener() 和 paintComponent()

Posted

技术标签:

【中文标题】使用 JPanel 的 addMouseListener() 和 paintComponent()【英文标题】:Using addMouseListener() and paintComponent() for JPanel 【发布时间】:2012-08-25 20:55:35 【问题描述】:

这是我的previous 问题的后续。我已经尽可能地简化了事情,但它仍然不起作用!虽然我使用getGraphics() 得到了好处。

非常感谢您详细解释这里出了什么问题。我怀疑我在这里使用addMouseListener() 方法的方式有问题。

EDIT 完全重写了代码。虽然仍然无法正常工作。

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class RunClass

    static MainClass1 inst1 = new MainClass1();



    public static void main(String args[])

        JFrame frame1 = new JFrame();
        frame1.add(inst1);
        frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame1.setTitle("NewPaintToolbox");
        frame1.setSize(200, 200);
        frame1.setLocationRelativeTo(null);     
        frame1.setVisible(true);                
    


class MainClass1 extends JPanel implements MouseListener, MouseMotionListener

    int xvar=30;
    int yvar=30;
    //static PaintClass22 inst1 = new PaintClass22();


    @Override
    public void mouseClicked(MouseEvent arg0) 
        // TODO Auto-generated method stub
        xvar = arg0.getX();
        yvar = arg0.getY();
        repaint(xvar,yvar,10,10);

       

    @Override
    public void mouseEntered(MouseEvent arg0) 
        // TODO Auto-generated method stub

    

    @Override
    public void mouseExited(MouseEvent arg0) 
        // TODO Auto-generated method stub

    

    @Override
    public void mousePressed(MouseEvent arg0) 
        // TODO Auto-generated method stub

    

    @Override
    public void mouseReleased(MouseEvent e) 
        // TODO Auto-generated method stub

    

    @Override
    public void mouseDragged(MouseEvent arg0) 
        // TODO Auto-generated method stub
        xvar = arg0.getX();
        yvar = arg0.getY();
        repaint(xvar,yvar,10,10);

    

    @Override
    public void mouseMoved(MouseEvent arg0) 
        // TODO Auto-generated method stub

    
    @Override
    public void paintComponent(Graphics g)

        super.paintComponent(g);
        g.setColor(Color.RED);
        g.fillRect(xvar, yvar, 10, 10);     

    




【问题讨论】:

【参考方案1】:

首先,PaintClass11 从未使用过...

其次,即使是这样,您也要创建两个单独的 PaintClass22 实例,一个放置在框架上,一个您尝试更新...因此屏幕上永远不会发生更新。

您不需要两个单独的类。将 PaintClass11PaintClass22 合并到一个类中并将其添加到您的框架中

【讨论】:

我听从了你的建议,请看代码。 paintComponent 在初始化时工作一次,但显然 MouseListener 有问题。【参考方案2】:

注意你方法中的错字:

public void paintComponent(Graphics g)

    super.paintComponents(g);
    g.setColor(Color.RED);
    g.fillRect(varx, vary, 10, 10);     


当您调用super.paintComponents(复数)时,您的方法名为paintComponent。如果你重写了一个方法(我强烈建议添加一个@Override 标记以避免由于拼写错误而实际上你没有重写任何东西),比如paintComponent,请确保调用super 方法(相同的方法,而不是另一个)。

【讨论】:

【参考方案3】:

您必须将 mouseListener 添加到面板。默认情况下,这不会像您预期的那样发生;-)

MainClass1() 
    addMouseListener(this);

顺便说一句:不建议公开仅供内部使用的公共 api。因此,不要让面板实现 MouseListener(强制公开曝光),而是让面板创建和使用 MouseListener:

private MouseListener mouseListener;
MainClass1() 
   mouseListener = createMouseListener();
   addMouseListener(mouseListener);


protected MouseListener createMouseListener() 
    MouseListener l = new MouseListener() 

    
   return l;

顺便说一句 2:在有限区域调用重绘并不是您想要的 (?) - 它 暂时 将正方形添加到绘画中,每当重绘整个面板时它们就会丢失 (与 getGraphics 的效果相同)。取决于你真正想要什么,

在最近点击的位置绘制一个正方形:调用 repaint() 在曾经点击过的所有位置绘制方块:将位置存储在列表中并实施重绘以循环遍历该列表。在这里你可以调用带参数的重绘,但是为什么要麻烦呢?

【讨论】:

好的,谢谢,这行得通。是的,如果添加了更改(例如调整窗口大小),我需要保留用户的输入。在MouseActionListener 实现之前是否可以避免运行paintComponent【参考方案4】:

这不是您问题的直接答案,但知道(或至少怀疑)您希望提供什么(一个简单的绘画程序),我建议从基于 BufferedImage 的这种方法开始作为绘画表面..

import java.awt.*;
import java.awt.RenderingHints.Key;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

public class BasicPaint 

    /** Reference to the original image. */
    private BufferedImage originalImage;
    /** Image used to make changes. */
    private BufferedImage canvasImage;
    /** The main GUI that might be added to a frame or applet. */
    private JPanel gui;
    /** The color to use when calling clear, text or other 
     * drawing functionality. */
    private Color color = Color.WHITE;
    /** General user messages. */
    private JLabel output = new JLabel("You DooDoodle!");

    private BufferedImage colorSample = new BufferedImage(
            16,16,BufferedImage.TYPE_INT_RGB);
    private JLabel imageLabel;
    private int activeTool;
    public static final int SELECTION_TOOL = 0;
    public static final int DRAW_TOOL = 1;
    public static final int TEXT_TOOL = 2;

    private Point selectionStart; 
    private Rectangle selection;
    private boolean dirty = false;
    private Stroke stroke = new BasicStroke(
            3,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND,1.7f);
    private RenderingHints renderingHints;

    public JComponent getGui() 
        if (gui==null) 
            Map<Key, Object> hintsMap = new HashMap<RenderingHints.Key,Object>();
            hintsMap.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            hintsMap.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            hintsMap.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            renderingHints = new RenderingHints(hintsMap); 

            setImage(new BufferedImage(320,240,BufferedImage.TYPE_INT_RGB));
            gui = new JPanel(new BorderLayout(4,4));
            gui.setBorder(new EmptyBorder(5,3,5,3));

            JPanel imageView = new JPanel(new GridBagLayout());
            imageView.setPreferredSize(new Dimension(480,320));
            imageLabel = new JLabel(new ImageIcon(canvasImage));
            JScrollPane imageScroll = new JScrollPane(imageView);
            imageView.add(imageLabel);
            imageLabel.addMouseMotionListener(new ImageMouseMotionListener());
            imageLabel.addMouseListener(new ImageMouseListener());
            gui.add(imageScroll,BorderLayout.CENTER);

            JToolBar tb = new JToolBar();
            tb.setFloatable(false);
            JButton colorButton = new JButton("Color");
            colorButton.setMnemonic('o');
            colorButton.setToolTipText("Choose a Color");
            ActionListener colorListener = new ActionListener() 
                public void actionPerformed(ActionEvent arg0) 
                    Color c = JColorChooser.showDialog(
                            gui, "Choose a color", color);
                    if (c!=null) 
                        setColor(c);
                    
                
            ;
            colorButton.addActionListener(colorListener);
            colorButton.setIcon(new ImageIcon(colorSample));
            tb.add(colorButton);

            setColor(color);

            final SpinnerNumberModel strokeModel = 
                    new SpinnerNumberModel(3,1,16,1);
            JSpinner strokeSize = new JSpinner(strokeModel);
            ChangeListener strokeListener = new ChangeListener() 
                @Override
                public void stateChanged(ChangeEvent arg0) 
                    Object o = strokeModel.getValue();
                    Integer i = (Integer)o; 
                    stroke = new BasicStroke(
                            i.intValue(),
                            BasicStroke.CAP_ROUND,
                            BasicStroke.JOIN_ROUND,
                            1.7f);
                
            ;
            strokeSize.addChangeListener(strokeListener);
            strokeSize.setMaximumSize(strokeSize.getPreferredSize());
            JLabel strokeLabel = new JLabel("Stroke");
            strokeLabel.setLabelFor(strokeSize);
            strokeLabel.setDisplayedMnemonic('t');
            tb.add(strokeLabel);
            tb.add(strokeSize);

            tb.addSeparator();

            ActionListener clearListener = new ActionListener() 
                public void actionPerformed(ActionEvent arg0) 
                    int result = JOptionPane.OK_OPTION;
                    if (dirty) 
                        result = JOptionPane.showConfirmDialog(
                                gui, "Erase the current painting?");
                    
                    if (result==JOptionPane.OK_OPTION) 
                        clear(canvasImage);
                    
                
            ;
            JButton clearButton = new JButton("Clear");
            tb.add(clearButton);
            clearButton.addActionListener(clearListener);

            gui.add(tb, BorderLayout.PAGE_START);

            JToolBar tools = new JToolBar(JToolBar.VERTICAL);
            tools.setFloatable(false);
            JButton crop = new JButton("Crop");
            final JRadioButton select = new JRadioButton("Select", true);
            final JRadioButton draw = new JRadioButton("Draw");
            final JRadioButton text = new JRadioButton("Text");

            tools.add(crop);            
            tools.add(select);          
            tools.add(draw);            
            tools.add(text);

            ButtonGroup bg = new ButtonGroup();
            bg.add(select);
            bg.add(text);
            bg.add(draw);
            ActionListener toolGroupListener = new ActionListener() 
                @Override
                public void actionPerformed(ActionEvent ae) 
                    if (ae.getSource()==select) 
                        activeTool = SELECTION_TOOL;
                     else if (ae.getSource()==draw) 
                        activeTool = DRAW_TOOL;
                     else if (ae.getSource()==text) 
                        activeTool = TEXT_TOOL;
                    
                
            ;
            select.addActionListener(toolGroupListener);
            draw.addActionListener(toolGroupListener);
            text.addActionListener(toolGroupListener);

            gui.add(tools, BorderLayout.LINE_END);

            gui.add(output,BorderLayout.PAGE_END);
            clear(colorSample);
            clear(canvasImage);
        

        return gui;
    

    /** Clears the entire image area by painting it with the current color. */
    public void clear(BufferedImage bi) 
        Graphics2D g = bi.createGraphics();
        g.setRenderingHints(renderingHints);
        g.setColor(color);
        g.fillRect(0, 0, bi.getWidth(), bi.getHeight());

        g.dispose();
        imageLabel.repaint();
    

    public void setImage(BufferedImage image) 
        this.originalImage = image;
        int w = image.getWidth();
        int h = image.getHeight();
        canvasImage = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);

        Graphics2D g = this.canvasImage.createGraphics();
        g.setRenderingHints(renderingHints);
        g.drawImage(image, 0, 0, gui);
        g.dispose();

        selection = new Rectangle(0,0,w,h); 
        if (this.imageLabel!=null) 
            imageLabel.setIcon(new ImageIcon(canvasImage));
            this.imageLabel.repaint();
        
        if (gui!=null) 
            gui.invalidate();
        
    

    /** Set the current painting color and refresh any elements needed. */
    public void setColor(Color color) 
        this.color = color;
        clear(colorSample);
    

    private JMenu getFileMenu(boolean webstart)
        JMenu file = new JMenu("File");
        file.setMnemonic('f');

        JMenuItem newImageItem = new JMenuItem("New");
        newImageItem.setMnemonic('n');
        ActionListener newImage = new ActionListener() 
            @Override
            public void actionPerformed(ActionEvent arg0) 
                BufferedImage bi = new BufferedImage(
                        360, 300, BufferedImage.TYPE_INT_ARGB);
                clear(bi);
                setImage(bi);
            
        ;
        newImageItem.addActionListener(newImage);
        file.add(newImageItem);

        if (webstart) 
            //TODO Add open/save functionality using JNLP API
         else 
            //TODO Add save functionality using J2SE API
            file.addSeparator();
            ActionListener openListener = new ActionListener() 
                @Override
                public void actionPerformed(ActionEvent arg0) 
                    if (!dirty) 
                        JFileChooser ch = getFileChooser();
                        int result = ch.showOpenDialog(gui);
                        if (result==JFileChooser.APPROVE_OPTION ) 
                            try 
                                BufferedImage bi = ImageIO.read(
                                        ch.getSelectedFile());
                                setImage(bi);
                             catch (IOException e) 
                                showError(e);
                                e.printStackTrace();
                            
                        
                     else 
                        // TODO
                        JOptionPane.showMessageDialog(
                                gui, "TODO - prompt save image..");
                    
                
            ;
            JMenuItem openItem = new JMenuItem("Open");
            openItem.setMnemonic('o');
            openItem.addActionListener(openListener);
            file.add(openItem);

            ActionListener saveListener = new ActionListener() 

                @Override
                public void actionPerformed(ActionEvent e) 
                    JFileChooser ch = getFileChooser();
                    int result = ch.showSaveDialog(gui);
                    if (result==JFileChooser.APPROVE_OPTION ) 
                        try 
                            File f = ch.getSelectedFile();
                            ImageIO.write(BasicPaint.this.canvasImage, "png", f);
                            BasicPaint.this.originalImage = BasicPaint.this.canvasImage;
                            dirty = false;
                         catch (IOException ioe) 
                            showError(ioe);
                            ioe.printStackTrace();
                        
                    
                
            ;
            JMenuItem saveItem = new JMenuItem("Save");
            saveItem.addActionListener(saveListener);
            saveItem.setMnemonic('s');
            file.add(saveItem);
        

        if (canExit()) 
            ActionListener exit = new ActionListener() 
                @Override
                public void actionPerformed(ActionEvent arg0) 
                    // TODO Auto-generated method stub
                    System.exit(0);
                
            ;
            JMenuItem exitItem = new JMenuItem("Exit");
            exitItem.setMnemonic('x');
            file.addSeparator();
            exitItem.addActionListener(exit);
            file.add(exitItem);
        

        return file;
    

    private void showError(Throwable t) 
        JOptionPane.showMessageDialog(
                gui, 
                t.getMessage(), 
                t.toString(), 
                JOptionPane.ERROR_MESSAGE);
    

    JFileChooser chooser = null;

    public JFileChooser getFileChooser() 
        if (chooser==null) 
            chooser = new JFileChooser();
            FileFilter ff = new FileNameExtensionFilter("Image files", ImageIO.getReaderFileSuffixes());
            chooser.setFileFilter(ff);
        
        return chooser;

    

    public boolean canExit() 
        boolean canExit = false;
        SecurityManager sm = System.getSecurityManager();
        if (sm==null) 
            canExit = true;
         else 
            try 
                sm.checkExit(0);
                canExit = true; 
             catch(Exception stayFalse) 
            
        

        return canExit;
    

    public JMenuBar getMenuBar(boolean webstart)
        JMenuBar mb = new JMenuBar();
        mb.add(this.getFileMenu(webstart));
        return mb;
    

    public static void main(String[] args) 
        Runnable r = new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(
                            UIManager.getSystemLookAndFeelClassName());
                 catch (Exception e) 
                    // use default
                
                BasicPaint bp = new BasicPaint();

                JFrame f = new JFrame("DooDoodle!");
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setLocationByPlatform(true);

                f.setContentPane(bp.getGui());
                f.setJMenuBar(bp.getMenuBar(false));

                f.pack();
                f.setMinimumSize(f.getSize());
                f.setVisible(true);
            
        ;
        SwingUtilities.invokeLater(r);
    

    public void text(Point point) 
        String text = JOptionPane.showInputDialog(gui, "Text to add", "Text");
        if (text!=null) 
            Graphics2D g = this.canvasImage.createGraphics();
            g.setRenderingHints(renderingHints);
            g.setColor(this.color);
            g.setStroke(stroke);
            int n = 0;
            g.drawString(text,point.x,point.y);
            g.dispose();
            this.imageLabel.repaint();
        
    

    public void draw(Point point) 
        Graphics2D g = this.canvasImage.createGraphics();
        g.setRenderingHints(renderingHints);
        g.setColor(this.color);
        g.setStroke(stroke);
        int n = 0;
        g.drawLine(point.x, point.y, point.x+n, point.y+n);
        g.dispose();
        this.imageLabel.repaint();
    

    class ImageMouseListener extends MouseAdapter 

        @Override
        public void mousePressed(MouseEvent arg0) 
            // TODO Auto-generated method stub
            if (activeTool==BasicPaint.SELECTION_TOOL) 
                selectionStart = arg0.getPoint();
             else if (activeTool==BasicPaint.DRAW_TOOL) 
                // TODO
                draw(arg0.getPoint());
             else if (activeTool==BasicPaint.TEXT_TOOL) 
                // TODO
                text(arg0.getPoint());
             else 
                JOptionPane.showMessageDialog(
                        gui, 
                        "Application error.  :(", 
                        "Error!", 
                        JOptionPane.ERROR_MESSAGE);
            
        

        @Override
        public void mouseReleased(MouseEvent arg0) 
            if (activeTool==BasicPaint.SELECTION_TOOL) 
                selection = new Rectangle(
                        selectionStart.x,
                        selectionStart.y,
                        arg0.getPoint().x,
                        arg0.getPoint().y);
            
        
    

    class ImageMouseMotionListener implements MouseMotionListener 

        @Override
        public void mouseDragged(MouseEvent arg0) 
            reportPositionAndColor(arg0);
            if (activeTool==BasicPaint.SELECTION_TOOL) 
                selection = new Rectangle(
                        selectionStart.x,
                        selectionStart.y,
                        arg0.getPoint().x-selectionStart.x,
                        arg0.getPoint().y-selectionStart.y);
             else if (activeTool==BasicPaint.DRAW_TOOL) 
                draw(arg0.getPoint());
            
        

        @Override
        public void mouseMoved(MouseEvent arg0) 
            reportPositionAndColor(arg0);
        

    

    private void reportPositionAndColor(MouseEvent me) 
        String text = "";
        if (activeTool==BasicPaint.SELECTION_TOOL) 
            text += "Selection (X,Y:WxH): " + 
                    (int)selection.getX() +
                    "," +
                    (int)selection.getY() +
                    ":" +
                    (int)selection.getWidth() +
                    "x" +
                    (int)selection.getHeight();
         else 
            text += "X,Y: " + (me.getPoint().x+1) + "," + (me.getPoint().y+1);
        
        output.setText(text);
    

这个来源非常不完整。

它有很多部分带有// TODO dirty 属性已声明,但从未以任何有意义的方式使用。 ..

这只是我今天一起破解的东西,并认为应该在它达到发布限制之前显示出来。

哦,不要去寻找任何'OO设计',因为我没有放入任何东西。如果有的话,那只是偶然。这段代码旨在演示什么是可能的以及如何开始做。

【讨论】:

除了两个版本(两个版本的代码都很棒!!!),我正在等待超过 5 个投票,奇怪......我的帽子下来 哇,太棒了!你花了多长时间完成它?很遗憾我不能接受两个答案。 我一整天都在工作,时不时地。我一直打算提供一个小的“绘画应用程序”。很长一段时间了。正是你的问题启发了我去 DooDoodle .. :) 至于“勾号”,它在 kleopatra 的答案中很好。虽然我的回答很好(IMO),但它不是您提出的问题的答案。公认的答案是。当然,我希望让你远离为这幅画扩展面板。所以,关于下一个问题.. ;) 谢谢,这确实是一个很棒的答案,因为它比我想要的要多得多:我接下来要问关于保存图像的问题。但我们会看到... +1 非常漂亮的 paint 风格程序,与 this object drawing 程序对比。

以上是关于使用 JPanel 的 addMouseListener() 和 paintComponent()的主要内容,如果未能解决你的问题,请参考以下文章

Java gridbaglayout 使用带有可拖动 jpanel 的滚动窗格

第十二周编程总结

如何将每个 JPanel 向左或向右对齐到主 JPanel

绘制使用自己的 Painter 创建的 JPanel

透明JPanel

JPanel 自定义背景