使用 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
实例,一个放置在框架上,一个您尝试更新...因此屏幕上永远不会发生更新。
您不需要两个单独的类。将 PaintClass11
和 PaintClass22
合并到一个类中并将其添加到您的框架中
【讨论】:
我听从了你的建议,请看代码。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()的主要内容,如果未能解决你的问题,请参考以下文章