My problem is the following: i'm trying to make a paint application in java. I get this error code:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at areacalc.DrawArea.clear(Form.java:67)
at areacalc.DrawArea.mouseMoved(Form.java:115)
when i'm clearing the drawing surface.
Here is my code, could you please help me to avoid the null pointer? Where should i place the event handlers to not to get null pointer?
Thank you so much!
Regards, Stanley.
package areacalc;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
class DrawArea extends JComponent implements MouseListener, MouseMotionListener
{
private Image image;
private Graphics2D g2;
private int currentX, currentY, oldX, oldY, X, Y;
private ArrayList<Integer[]> points = new ArrayList<Integer[]>();
private boolean stopped = false;
public boolean crtl_pressed = false;
public DrawArea()
{
setDoubleBuffered(false);
initEventHandlers();
}
public void initEventHandlers()
{
addMouseListener(this);
addMouseMotionListener(this);
}
public double length(int x1, int x2)
{
return Math.abs(x1-x2);
}
protected void paintComponent(Graphics g)
{
//System.out.println("paintComponent");
if (image == null)
{
image = createImage(getSize().width, getSize().height);
g2 = (Graphics2D) image.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
clear();
}
g.drawImage(image, 0, 0, null);
}
public void clear()
{
g2.setPaint(Color.black);
g2.fillRect(0, 0, getSize().width, getSize().height);
repaint();
}
#Override
public void mousePressed(MouseEvent e)
{
if(e.getButton() == MouseEvent.BUTTON1&&(!stopped))
{
oldX = e.getX();
oldY = e.getY();
if((currentX!=oldX)&&(currentY!=oldY)&&(currentX!=0)&&(currentY!=0))
{
g2.setPaint(Color.green);
Integer i[] = { currentX, currentY, e.getX(), e.getY() };
points.add(i);
g2.drawLine(currentX, currentY, e.getX(), e.getY());
repaint();
}
}
else if(e.getButton() == MouseEvent.BUTTON3&&points.size()>0)
{
g2.setPaint(Color.green);
g2.drawLine(currentX, currentY, e.getX(), e.getY());
if(points.size()>0)
{
Integer i[] = points.get(0);
g2.drawLine(e.getX(), e.getY(), i[0], i[1]);
}
repaint();
stopped = true;
}
}
#Override
public void mouseMoved(MouseEvent e)
{
if((!stopped))
{
//label.setText(e.getX() + ";" + e.getY());
currentX = oldX;
currentY = oldY;
clear();
for(int j = 0; j < points.size(); j++)
{
g2.setPaint(Color.green);
Integer i[] = points.get(j);
g2.drawLine(i[0], i[1], i[2], i[3]);
repaint();
}
if((currentX!=0)&&(currentY!=0))
{
g2.setPaint(Color.yellow);
if(!crtl_pressed)
{
if(points.size()>0)
{
Integer i[] = points.get(0);
g2.drawLine(e.getX(), e.getY(), i[0], i[1]);
}
g2.drawLine(currentX, currentY, e.getX(), e.getY());
}
else
{
if(length(currentX, e.getX())>=length(currentY, e.getY()))
{
if(points.size()>0)
{
Integer i[] = points.get(0);
g2.drawLine(e.getX(), currentY, i[0], i[1]);
}
g2.drawLine(currentX, currentY, e.getX(), currentY);
}
else
{
if(points.size()>0)
{
Integer i[] = points.get(0);
g2.drawLine(currentX, e.getY(), i[0], i[1]);
}
g2.drawLine(currentX, currentY, currentX, e.getY());
}
}
repaint();
}
}
}
public void mouseDragged(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
}
public class Form extends JFrame implements ActionListener
{
public Form()
{
initUI();
}
private void initUI()
{
setTitle("Area Calculator");
setSize(600, 600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
Container content = getContentPane();
content.setLayout(new BorderLayout());
DrawArea drawArea = new DrawArea();
content.add(drawArea, BorderLayout.CENTER);
addKeyListener(
new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_CONTROL);
//drawArea.crtl_pressed = true;
}
public void keyReleased(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_CONTROL);
//drawArea.crtl_pressed = false;
}
});
}
private void doDrawing(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.blue);
g2d.drawLine(20, 20, 500, 500);
}
public void paintComponent(Graphics g)
{
paintComponent(g);
doDrawing(g);
}
#Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
If clear() is called before paintComponent(), the variable g2 is not initialized.
You could initialize your g2 instance in the constructor, but I'm not sure if this works for you because you need the size of the window.
Another option is to insert
if (g2 == null) return;
as first line for clear.
It seems that this is kind of a double buffering implementation. I'm sure Google will help you finding good examples for alternative implementations without this problem.
Related
I'm trying to create an interface where the user can change the color of a line to mark what is background or what is foreground of an image. So far I have coded this:
private class ImageLine extends JComponent
{
java.awt.Point p1,p2;
BufferedImage show;
ArrayList <Shape> shapes = new ArrayList<Shape>();
int flag = 0;
Color color = Color.ORANGE;
public ImageLine(BufferedImage img)
{
show = img;
setPreferredSize(new Dimension(img.getWidth(), img.getHeight()));
this.addMouseListener
(
new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
p1 = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e)
{
p2 = e.getPoint();
Shape r = createLine(p1.x,p1.y,e.getX(),e.getY());
shapes.add(r);
repaint();
}
#Override
public void mouseDragged(MouseEvent e)
{
mouseReleased(e);
repaint();
}
}
);
this.addKeyListener(
new KeyAdapter()
{
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyChar() == 'b')
{
color = Color.GREEN;
System.out.println("bck");
}
if(e.getKeyChar() == 'f')
{
color = Color.RED;
System.out.println("fgr");
}
}
}
);
this.setFocusable(true);
}
private Line2D.Float createLine(int x1, int y1, int x2, int y2)
{
return new Line2D.Float(x1,y1 ,x2, y2);
}
Until this part everything works fine , my real problem comes when I try to override the paintComponent() method, here actually I dont know exactly how to set the color as the keylistener indicates, in this case if the user press the "b" key the color of the line has to change to green, in the other hand if the user press the "f" key the color of the line has to change to red, also if the user draws differents lines , these lines have to remain shown. I have tried with this code without sucess:
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(10));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50f));
for (Shape s : shapes) {
//g2.setPaint(Color.BLACK);
g2.draw(s);
g2.setPaint(color);
//g2.fill(s);
}
if (p1 != null && p2 != null) {
g2.setPaint(Color.CYAN);
Shape r = createLine(p1.x, p1.y, p2.x, p2.y);
g2.draw(r);
}
}
But the result is not what I want, I paint the lines and change the color, but when this happen , the lines I drew before change it´s color automatically to the color selected, and they dont keep it's "original color". Any sugestions? Thanks in advance.
There are a few ways you might be able to achieve this, I kind of like the idea of marrying the Shape and Color together into a single object, but this might present more work as you add more shapes.
Another solution might be to use a Map to simply map the line to a color, for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
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.Line2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawLines {
public static void main(String[] args) {
new DrawLines();
}
public DrawLines() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Line2D currentLine;
private Map<Shape, Color> mapColors;
private Color currentColor;
private List<Shape> shapes;
public TestPane() {
mapColors = new HashMap<>(25);
shapes = new ArrayList<>(25);
InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F, 0), "foreground");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_B, 0), "background");
actionMap.put("foreground", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
currentColor = Color.GREEN;
if (currentLine != null) {
mapColors.put(currentLine, currentColor);
repaint();
}
}
});
actionMap.put("background", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
currentColor = Color.RED;
if (currentLine != null) {
mapColors.put(currentLine, currentColor);
repaint();
}
}
});
MouseAdapter ma = new MouseAdapter() {
private Point p1, p2;
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
currentLine = null;
}
#Override
public void mouseReleased(MouseEvent e) {
p2 = e.getPoint();
if (currentLine == null) {
currentLine = createLine(p1.x, p1.y, e.getX(), e.getY());
} else {
currentLine.setLine(p1, p2);
}
mapColors.put(currentLine, currentColor);
shapes.add(currentLine);
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
p2 = e.getPoint();
if (currentLine == null) {
currentLine = createLine(p1.x, p1.y, e.getX(), e.getY());
} else {
currentLine.setLine(p1, p2);
}
repaint();
}
private Line2D.Float createLine(int x1, int y1, int x2, int y2) {
return new Line2D.Float(x1, y1, x2, y2);
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shape shape : shapes) {
Color color = mapColors.get(shape);
if (color == null) {
color = Color.BLACK;
}
g2d.setColor(color);
g2d.draw(shape);
}
if (currentLine != null) {
Color color = mapColors.get(currentLine);
if (color == null) {
color = currentColor;
}
g2d.setColor(color);
g2d.draw(currentLine);
}
g2d.dispose();
}
}
}
Also, I avoid using KeyListener and use the key bindings API instead, it's less troublesome
See How to Use Key Bindings for more details
Check out Custom Painting Approaches.
You would want to use the DrawOnImage approach. It uses a ColoredRectangle class to store information of the color and the Rectangle. Then in the paintComponent() method is loops through all the ColoredRectangle classes and paints the Rectangle.
You will need to add logic to change the Color of a selected Rectangle.
I have an application where the user draws lines. There is a JButton btnClear which, when the user clicks, must clear the drawings, so that the user can draw anew. I use an ActionListener on btnClear to know when it is clicked. I set a boolean Clear so that the correct IF statement is executed in paintComponent(). However, the boolean Clear keeps a False value in paintComponent() although I set it to True just before repaint(). Why is it so?
Note: I tried setting the frame's background to red just for to test the boolean in paintComponent().
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
public class Clipping extends JPanel implements MouseListener, ActionListener
{
static JFrame frame;
static JComboBox cboDraw;
static JButton btnClear;
static JButton btnClip;
double x1, y1, x2, y2;
boolean FirstPoint;
boolean Clear = false;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
CreateFrame();
}
});
}
private static void CreateFrame()
{
frame = new JFrame("Clipping");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Clipping());
frame.setSize(500,500);
frame.setVisible(true);
}
public Clipping()
{
setLayout(new BorderLayout());
JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
PopulateToolBar(toolbar);
add(toolbar, BorderLayout.WEST);
addMouseListener(this);
cboDraw.addMouseListener(this);
btnClip.addActionListener(this);
btnClear.addActionListener(this);
}
private static void PopulateToolBar(JToolBar toolbar)
{
String[] cboList = {"Line", "Polygon"};
cboDraw = new JComboBox(cboList);
cboDraw.setMaximumSize(new Dimension(70,30));
btnClip = new JButton("Set clip area");
btnClear = new JButton("Clear");
toolbar.add(cboDraw);
toolbar.addSeparator();
toolbar.add(btnClip);
toolbar.addSeparator();
toolbar.add(btnClear);
cboDraw.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClip.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClear.setAlignmentX(Component.CENTER_ALIGNMENT);
toolbar.setMargin(new Insets(10,10,10,10));
toolbar.setFloatable(false);
toolbar.setBackground(Color.black);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
if (cboDraw.getSelectedIndex() == 0) //draw line
{
g2.draw(new Line2D.Double(x1, y1, x2, y2));
}
else if (Clear == true)
{
frame.setBackground(Color.red); //ONLY FOR TESTING PURPOSE
}
}
public void mousePressed(MouseEvent e)
{
if (e.getSource() != cboDraw) //to prevent line coordinates from being saved when selecting from combobox
{
if (cboDraw.getSelectedIndex() == 0) //user wants to draw line
{
if (FirstPoint == false) //first coordinates
{
x1 = e.getX();
y1 = e.getY();
FirstPoint = true;
}
else //second coordinates
{
x2 = e.getX();
y2 = e.getY();
repaint();
FirstPoint = false;
}
}
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == btnClear)
{
Clear = true;
repaint();
Clear = false;
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
You should just do setBackground(Color.red); to call it on your JPanel instead of the JFrame
I think your actionPerformed and mousePressed are both executed at the same time. In mousePressed your if condition inside mousePressed is satisfied also if btnClear() is the source. So repaint method is called anyway, and you see no changes.
You're not calling the super.paintComponent(g) method within your paintComponent method override. Call this first, else, clear won't work.
Note that in your current code, this will not work for you since you're painting incorrectly in that you're not specifying, iterating through and drawing all the lines that need to be drawn within your paintComponent method. The way to solve this is via one of two ways:
Either create a List<Line2D> such as an ArrayList<Line2D>, fill it in your MouseListener/MouseMotionListener code, and then iterate through this List in your paintComponent method, drawing each line. If you do this, then your clear button's action would be to simply clear the List via clear() and call repaint(). No need for a boolean.
Or you could draw your lines onto a BufferedImage, and then draw the BufferedImage in your paintComponent method via the g.drawImage(...) method. If you do this, always check that the image is not null before drawing. Then in your mouse listening code, you'd draw to this image. In your clear button action, you'd create a new BufferedImage, or clear the current BufferedImage.
Also get rid of setBackground(...) calls from within paintComponent as they really shouldn't be in there.
For example of use of an ArrayList of lines:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawPanelViaArrayList extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color LINES_COLOR = Color.black;
private static final Color DRAW_LINE_COLOR = Color.pink;
private static final Stroke STROKE = new BasicStroke(3f);
private List<Line2D> lineList = new ArrayList<>();
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
public DrawPanelViaArrayList() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
add(new JButton(new ClearAction("Clear")));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// first draw the temporary line to show the user
// where he's drawing
if (x1 != x2 && y1 != y2) {
g2.setColor(DRAW_LINE_COLOR);
g2.drawLine(x1, y1, x2, y2);
}
// then draw all the lines held in the linesList.
Stroke oldStroke = g2.getStroke();
g2.setColor(LINES_COLOR);
g2.setStroke(STROKE); // draw thicker lines
for (Line2D line2d : lineList) {
g2.draw(line2d);
}
g2.setStroke(oldStroke); // reset stroke
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getPoint().x;
y1 = e.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
Line2D line = new Line2D.Double(x1, y1, x2, y2);
// add line to ArrayList
lineList.add(line);
x1 = x2 = y1 = y2 = 0;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
// draw temporary line
repaint();
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
lineList.clear();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawPanelViaArrayList());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
And what the heck, a BufferedImage version
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawPanelViaBufferedImage extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color LINES_COLOR = Color.black;
private static final Color DRAW_LINE_COLOR = Color.pink;
public static final Color CLEAR_COLOR = new Color(0, 0, 0, 0);
public static final Stroke STROKE = new BasicStroke(3f);
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
private BufferedImage img = new BufferedImage(PREF_W, PREF_W, BufferedImage.TYPE_INT_ARGB);
public DrawPanelViaBufferedImage() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
add(new JButton(new ClearAction("Clear")));
// if the GUI is to be re-sizeable, then consider adding a
// ComponentListener here, and resizing the BufferedImage in it
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// first draw the temporary line to show the user
// where he's drawing
if (x1 != x2 && y1 != y2) {
g2.setColor(DRAW_LINE_COLOR);
g2.drawLine(x1, y1, x2, y2);
}
// then draw the BufferedImage if not null
if (img != null) {
g2.drawImage(img, 0, 0, null);
}
}
// size of our GUI
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getPoint().x;
y1 = e.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
Line2D line = new Line2D.Double(x1, y1, x2, y2);
// draw to the BufferedImage
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(STROKE);
g2.setColor(LINES_COLOR);
g2.draw(line);
g2.dispose();
x1 = x2 = y1 = y2 = 0;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
repaint();
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g2 = img.createGraphics();
g2.setBackground(CLEAR_COLOR);
g2.clearRect(0, 0, getWidth(), getHeight());
g2.dispose();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawPanelViaBufferedImage());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
So I am writing a program in Java, and one of the important things it needs to be able to do is render multiple objects onto the screen that can be dragged and dropped by the user. I have made a class (pasted below), but when I try to initialise one with
new DragNode();
it doesn't work.
Can someone please tell me what I am doing wrong?
Thanks,
Sam
public static class DragNode extends JLabel{
public static final long serialVersionUID=155L;
public class MA extends MouseAdapter{
public void mousePressed(MouseEvent e) {
preX = rect.x - e.getX();
preY = rect.y - e.getY();
if (rect.contains(e.getX(), e.getY())) {
updateLocation(e);
System.out.println("Mouse pressed on rectangle");
} else {
pressOut = true;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (!pressOut) {
updateLocation(e);
} else {
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (rect.contains(e.getX(), e.getY())) {
updateLocation(e);
} else {
pressOut = false;
}
}
public void updateLocation(MouseEvent e) {
rect.setLocation(preX + e.getX(), preY + e.getY());
checkRect();
repaint();
}
}
public int x,y;
Rectangle rect,area;
int preX,preY;
boolean firstTime=true;
boolean pressOut=false;
private Dimension dim=getGraphPanelSize();
private Image Node_Sprite;
public DragNode(){
init();
}
public DragNode(int x,int y){
this.x=x;
this.y=y;
init();
}
public void init(){
//setBackground(Color.GRAY);
setOpaque(false);
addMouseMotionListener(new MA());
addMouseListener(new MA());
rect = new Rectangle(x, y, 50, 50);
area=new Rectangle(dim);
try{
Node_Sprite=ImageIO.read(new File("Node_Sprite.png"));
}catch(IOException ioe){}
}
boolean checkRect() {
if (area == null) {
return false;
}
if (area.contains(rect.x, rect.y, rect.getWidth(), rect.getHeight())) {
return true;
}
int new_x = rect.x;
int new_y = rect.y;
if ((rect.x + rect.getWidth()) > area.getWidth()) {
new_x = (int) area.getWidth() - (int) (rect.getWidth() - 1);
}
if (rect.x < 0) {
new_x = -1;
}
if ((rect.y + rect.getHeight()) > area.getHeight()) {
new_y = (int) area.getHeight() - (int) (rect.getHeight() - 1);
}
if (rect.y < 0) {
new_y = -1;
}
rect.setLocation(new_x, new_y);
return false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
if (firstTime) {
rect.setLocation(x,y);
firstTime = false;
}
g2d.setColor(new Color(0,0,0,0));
g2d.drawImage(Node_Sprite, rect.x,rect.y,50,50,null);
}
}
When I replace the code for drawing the rectangle with
g2d.setColor(Color.red);
g2d.fillRect( rect.x, rect.y, 50, 50);
// g2d.drawImage(Node_Sprite, rect.x, rect.y, 50, 50, null);
It is drawn and can be dragged in the x-direction. So there might be something wrong with your image (does it exist in the specified folder?). I expected the dragging to be in both directions, so there might be an issue there as well for the y-direction.
EDIT: Ok, I tried it with an image as well, works well. I also saw that your "borders" 'dim' are set very narrow, so I increased them to 300/300 so I can drag around my image in both axis.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import net.miginfocom.swing.MigLayout;
public class DragNode extends JLabel {
public static final long serialVersionUID = 155L;
public static void main(String[] args) {
JLabel node = new DragNode();
JFrame frame = new JFrame("blupp");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new MigLayout(""));
frame.getContentPane().add(node, "grow, push");
frame.setLocationRelativeTo(null);
frame.setSize(new Dimension(500, 300));
frame.setVisible(true);
}
public class MA extends MouseAdapter {
public void mousePressed(MouseEvent e) {
preX = rect.x - e.getX();
preY = rect.y - e.getY();
if (rect.contains(e.getX(), e.getY())) {
updateLocation(e);
System.out.println("Mouse pressed on rectangle");
} else {
pressOut = true;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (!pressOut) {
updateLocation(e);
} else {}
}
#Override
public void mouseReleased(MouseEvent e) {
if (rect.contains(e.getX(), e.getY())) {
updateLocation(e);
} else {
pressOut = false;
}
}
public void updateLocation(MouseEvent e) {
rect.setLocation(preX + e.getX(), preY + e.getY());
checkRect();
repaint();
}
}
public int x, y;
Rectangle rect, area;
int preX, preY;
boolean firstTime = true;
boolean pressOut = false;
private Dimension dim = new Dimension(300, 300);
private Image Node_Sprite;
public DragNode() {
init();
}
public DragNode(int x, int y) {
this.x = x;
this.y = y;
init();
}
public void init() {
//setBackground(Color.GRAY);
setOpaque(false);
addMouseMotionListener(new MA());
addMouseListener(new MA());
rect = new Rectangle(x, y, 50, 50);
area = new Rectangle(dim);
try {
Node_Sprite = ImageIO.read(new File("Node_Sprite.png"));
} catch (IOException ioe) {}
}
boolean checkRect() {
if (area == null) {
return false;
}
if (area.contains(rect.x, rect.y, rect.getWidth(), rect.getHeight())) {
return true;
}
int new_x = rect.x;
int new_y = rect.y;
if ((rect.x + rect.getWidth()) > area.getWidth()) {
new_x = (int) area.getWidth() - (int) (rect.getWidth() - 1);
}
if (rect.x < 0) {
new_x = -1;
}
if ((rect.y + rect.getHeight()) > area.getHeight()) {
new_y = (int) area.getHeight() - (int) (rect.getHeight() - 1);
}
if (rect.y < 0) {
new_y = -1;
}
rect.setLocation(new_x, new_y);
return false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.green);
// g2d.fillRect(10, 10, 100, 100);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
if (firstTime) {
rect.setLocation(x, y);
firstTime = false;
}
g2d.setColor(Color.red);
// g2d.fillRect( rect.x, rect.y, 50, 50);
g2d.drawImage(Node_Sprite, rect.x, rect.y, 50, 50, null);
}
}
Not sure why you are extending JLabel. You are not using the label to paint text or an Icon. Instead you are actually doing the custom painting yourself so you should be extending JComponent.
Or the other approach is to just use a JLabel to display the image and then create your listener to drag the label to a new location and let the label to the painting. You can check out Component Mover which allows you to drag any component.
I've been trying to work this code, it's like when you hover over the start button it should change its color to gray, but whenever i hover over it. nothing happens, can somebody tell me why? i didn't get any error and it seems like my mousemoved listener isn't recognized by the compiler, sorry for my english. I haven't finish it yet but here is the code:
class Contents extends JFrame implements Runnable {
private Image dbi;
private Graphics dbg;
private boolean isStarted, isHovered;
private int x,y,xDir,yDir,bx,by,timer,life,my,mx,mhx,mhy;
private Rectangle startgame = new Rectangle(80,100,150,40);
Contents()
{
super();
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
if(isStarted)
setSize(600,600);
else
{
setSize(300,300);
setBackground(Color.BLUE);
}
setLocationRelativeTo(null);
isStarted = false;
isHovered = false;
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
mx = e.getX();
my = e.getY();
if(mx > startgame.x && mx < startgame.x+startgame.width &&
my > startgame.y && my < startgame.y+startgame.height)
{
isStarted = true;
}
}
public void mouseMoved(MouseEvent e)
{
mhx = e.getX();
mhy = e.getY();
if(mhx > startgame.x && mhx < startgame.x+startgame.width &&
mhy > startgame.y && mhy < startgame.y+startgame.height)
isHovered = true;
else
isHovered = false;
}
});
}
public void paint(Graphics g)
{
dbi = createImage(getWidth(), getHeight());
dbg = dbi.getGraphics();
draw(dbg);
g.drawImage(dbi,0,0,this);
repaint();
}
public void draw(Graphics g)
{
if(!isStarted)
{
if(!isHovered)
g.setColor(Color.GRAY);
else
g.setColor(Color.GREEN);
g.fillRect(startgame.x, startgame.y, startgame.width, startgame.height);
g.setFont(new Font("Serif",Font.BOLD,24));
g.setColor(Color.WHITE);
g.drawString("Start game", startgame.x+20, startgame.y+25);
g.drawString(String.format("hoverx: %d hovery: %d",mhx,mhy), 50,200);
}
else
{
}
}
public void run()
{
} }
public class Game {
public static void main(String[] args)
{
Contents c = new Contents();
} }
Just use Rectangle.contains(Point) to check if the point from the MouseEvent is inside the Rectangle. Here is an example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PaintedButton extends JPanel {
private static final Color HOVER_COLOR = Color.BLUE;
private static final Color NON_HOVER_COLOR = Color.GREEN;
private static final Rectangle2D RECTANGLE = new Rectangle2D.Double(50, 50,
200, 100);
private Color color = NON_HOVER_COLOR;
public PaintedButton() {
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if (RECTANGLE.contains(p)) {
color = HOVER_COLOR;
} else {
color = NON_HOVER_COLOR;
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(color);
g2.fill(RECTANGLE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PaintedButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
So, I'm trying to dynamically draw a Polygon starting from when I click the mouse until I stop dragging and release. Instead of, for the purpose of this question, a square outline being drawn when I click, drag down, then right-across, then up, then left-across, this is what happens: http://imgur.com/t8ZN3Pp
Any suggestions?
Notes:
model.addPolygon() creates a Polygon with starting points and adds it to an ArrayList called 'polys'
model.addPolygonPoint() adds points to this created polygon that is stored in 'polys'
my paint function iterates through polys to paint
public void mousePressed(MouseEvent e) {
oldX = e.getX();
oldY = e.getY();
model.addPolygon(oldX, oldY);
}
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
model.addPolyPoint(currentX, currentY);
repaint();
}
.
.
. then in paintComponent:
for (ListIterator<Polys> iter =
model.polys.listIterator(model.polys.size());
iter.hasPrevious();){
graphics2D.draw(iter.previous().poly);
Full paintComponent:
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null) {
image = createImage(getSize().width, getSize().height);
graphics2D = (Graphics2D) image.getGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
g.drawImage(image, 0, 0, null);
for (ListIterator<Polys> iter =
model.polys.listIterator(model.polys.size());
iter.hasPrevious();){
graphics2D.draw(iter.previous().poly);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
public class Testing {
private static int lastX;
private static int lastY;
private static int modX;
private static int modY;
private static final BasicStroke STROKE = new BasicStroke(2.0F);
private static final Point[] SHAPE = new Point[]{
new Point(10, 10),
new Point(10, 40),
new Point(60, 90),
new Point(50, 50)
};
public static void main(final String[] args) {
final JFrame frame = new JFrame("Polygon Movement");
final JPanel pane = new JPanel() {
#Override
public void paintComponent(final Graphics g1) {
final Graphics2D g = (Graphics2D) g1;
g.setColor(Color.RED);
g.translate(modX, modY);
g.setStroke(STROKE);
for (int i = 0; i < SHAPE.length; i++) {
g.drawLine(SHAPE[i].x, SHAPE[i].y, SHAPE[(i + 1) % SHAPE.length].x, SHAPE[(i + 1) % SHAPE.length].y);
}
}
};
pane.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
modX += e.getX() - lastX;
modY += e.getY() - lastY;
lastX = e.getX();
lastY = e.getY();
frame.repaint();
}
});
pane.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
}
});
pane.setPreferredSize(new Dimension(200, 200));
frame.add(pane);
frame.pack();
frame.setVisible(true);
}
}
As you can see, I make a basic shape with defined points. It is the most effective way to do this, unless you wish to change the basic shape (here it is static). In that case, you find the point the mouse it 'grabbing' and modify that one. Either way, no adding or removing of points is needed. I use the terms lastX instead of oldX just by preference.
The BasicStroke is quite optional, same with casting to a Graphics2D object.
The line:
g.drawLine(SHAPE[i].x, SHAPE[i].y, SHAPE[(i + 1) % SHAPE.length].x, SHAPE[(i + 1) % SHAPE.length].y);
Should make some sense if you've tried this thing before. It iterates through all the points, drawing a line from the current (SHAPE[i]) to the next (SHAPE[(i + 1) & SHAPE.length).
The reason behind that logic, is that say you have 4 points, as we do here. The last iteration through them, you will be given i = 3. Due to this and the array only containing 4 indexes (0-3), we must get that value back down to zero. For simplicity I use the % SHAPE.length so there wouldn't be a need for special cases.
I also opted to use adapters seeing as there were only 2 methods needed of the 7 possible ones.
If you have any questions feel free to ask me about this.
~Legend
If all you want to do is draw a polygon. You could simply use the Shape API
This will allow you to "add" points to the shape and allow the shape to paint itself.
Here I use a simple Path2D shape, as it allows me to append points over time. I keep a running list of shapes, which allows me to generate multiple polygons as required
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawPolygon {
public static void main(String[] args) {
new DrawPolygon();
}
public DrawPolygon() {
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("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PolyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class PolyPane extends JPanel {
private MouseHandler mouseHandler;
private Path2D currentShape;
private List<Path2D> lstPloys;
private Point lastPoint;
private Point currentPoint;
public PolyPane() {
lstPloys = new ArrayList<>();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void addNotify() {
super.addNotify();
addMouseListener(getMouseHandler());
addMouseMotionListener(getMouseHandler());
}
#Override
public void removeNotify() {
removeMouseListener(getMouseHandler());
removeMouseMotionListener(getMouseHandler());
super.removeNotify();
}
public MouseHandler getMouseHandler() {
if (mouseHandler == null) {
mouseHandler = new MouseHandler();
}
return mouseHandler;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (lastPoint != null) {
g2d.setColor(Color.RED);
g2d.fillOval(lastPoint.x - 2, lastPoint.y - 2, 4, 4);
}
if (currentShape != null) {
g2d.setColor(Color.RED);
g2d.draw(currentShape);
if (lastPoint != null && currentPoint != null) {
System.out.println(lastPoint + " - " + currentPoint);
g2d.setColor(new Color(255, 0, 0, 64));
g2d.draw(new Line2D.Float(lastPoint, currentPoint));
}
}
g2d.setColor(Color.BLACK);
for (Shape shape : lstPloys) {
g2d.draw(shape);
}
g2d.dispose();
}
public class MouseHandler extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.getClickCount() == 1) {
Point p = e.getPoint();
lastPoint = p;
if (currentShape == null) {
currentShape = new Path2D.Float();
currentShape.moveTo(p.x, p.y);
} else {
currentShape.lineTo(p.x, p.y);
}
repaint();
} else if (e.getClickCount() == 2) {
currentShape.closePath();
lstPloys.add(currentShape);
currentShape = null;
lastPoint = null;
repaint();
}
}
}
#Override
public void mouseMoved(MouseEvent e) {
if (currentShape != null) {
currentPoint = e.getPoint();
repaint();
} else {
currentPoint = null;
}
}
}
}
}
Take a look at Working with Geometry for more details