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();
}
});
}
}
Related
I've made a tool which could allow user to draw on screenshot.Here is my code:
package GUI.Views;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
public class Canvas extends JPanel {
public static int radius = 5;
public class DrawPointListener extends MouseInputAdapter{
private void draw(Point p, int radius){
Graphics2D g2 = (Graphics2D)getGraphics();
int x = (int)p.getX() - radius/2;
int y = (int)p.getY() - radius/2;
g2.fillOval(x, y, radius, radius);
}
#Override
public void mousePressed(MouseEvent e) {
draw(e.getPoint(), radius);
}
#Override
public void mouseDragged(MouseEvent e) {
draw(e.getPoint(), radius);
}
}
private BufferedImage screenShot;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(this.screenShot, 0, 0, null);
}
public Canvas() {
this.setVisible(true);
this.screenShot = getScreenShot();
this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
DrawPointListener drawListener = new DrawPointListener();
this.addMouseListener(drawListener);
this.addMouseMotionListener(drawListener);
}
private BufferedImage getScreenShot() {
try {
return new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
} catch (AWTException e) {
System.out.println("Error");
return null;
}
}
public static void main(String[] args) {
JFrame f = new JFrame();
Canvas canvas = new Canvas();
f.setUndecorated(true);
f.add(canvas);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
}
}
Code worked fine when you try to "draw" on the screen slowly, but when you move your mouse quickly, you would see those points are not consecutive, like this:
I think that because there is a time interval of Listener.How could I improve it?
You can't change the time interval of the listener. You are NOT guaranteed to generate an event for every pixel.
So instead of drawing a single point, you need to draw a line between the current and previous point.
Something like:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DrawingPanel extends JPanel
{
private ArrayList<ArrayList<Point>> previous = new ArrayList<ArrayList<Point>>();
private ArrayList<Point> current = new ArrayList<Point>();
private BasicStroke basicStroke;
public DrawingPanel(int strokeSize)
{
basicStroke = new BasicStroke(strokeSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
MouseAdapter ma = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
}
#Override
public void mouseDragged(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
if (current.size() > 1)
{
previous.add( current );
}
current = new ArrayList<Point>();
}
};
addMouseMotionListener( ma );
addMouseListener( ma );
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke( basicStroke );
// Paint lines from previous drags
for (int i = 0; i < previous.size(); i++)
{
drawLines(g, previous.get(i));
}
// Paint line from current drag
drawLines(g, current);
}
private void drawLines(Graphics g, ArrayList<Point> points)
{
for (int i = 0; i < points.size() - 1; i++)
{
int x = (int) points.get(i).getX();
int y = (int) points.get(i).getY();
int x2 = (int) points.get(i + 1).getX();
int y2 = (int) points.get(i + 1).getY();
g.drawLine(x, y, x2, y2);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(15));
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
This illustrates my problem
Im trying to copy a paint like program but whenever I click on the screen the menu bar is displayed in the panel area. It doesn't do it unless I click on the screen (to draw). The menu bar is added before the panel, any help would be much appreciated.
public class Main {
public static Frame frame;
public static Panel panel;
public static MenuBar menubar;
public static void main(String[] args) {
frame = new Frame();
panel = new Panel();
menubar = new MenuBar();
frame.setJMenuBar(menubar);
frame.addMouseMotionListener(panel);
frame.add(panel);
frame.setVisible(true);
}
}
public class Frame extends JFrame {
public Frame() {
setTitle("Paint");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(JFrame.MAXIMIZED_BOTH);
}
}
public class Panel extends JPanel implements MouseMotionListener {
public boolean isMouseDown = false;
public int x1,y1, x2, y2;
public Color colour = Color.BLACK;
public int size = 3;
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(colour);
g2.setStroke(new BasicStroke(size));
g2.drawLine(x1 - 10, y1 - 80, x2 - 10, y2 - 80);
}
public void mouseDragged(MouseEvent arg0) {
x1 = x2;
y1 = y2;
x2 = arg0.getX();
y2 = arg0.getY();
repaint();
}
public void mouseMoved(MouseEvent arg0) {
x1 = x2;
y1 = y2;
x2 = arg0.getX();
y2 = arg0.getY();
}
public void mouseClicked(MouseEvent arg0) {}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
}
public class MenuBar extends JMenuBar {
JMenu file = new JMenu("File");
JMenu brush = new JMenu("Pen");
JMenu colour = new JMenu("Colour");
Font font = new Font("Times New Romans", Font.PLAIN, 18);
public MenuBar() {
//JMenuBar Code here, left black as very long
}
}
One problems: You need to call super.paintComponent(g); within your paintComponent method. Else the JPanel cannot do its house-keeping painting of its own self.
If you want the drawings to persist, then either create an ArrayList of lines that are then drawn within paintComponent in a for loop, or better, paint onto a BufferedImage which is then displayed within the paintComponent method.
e.g.,
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class MyMain {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
MyPainting mainPanel = new MyPainting();
MenuCreator menuCreator = new MenuCreator(mainPanel);
JFrame frame = new JFrame("MyPainting");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.setJMenuBar(menuCreator.getMenubar());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
#SuppressWarnings("serial")
class MyPainting extends JPanel {
private static final int IMG_W = 600;
private static final int IMG_H = 450;
private static final int STROKE_W = 3;
private static final Stroke STROKE = new BasicStroke(STROKE_W);
private BufferedImage img = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_ARGB);
private Color drawColor = Color.BLACK;
public MyPainting() {
setPreferredSize(new Dimension(IMG_W, IMG_H));
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
private class MyMouse extends MouseAdapter {
private Graphics2D g2 = null;
private Point p0;
#Override
public void mousePressed(MouseEvent e) {
if (img == null) {
return;
}
g2 = img.createGraphics();
g2.setStroke(STROKE);
g2.setColor(drawColor);
p0 = e.getPoint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (p0 == null) {
return;
}
drawLine(e);
}
#Override
public void mouseReleased(MouseEvent e) {
if (p0 == null) {
return;
}
drawLine(e);
g2.dispose();
p0 = null;
}
private void drawLine(MouseEvent e) {
Point p1 = e.getPoint();
g2.drawLine(p0.x, p0.y, p1.x, p1.y);
repaint();
p0 = p1;
}
}
public void setDrawColor(Color drawColor) {
this.drawColor = drawColor;
}
public void clear() {
img = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_ARGB);
repaint();
}
}
class MenuCreator {
private JMenuBar menubar = new JMenuBar();
private MyPainting myPainting;
public MenuCreator(MyPainting myPainting) {
JMenuItem clearDrawing = new JMenuItem(new AbstractAction("Clear Drawing") {
#Override
public void actionPerformed(ActionEvent arg0) {
if (myPainting != null) {
myPainting.clear();
}
}
});
JMenu fileMenu = new JMenu("File");
fileMenu.add(clearDrawing);
JMenu colourMenu = new JMenu("Colour");
for (MyColor myColor : MyColor.values()) {
colourMenu.add(new JMenuItem(new ColorAction(myColor)));
}
menubar.add(fileMenu);
menubar.add(new JMenu("Pen"));
menubar.add(colourMenu);
this.myPainting = myPainting;
}
public JMenuBar getMenubar() {
return menubar;
}
private class ColorAction extends AbstractAction {
private MyColor myColor;
public ColorAction(MyColor myColor) {
super(myColor.getText());
this.myColor = myColor;
}
#Override
public void actionPerformed(ActionEvent e) {
if (myPainting != null) {
myPainting.setDrawColor(myColor.getColor());
}
}
}
}
enum MyColor {
BLACK("Black", Color.BLACK), RED("Red", Color.RED), ORANGE("Orange", Color.ORANGE), BLUE("Blue", Color.BLUE);
private String text;
private Color color;
private MyColor(String text, Color color) {
this.text = text;
this.color = color;
}
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
You failed to honour the requirements of the paint chain...
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(colour);
g2.setStroke(new BasicStroke(size));
g2.drawLine(x1 - 10, y1 - 80, x2 - 10, y2 - 80);
}
Apart from not needing to be public, one of the jobs of paintComponent is to prepare the Graphics context for painting. So, one of the first things you should do is call super.paintComponent before you do any custom painting.
protected void paintComponent(Graphics g) {
super. paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(colour);
g2.setStroke(new BasicStroke(size));
g2.drawLine(x1 - 10, y1 - 80, x2 - 10, y2 - 80);
}
Graphics is a shared context, it is shared amongst the other components which need to be painted, so it is very important that you ensure that it's properly prepared before you paint to it.
It's also important that any significant modifications you make to the context (like transformations or rendering hints) are reversed before the method exists
I would recommend taking a look at Performing Custom Painting and Painting in Swing to gain a better understanding into how the painting system actually works
This code is for drawing on a JPanel. In the paintComponent(Graphics) I am trying to draw curves via repeated Graphics2D#fillOval(x, y, with, height).
The app is working OK, and when I drag the mouse cursor slowly; it draws a continuous curve as I need. But when I speed up dragging the mouse cursor, the result is separated dots and not a continuous curve.
So how to make it draw a continuous curve even if I speed up dragging?
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class Painter extends JPanel {
int x, y;
ArrayList<Point> points;
public Painter() {
setBackground(Color.white);
points = new ArrayList<>();
MouseHandler listener = new MouseHandler();
this.addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
Point point = new Point(e.getX(), e.getY());
points.add(point);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Point point : points) {
g2d.fillOval(point.x, point.y, 15, 15);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setContentPane(new Painter());
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
As mentioned in comment to your previous similar question:
Don't draw discrete ovals in your paintComponent method.
Instead connect the points hold in the List in the paintComponent by drawing lines between adjacent points.
If you need to make the line thicker, change the Stroke property of the Graphics2D object, using one that has a wider thickness.
Be careful with Strokes however since often you don't want the property change to propagate down the paint chain. This means that sometimes you will want to copy the Graphics object and set the Stroke on the new Graphics object and paint with that, then dispose of it.
The simplest way to create a Stroke is to use the BasicStroke class, e.g., new BasicStroke(6f) will get you a nice thick curve.
For example:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class Painter2 extends JPanel {
private static final float STROKE_WIDTH = 15f;
private static final Stroke STROKE = new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
int x, y;
ArrayList<Point> points;
public Painter2() {
setBackground(Color.white);
points = new ArrayList<>();
MouseHandler listener = new MouseHandler();
this.addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mouseDragged(MouseEvent e) {
Point point = new Point(e.getX(), e.getY());
points.add(point);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(STROKE);
for (int i = 1; i < points.size(); i++) {
int x1 = points.get(i - 1).x;
int y1 = points.get(i - 1).y;
int x2 = points.get(i).x;
int y2 = points.get(i).y;
g2d.drawLine(x1, y1, x2, y2);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setContentPane(new Painter2());
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
Or better still:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Painter2 extends JPanel {
private static final float STROKE_WIDTH = 15f;
private static final Stroke STROKE = new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
private static final Color CURVES_COLOR = Color.BLUE;
private static final Color TEMP_CURVE_COLOR = Color.PINK;
private List<List<Point>> curvesList = new ArrayList<>();
private List<Point> tempCurve = null;
public Painter2() {
setBackground(Color.white);
MouseHandler listener = new MouseHandler();
addMouseListener(listener);
addMouseMotionListener(listener);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mousePressed(MouseEvent e) {
tempCurve = new ArrayList<>();
tempCurve.add(e.getPoint());
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
tempCurve.add(e.getPoint());
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
tempCurve.add(e.getPoint());
curvesList.add(tempCurve);
tempCurve = null;
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setStroke(STROKE);
g2.setColor(CURVES_COLOR);
for (List<Point> curve : curvesList) {
drawCurve(g2, curve);
}
if (tempCurve != null) {
g2.setColor(TEMP_CURVE_COLOR);
drawCurve(g2, tempCurve);
}
g2.dispose();
}
private void drawCurve(Graphics2D g2, List<Point> ptList) {
for (int i = 1; i < ptList.size(); i++) {
int x1 = ptList.get(i - 1).x;
int y1 = ptList.get(i - 1).y;
int x2 = ptList.get(i).x;
int y2 = ptList.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setContentPane(new Painter2());
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
Does anyone know how to draw solid lines when the mouse is rapidly moved?
When I move the mouse slowly the line is drawn solid, but when the mouse is moved quickly the line is drawn like a dotted line, as shown here.
The code to draw the lines is currently this:
private final class MouseL extends MouseAdapter implements MouseMotionListener
{
#Override
public void mouseClicked(MouseEvent e)
{
Point p = e.getPoint();
int half = brushDiameter / 1200;
Graphics2D g = getImage().createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setPaint(getColor());
g.fillOval(p.x - half, p.y - half, brushDiameter, brushDiameter);
g.dispose();
repaint(p.x - half, p.y - half, brushDiameter, brushDiameter);
}
#Override
public void mouseDragged(MouseEvent e)
{
mouseClicked(e);
}
But i would like to change it so that the line appears solid. Any help to achieve this is greatly appreciated thanks.
Simple: If you want to draw curves, don't draw individual points or ovals. Instead connect adjacent points with a line using g.drawLine(...). If you want a thicker curve, change the Graphics2D's Stroke width.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawCurve extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color CURRENT_PTS_COLOR = Color.BLUE.brighter().brighter();
public static final Color IMG_COLOR = Color.RED;
public static final Stroke IMG_STROKE = new BasicStroke(4f);
private static final Color FILL_COLOR = Color.WHITE;
private BufferedImage img = null;
private List<Point> currentPts = new ArrayList<>();
public DrawCurve() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setBackground(FILL_COLOR);
g2.clearRect(0, 0, PREF_W, PREF_H);
g2.dispose();
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
Graphics2D g2 = (Graphics2D) g;
g2.setColor(CURRENT_PTS_COLOR);
for (int i = 1; i < currentPts.size(); i++) {
int x1 = currentPts.get(i - 1).x;
int y1 = currentPts.get(i - 1).y;
int x2 = currentPts.get(i).x;
int y2 = currentPts.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
currentPts = new ArrayList<>();
currentPts.add(e.getPoint());
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
currentPts.add(e.getPoint());
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
currentPts.add(e.getPoint());
Graphics2D g2 = img.createGraphics();
g2.setColor(IMG_COLOR);
g2.setStroke(IMG_STROKE);
for (int i = 1; i < currentPts.size(); i++) {
int x1 = currentPts.get(i - 1).x;
int y1 = currentPts.get(i - 1).y;
int x2 = currentPts.get(i).x;
int y2 = currentPts.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.dispose();
currentPts.clear();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawCurve");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawCurve());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I am trying to count mouseClicks, but I don't understand why my counter gets incremented by 2 every click. Tried getClickCount(), but it also is not what I need.
My goal after counting: I would use the counter to paint different things at different clickcount. Lets say 1st and 2nd would always get coordinates to drawLine() and 3rd click would drawRect().
package graphics_training_painting;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
public class U4 extends Canvas implements MouseListener{
private int x1;
private int y1;
private int x2;
private int y2;
private int counter = 0;
public U4() {
setBackground(Color.white);
}
public static void main(String[] args) {
U4 u = new U4();
JFrame f = new JFrame();
f.add(u);
f.setSize(800, 600);
f.setVisible(true);
}
#Override
public void mouseClicked(MouseEvent e) {
counter++;
System.out.println(counter);
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
/* x1 = e.getX();
y1 = e.getY();*/
}
#Override
public void mouseReleased(MouseEvent e) {
/* x2 = e.getX();
y2 = e.getY();
repaint();*/
}
public void paint(Graphics g) {
addMouseListener(this);
g.setColor(Color.blue);
g.drawLine(x1, y1, x2, y2);
}
}
Thank you a lot for help or suggestions,
Timmy!
Don't add your MouseListener in your paint Method! That will add many listeners each incrementing the counter when activated.
You need to know that you do not have control over when or if paint will be called, and it is likely to be called many times during a typical program run. For this reason and others, you should not put program logic, state change code, or component creation inside of this method. Add your MouseListener in some initialization code, such as a constructor, that is called once.
As an aside, you will not want to mix AWT and Swing components as you're doing. Instead you should have your U4 class extend JPanel, and do drawing in its paintComponent method.
So, change this:
public U4() {
setBackground(Color.white);
}
// ...
public void paint(Graphics g) {
addMouseListener(this);
g.setColor(Color.blue);
g.drawLine(x1, y1, x2, y2);
}
to this:
public U4() {
setBackground(Color.white);
addMouseListener(this);
}
// ...
public void paint(Graphics g) {
// addMouseListener(this);
super.paint(g);
g.setColor(Color.blue);
g.drawLine(x1, y1, x2, y2);
}
and then next, make the changes that I recommended
something like:
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.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class U4b extends JPanel {
private static final Color BG = Color.white;
private static final Color DRAW_COLOR = Color.red;
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static final Stroke BASIC_STROKE = new BasicStroke(3f);
private int counter = 0;
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
public U4b() {
setBackground(BG);
MyMouseListener myMouseListener = new MyMouseListener();
addMouseListener(myMouseListener);
addMouseMotionListener(myMouseListener);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(DRAW_COLOR);
g2.setStroke(BASIC_STROKE);
g2.drawLine(x1, y1, x2, y2);
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
counter++;
System.out.println("Counter: " + counter);
x1 = e.getX();
y1 = e.getY();
x2 = x1;
y2 = y1;
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getX();
y2 = e.getY();
repaint();
}
}
private static void createAndShowGui() {
U4b mainPanel = new U4b();
JFrame frame = new JFrame("U4b");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}