I'm trying to create a paint application in Java.
I have a "canvas" class which extends JComponent.
I have an ArrayList of type Shape which holds all the shapes of the entire drawing.
Inside the paintComponent() method:
Each loop everything gets cleared
Every Shape inside the ArrayList gets painted with either g.draw() or g.fill()
If I want to add a shape or draw a Shape, I add it to the ArrayList.
The problem is that after a considerably high amount of shapes inside the ArrayList, the execution of the paintComponent() method slows.
For example a custom brush.
When dragging the brush over the canvas I have to add a new Shape of type "CustomBrush extends Shape" to the ArrayList
So with just a single stroke I end up with hundreds of shapes in the ArrayList
The question is:
How do I "pack" say 100 Shape objects into one, so that a single brush stroke becomes one single object in my ArrayList ?
The ultimate goal is however to speed up the paintComponent() method so that it paints all the drawn Shapes faster.
Thank You!
Here's a sample code:
public class GraphicPanel extends JComponent{
private ArrayList<Shape> shapeBuffer;
public void paintComponent( Graphics gPlain ){
Graphics2D g = (Graphics2D)gPlain;
for( Shape s : shapeBuffer ){
if( filled.next() ){
g.fill( s );
}
else{
g.draw( s );
}
}
}
Paint the background to a BufferedImage, and then draw the BufferedImage to your GrahpicPanel within the paintComponent(...) method using g.drawImage(...). You get the BufferedImage's Graphics context by calling getGraphics() or createGraphics() (for a Graphics2D object). Don't forget to dispose of the Graphics object obtained in this way (but never dispose of a Graphics object given to you by the JVM).
Also, don't forget to call the super.paintComponent(g) in your override!
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.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class MyPaint extends JComponent {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Stroke STROKE = new BasicStroke(4f);
private static final Color[] COLORS = { Color.RED, Color.GREEN,
Color.yellow, Color.orange, Color.blue, Color.cyan };
private BufferedImage img = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
private Rectangle rect = null;
public MyPaint() {
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, null);
}
if (rect != null) {
g.setColor(Color.LIGHT_GRAY);
((Graphics2D) g).draw(rect);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
private Random random = new Random();
private Point p;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
p = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (p != null) {
Rectangle rect2 = createRect(e.getPoint());
Graphics2D g2 = img.createGraphics();
g2.setStroke(STROKE);
Color c = COLORS[random.nextInt(COLORS.length)];
g2.setColor(c);
g2.fill(rect2);
g2.setColor(c.darker());
g2.draw(rect2);
g2.dispose();
}
p = null;
rect = null;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
rect = createRect(e.getPoint());
repaint();
}
private Rectangle createRect(Point p2) {
int x = Math.min(p.x, p2.x);
int y = Math.min(p.y, p2.y);
int width = Math.abs(p.x - p2.x);
int height = Math.abs(p.y - p2.y);
Rectangle rect2 = new Rectangle(x, y, width, height);
return rect2;
}
}
private static void createAndShowGui() {
MyPaint mainPanel = new MyPaint();
JFrame frame = new JFrame("MyPaint");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Brushstroke and color are attributes of a shape. You could define your own shape class like this. That way, you're maintaining shapes in your List.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Shape;
public class MyShape {
private int brushSize;
private Color interiorColor;
private Shape shape;
public MyShape() {
this.brushSize = 1;
this.interiorColor = Color.blue;
}
public MyShape(int brushSize, Color interiorColor, Shape shape) {
this.brushSize = brushSize;
this.interiorColor = interiorColor;
this.shape = shape;
}
public int getBrushSize() {
return brushSize;
}
public void setBrushSize(int brushSize) {
this.brushSize = brushSize;
}
public Color getInteriorColor() {
return interiorColor;
}
public void setInteriorColor(Color interiorColor) {
this.interiorColor = interiorColor;
}
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
}
If I missed any attributes, feel free to add them to your shape class.
Related
I have a code that has a button. When a button is pressed, circles appear at random positions with random colors. There can only be 10 circles.
Now that I added random colors functionality, the problem is that after each circle is drawn, its color starts changing infinetely.
How can I make it so the colors don't change?
class Panel extends JPanel {
private JButton button;
private Ellipse2D.Double[] circles;
Integer count;
public Panel() {
setup();
}
private void setup() {
count=new Integer(0);
circles=new Ellipse2D.Double[10];
button=new JButton(count.toString());
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Random r=new Random();
//position circles with diameter 100 in a way
//that it would fit in a window's size
int highX=getWidth()-100;
int highY=getHeight()-100;
circles[count]=new
Ellipse2D.Double(r.nextInt(highX),
r.nextInt(highY), 100, 100);
count++;
button.setText(count.toString());
}
});
add(button);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintStuff(g);
repaint();
}
private void paintStuff(Graphics g) {
Graphics2D g2=(Graphics2D) g;
g2.setPaint(Color.RED);
if (count!=0) {
for (int i=0; i<count; i++) {
g2.draw(circles[i]);
Random r=new Random();
int red=r.nextInt(256);
int green=r.nextInt(256);
int blue=r.nextInt(256);
g2.setPaint(new Color(red, green, blue));
g2.fill(circles[i]);
}
}
}
}
public class Frame extends JFrame {
private Panel panel;
public Frame() {
panel=new Panel();
add(panel);
}
public static void main(String[] args) {
Frame frame=new Frame();
}
}
Never call repaint within a painting method as that causes a "poor-man's" animation to occur. Instead call it in your JButton's ActionListener. Also, don't randomize within the painting method, but rather do this within the ActionListener. The painting method is not under your control, and you don't want to use it to change your object's state, but rather only to display it.
Other suggestions:
Your code still needs to set the JFrame's setDefaultCloseOperation
and still needs to set the JFrame visible
You never suggest sizing in the code. Myself, I recommend overriding public Dimension getPreferredSize() of your JPanel and call pack() on the JFrame after adding the JPanel but before displaying it.
I'd rename your classes so that the names don't clash with core Java classes and cause confusion to your instructors, us, or your future self.
Don't keep re-creating a new Random object within the for loop. Rather why not simply give the class a Random field, create it once, but reuse the object repeatedly.
You will want to associate a color with your shape/Ellipse2D. For a one-to-one correspondence, consider using a Map such as a HashMap<Shape, Color>.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class Panel2 extends JPanel {
// preferred size constants
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
// map to hold circles and colors
private Map<Shape, Color> shapeColorMap = new LinkedHashMap<>();
public Panel2() {
add(new JButton(new RandomColorAction()));
}
#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;
// create *smooth* drawings
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paintStuff(g2);
}
private void paintStuff(Graphics2D g2) {
// iterate through our map extracting all circles and colors
// and drawing them
for (Entry<Shape, Color> entry : shapeColorMap.entrySet()) {
Shape shape = entry.getKey();
Color color = entry.getValue();
g2.setColor(color);
g2.fill(shape);
}
}
// listener for our button
private class RandomColorAction extends AbstractAction {
private static final int CIRC_WIDTH = 100;
private Random random = new Random();
private int count = 0;
public RandomColorAction() {
super("Random Circle: 0");
putValue(MNEMONIC_KEY, KeyEvent.VK_R);
}
#Override
public void actionPerformed(ActionEvent e) {
// create our random ellipses
int x = random.nextInt(getWidth() - CIRC_WIDTH);
int y = random.nextInt(getHeight() - CIRC_WIDTH);
Shape shape = new Ellipse2D.Double(x, y, CIRC_WIDTH, CIRC_WIDTH);
// create our random color using HSB for brighter colors
float hue = random.nextFloat();
float saturation = (float) (0.8 + random.nextFloat() * 0.2);
float brightness = (float) (0.8 + random.nextFloat() * 0.2);
Color color = Color.getHSBColor(hue, saturation, brightness);
shapeColorMap.put(shape, color);
// increment count, place items into map, repaint
count++;
putValue(NAME, "Random Circle: " + count);
repaint();
}
}
private static void createAndShowGui() {
Panel2 mainPanel = new Panel2();
JFrame frame = new JFrame("Panel2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
In comments, Camickr astutely points out:
A painting method should paint the current state of the component. By using the HashMap you are introducing the possibility of randomness. The order of iteration through the map can't be guaranteed. Therefore as new entries are added to the map the order each Shape is painted could change. Generally not a problem, but if two random shapes ever overlap, the result good be flip flopping which shape is painted on top of one another.
And of course, he is absolutely correct, since there is no guaranteed order for a HashMap. Fortunately the variable itself was declared to be of Map type, and so to preserve order all one needs to do is to change the actual object type from HashMap to that of LinkedHashMap, a class which per its API:
This implementation spares its clients from the unspecified, generally chaotic ordering provided by HashMap (and Hashtable), without incurring the increased cost associated with TreeMap.
So for TLDR, change this:
private Map<Shape, Color> shapeColorMap = new HashMap<>();
to this:
private Map<Shape, Color> shapeColorMap = new LinkedHashMap<>();
Edited to fix the color calculation.
A just for the fun of it version that introduces Path2D and AffineTransform with a MouseListener/MouseMotionListener to allow for dragging the circles:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
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.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import javax.swing.*;
#SuppressWarnings("serial")
public class Panel2 extends JPanel {
// preferred size constants
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
// map to hold circles and colors
private Map<Shape, Color> shapeColorMap = new LinkedHashMap<>();
public Panel2() {
add(new JButton(new RandomColorAction()));
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);
Graphics2D g2 = (Graphics2D) g;
// create *smooth* drawings
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paintStuff(g2);
}
private void paintStuff(Graphics2D g2) {
// iterate through our map extracting all circles and colors
// and drawing them
for (Entry<Shape, Color> entry : shapeColorMap.entrySet()) {
Shape shape = entry.getKey();
Color color = entry.getValue();
g2.setColor(color);
g2.fill(shape);
}
}
private class MyMouse extends MouseAdapter {
private Entry<Shape, Color> selected = null;
private Path2D path;
private Point p = null;
#Override
public void mousePressed(MouseEvent e) {
Set<Entry<Shape, Color>> entrySet = shapeColorMap.entrySet();
// get Shape pressed
for (Entry<Shape, Color> entry : entrySet) {
if (entry.getKey().contains(e.getPoint())) {
selected = entry;
}
}
if (selected != null) {
path = new Path2D.Double(selected.getKey());
// move it to the top
entrySet.remove(selected);
shapeColorMap.put(path, selected.getValue());
p = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (selected != null) {
moveSelected(e);
}
selected = null;
}
#Override
public void mouseDragged(MouseEvent e) {
if (selected != null) {
moveSelected(e);
}
}
private void moveSelected(MouseEvent e) {
int x = e.getX() - p.x;
int y = e.getY() - p.y;
p = e.getPoint();
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
path.transform(at);
repaint();
}
}
// listener for our button
private class RandomColorAction extends AbstractAction {
private static final int CIRC_WIDTH = 100;
private Random random = new Random();
private int count = 0;
public RandomColorAction() {
super("Random Circle: 0");
putValue(MNEMONIC_KEY, KeyEvent.VK_R);
}
#Override
public void actionPerformed(ActionEvent e) {
// create our random ellipses
int x = random.nextInt(getWidth() - CIRC_WIDTH);
int y = random.nextInt(getHeight() - CIRC_WIDTH);
Shape shape = new Ellipse2D.Double(x, y, CIRC_WIDTH, CIRC_WIDTH);
// create our random color using HSB for brighter colors
float hue = random.nextFloat();
float saturation = (float) (0.8 + random.nextFloat() * 0.2);
float brightness = (float) (0.8 + random.nextFloat() * 0.2);
Color color = Color.getHSBColor(hue, saturation, brightness);
shapeColorMap.put(shape, color);
// increment count, place items into map, repaint
count++;
putValue(NAME, "Random Circle: " + count);
repaint();
}
}
private static void createAndShowGui() {
Panel2 mainPanel = new Panel2();
JFrame frame = new JFrame("Panel2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
paintStuff(Graphics g);
Is called many times, and each time it refreshes the circle color. That's the wrong place to set the color, you need to set it when you add the circle.
Create a java.awt.Color array as a global variable
private Color[] circlesColors;
Then just fill this array in the actionPerformed(...) method. This is the setupmethod with the changes
private void setup() {
count=new Integer(0);
circles=new Ellipse2D.Double[10];
circlesColors = new Color[10]; //Init the colors array to the same size of circles array
button=new JButton(count.toString());
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Random r=new Random();
int highX=getWidth()-100;
int highY=getHeight()-100;
circles[count]=new Ellipse2D.Double(r.nextInt(highX), r.nextInt(highY), 100, 100);
circlesColors[count] = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)); //Assign random color
count++;
button.setText(count.toString());
}
});
add(button);
}
Then in your paint(...) method
private void paintStuff(Graphics g) {
Graphics2D g2=(Graphics2D) g;
g2.setPaint(Color.RED);
if (count!=0) {
for (int i=0; i<count; i++) {
g2.draw(circles[i]);
g2.setPaint(circlesColors[i]); //Get and set the color associated to the circle
g2.fill(circles[i]);
}
}
}
I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){
frame.repaint();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I've had a look around and tried using other queries, but I just can't seem to get this to work.
I am trying to retain an image from a JPanel drawn via the g.draw/fill methods.
I've attempted to save the drawing in a buffered image, but when I display it in a messageDialog all I get is the background and none of the drawings
These two methods are the important code (from the DrawingPanel class):
public void loadDrawing(BufferedImage bi) {
//opens a message dialog and displays the image parameter
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
System.out.println("w:" + bi.getWidth() + " h:" + bi.getHeight());
}
public BufferedImage getScreenShot() {
BufferedImage image = new BufferedImage(this.getWidth(),
this.getHeight(), BufferedImage.TYPE_INT_RGB);
// call the Panels's paint method, using
// the Graphics object of the image.
this.paint(image.getGraphics());
return image;
}
They get called here:
#Override
public void actionPerformed(ActionEvent ae) {
BufferedImage bi = dp.getScreenShot();
dp.loadDrawing(bi);
}
Here is the whole program, it should run.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class TestClass extends JFrame implements ActionListener {
DrawingPanel dp;
public TestClass() {
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel top = new JPanel();
JButton load = new JButton("Load Image");
load.addActionListener(this);
top.add(load);
dp = new DrawingPanel();
dp.setBackground(Color.CYAN);
add(top, BorderLayout.NORTH);
add(dp, BorderLayout.CENTER);
setVisible(true);
}
public static void main(String[] args) {
new TestClass();
}
#Override
public void actionPerformed(ActionEvent ae) {
BufferedImage bi = dp.getScreenShot();
dp.loadDrawing(bi);
}
private class DrawingPanel extends JPanel implements
MouseListener, MouseMotionListener {
private int xPos, yPos;//mouse positions
private DrawingPanel() {
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public void mousePressed(MouseEvent me) {
xPos = me.getX();
yPos = me.getY();
}
#Override
public void mouseDragged(MouseEvent me) {
int x = me.getX(), y = me.getY();
Graphics g = getGraphics();
g.setColor(Color.BLACK);
g.drawOval(xPos, yPos, 30, 30);
xPos = x;
yPos = y;
}
public void loadDrawing(BufferedImage bi) {
//opens a message dialog and displays the image parameter
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
System.out.println("w:" + bi.getWidth() + " h:" + bi.getHeight());
}
public BufferedImage getScreenShot() {
BufferedImage image = new BufferedImage(this.getWidth(),
this.getHeight(), BufferedImage.TYPE_INT_RGB);
// call the Panels's paint method, using
// the Graphics object of the image.
this.paint(image.getGraphics());
return image;
}
//unused abstract method
#Override
public void mouseClicked(MouseEvent me) {
}
#Override
public void mouseReleased(MouseEvent me) {
}
#Override
public void mouseEntered(MouseEvent me) {
}
#Override
public void mouseExited(MouseEvent me) {
}
#Override
public void mouseMoved(MouseEvent me) {
}
}
}
I need to be able to store the graphics from the panel and retrieve it.
Help would be heavily appreciated.
Every attempt to draw something into the Graphics object that was obtained from a Component by calling getGraphics will fail sooner or later. This Graphics object merely serves as a "path" to the actual screen (that is only valid while the component is actually painted on the screen). It is not a "buffer", and it does not "store" anything that was drawn.
If you want to create a simple painting program, you should draw to a BufferedImage. And when the DrawingPanel is to be painted, you only paint this BufferedImage. The additional advantage here is that when you want to make a screenshot, you basically just have to return a copy of this BufferedImage.
I sketeched the basic approach in your DrawingPanel class, with some in-lined comments. It could be cleaned up and beautified, and there are some aspects to consider (e.g. what should happen when the DrawingPanel is resized?), but it shows how it should work in general:
private class DrawingPanel extends JPanel implements
MouseListener, MouseMotionListener {
// The image that will store what was drawn. In the
// mouseDragged method, the painting operations will
// go directly to this image. When this panel is
// painted, then ONLY this image will be painted.
private BufferedImage bufferedImage;
private int xPos, yPos;//mouse positions
private DrawingPanel() {
addMouseListener(this);
addMouseMotionListener(this);
}
// Make sure that the "bufferedImage" is non-null
// and has the same size as this panel
private void validateImage()
{
if (bufferedImage == null)
{
bufferedImage = new BufferedImage(
getWidth(), getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = bufferedImage.getGraphics();
g.setColor(getBackground());
g.fillRect(0,0,getWidth(),getHeight());
g.dispose();
}
if (bufferedImage.getWidth() != getWidth() ||
bufferedImage.getHeight() != getHeight())
{
BufferedImage newBufferedImage = new BufferedImage(
getWidth(), getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = newBufferedImage.getGraphics();
g.setColor(getBackground());
g.fillRect(0,0,getWidth(),getHeight());
g.drawImage(bufferedImage, 0,0,null);
g.dispose();
bufferedImage = newBufferedImage;
}
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
validateImage();
// Paint the bufferedImage which stores
// what was drawn until now
g.drawImage(bufferedImage, 0, 0, null);
}
#Override
public void mousePressed(MouseEvent me) {
xPos = me.getX();
yPos = me.getY();
}
#Override
public void mouseDragged(MouseEvent me) {
int x = me.getX(), y = me.getY();
validateImage();
// Paint directly into the bufferedImage here
Graphics g = bufferedImage.getGraphics();
g.setColor(Color.BLACK);
g.drawOval(xPos, yPos, 30, 30);
repaint();
xPos = x;
yPos = y;
}
public void loadDrawing(BufferedImage bi) {
//opens a message dialog and displays the image parameter
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
System.out.println("w:" + bi.getWidth() + " h:" + bi.getHeight());
}
public BufferedImage getScreenShot() {
// This basically returns a "copy" of the
// bufferedImage that stores what was drawn
BufferedImage image = new BufferedImage(
getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.drawImage(bufferedImage, 0, 0, null);
g.dispose();
return image;
}
//unused abstract method
#Override
public void mouseClicked(MouseEvent me) {
}
#Override
public void mouseReleased(MouseEvent me) {
}
#Override
public void mouseEntered(MouseEvent me) {
}
#Override
public void mouseExited(MouseEvent me) {
}
#Override
public void mouseMoved(MouseEvent me) {
}
}
Your getting your Graphics object by calling getGraphics() on a component, and images drawn with this will not persist. Why not instead draw to a BufferedImage with its Graphics object, and then simply save that BufferedImage. This would simplify things greatly, and your program would work.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class TestClass2 extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
public TestClass2() {
JPanel northPanel = new JPanel();
northPanel.add(new JButton(new GetImageAction("Get Image")));
northPanel.add(new JButton(new ClearImageAction("Clear Image")));
setLayout(new BorderLayout(5, 5));
add(drawingPanel, BorderLayout.CENTER);
add(northPanel, BorderLayout.NORTH);
}
private class GetImageAction extends AbstractAction {
public GetImageAction(String name) {
super(name);
putValue(MNEMONIC_KEY, KeyEvent.VK_G);
}
#Override
public void actionPerformed(ActionEvent evt) {
BufferedImage img = drawingPanel.getMainImage();
ImageIcon icon = new ImageIcon(img);
JOptionPane.showMessageDialog(TestClass2.this, icon);
}
}
private class ClearImageAction extends AbstractAction {
public ClearImageAction(String name) {
super(name);
putValue(MNEMONIC_KEY, KeyEvent.VK_C);
}
#Override
public void actionPerformed(ActionEvent evt) {
drawingPanel.clearImage();
drawingPanel.repaint();
}
}
private static void createAndShowGui() {
TestClass2 mainPanel = new TestClass2();
JFrame frame = new JFrame("TestClass2");
frame.setDefaultCloseOperation(JFrame.EXIT_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();
}
});
}
}
class DrawingPanel extends JPanel {
public static final int BI_WIDTH = 400;
public static final int BI_HEIGHT = BI_WIDTH;
private static final Color BACKGROUND = Color.CYAN;
public static final Color DRAW_COLOR = Color.black;
public static final int OVAL_WIDTH = 30;
private BufferedImage mainImage;
public DrawingPanel() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
clearImage();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (mainImage != null) {
g.drawImage(mainImage, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(BI_WIDTH, BI_HEIGHT);
}
public BufferedImage getMainImage() {
return mainImage;
}
public void clearImage() {
mainImage = new BufferedImage(BI_WIDTH, BI_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics g = mainImage.getGraphics();
g.setColor(BACKGROUND);
g.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
g.dispose();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent mEvt) {
draw(mEvt);
}
#Override
public void mouseDragged(MouseEvent mEvt) {
draw(mEvt);
}
private void draw(MouseEvent mEvt) {
Graphics2D g2 = mainImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(DRAW_COLOR);
g2.drawOval(mEvt.getX() - OVAL_WIDTH / 2, mEvt.getY() - OVAL_WIDTH / 2, OVAL_WIDTH, OVAL_WIDTH);
g2.dispose();
repaint();
}
}
}
I'm trying to build a custom triangle component that has the same features as a JComponent (like a JButton per say).
The porpoise of the program will be to add triangle on a mouse click exactly where the mouse is and to handle a mouseover event by highlighting the bg of the shape.
I let the default layouts(or null), because while using others, the applications just doesn't place the triangles where I want...
Right now my major issue is how to adjust the size of the triangles with direct proportionality relative to the form size? So that if I reduce the frame size 50% all the components are down that value as well.
One other issue is that the JComponent requires a rectangular area to handle events, for what I've seen there's no way countering this, so if I try to click on the affected area it will just ignore it instead of creating a new triangle there.
And yet another problem is that sometimes while moving out of the triangle from the bottom it is still green.
Thanks!
Here is the SSCCE:
// TriangleCustom.java
package TriangleCustom;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TriangleCustom {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame f = new JFrame("Triangle");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(1200, 800);
Panel p = new Panel();
f.add(p);
f.setVisible(true);
}
}
class Panel extends JPanel {
// the offsets are the area (rect border) to contain the triangle shape
private final int xOFFSET = 25;
private final int yOFFSET = 50;
ArrayList<TriangleShape> triangleAL = new ArrayList<TriangleShape>();
public Panel() {
setBounds(0, 0, 800, 400);
// setBorder(BorderFactory.createLineBorder(Color.black,2));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
addTriangle(new Point(e.getX(), e.getY()), new Point(e.getX()
- xOFFSET, e.getY() + yOFFSET), new Point(e.getX()
+ xOFFSET, e.getY() + yOFFSET));
}
});
}
private void addTriangle(Point topCorner, Point leftCorner,
Point rightCorner) {
final TriangleDTO tdto = new TriangleDTO(new Point(25, 0), new Point(0,
50), new Point(50, 50));
TriangleShape ts = new TriangleShape(tdto);
ts.setBorderColor(Color.BLACK);
ts.setFillColor(Color.RED);
ts.setBounds((int) (topCorner.getX() - 25), (int) topCorner.getY(), 51,
51);
triangleAL.add(ts);
this.add(ts);
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.draw(new Rectangle2D.Double(0, 0, 799, 399));
}
}
// the custom component in a shape of a triangle
class TriangleShape extends JComponent {
private GeneralPath triangle = new GeneralPath();
private TriangleDTO tdto = new TriangleDTO();
private Color borderColor = new Color(0);
private Color fillColor = new Color(0);
// Constructor
public TriangleShape(TriangleDTO tdto) {
this.tdto = tdto;
triangle.moveTo(tdto.getTopCorner().getX(), tdto.getTopCorner().getY());
triangle.lineTo(tdto.getLeftCorner().getX(), tdto.getLeftCorner()
.getY());
triangle.lineTo(tdto.getRightCorner().getX(), tdto.getRightCorner()
.getY());
triangle.closePath();
addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
// there are some issues when going out of the triangle from
// bottom
if (triangle.contains((Point2D) e.getPoint())) {
setFillColor(Color.GREEN);
repaint();
} else {
setFillColor(Color.RED);
repaint();
}
}
});
}
public void setBorderColor(Color borderColor) {
this.borderColor = borderColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(fillColor);
g2d.fill(triangle);
g2d.setPaint(borderColor);
g2d.draw(triangle);
}
}
// just a plain DTO for the triangle points
class TriangleDTO {
private Point topCorner = new Point();
private Point leftCorner = new Point();
private Point rightCorner = new Point();
// Constructors
public TriangleDTO() {
}
public TriangleDTO(Point topCorner, Point leftCorner, Point rightCorner) {
super();
this.topCorner = topCorner;
this.leftCorner = leftCorner;
this.rightCorner = rightCorner;
}
// Getters and Setters
public Point getTopCorner() {
return topCorner;
}
public void setTopCorner(Point topCorner) {
this.topCorner = topCorner;
}
public Point getLeftCorner() {
return leftCorner;
}
public void setLeftCorner(Point leftCorner) {
this.leftCorner = leftCorner;
}
public Point getRightCorner() {
return rightCorner;
}
public void setRightCorner(Point rightCorner) {
this.rightCorner = rightCorner;
}
}
i have a program similar to paint. and that i am trying to implement a change pen color however when i change the color, everything currently drawn is changed to the color RED for example in my program,how can i make it such that it will not repaint everything currently drawn to the currently changed color?Below code will compile and run
Class for the JPanel drawing area
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
//refer to http://jkost.ergoway.gr/jnkjavaconnection/freedraw.html for the algorithm.
public class STDrawingArea extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
ArrayList<Rectangle> dPoint = new ArrayList<Rectangle>();
Point point = new Point(-1,-1);
private Color currentColor;
public STDrawingArea()
{
setBorder(BorderFactory.createLineBorder(Color.black));
setBackground(Color.WHITE);
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e)
{
dPoint.add(new Rectangle(point.x,point.y,e.getX(),e.getY()));
point.x = e.getX();
point.y = e.getY();
repaint();
}
});
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e)
{
System.out.println("mousePressed X: "+e.getX()+"mousePressed Y: "+e.getY());
dPoint.add(new Rectangle(e.getX(),e.getY(),-1,-1));
point.x = e.getX();
point.y = e.getY();
}
});
addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent e)
{
System.out.println("mouseReleased X: "+e.getX()+"mouseReleased Y: "+e.getY());
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(700,500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getCurrentColor());
for (int i=0; i < dPoint.size(); i++) {
Rectangle r = dPoint.get(i);
if (r.width != -1)
{
g.drawLine(r.x, r.y, r.width, r.height);
}
}
/* Draw current point.*/
g.drawLine(point.x, point.y, point.x, point.y);
}
//set current drawing color
public void changePenColor(Color color)
{
if (color == null)
setCurrentColor(Color.BLACK);
else
setCurrentColor(color);
}
//clear drawings method
public void clearDrawings()
{
if(!(dPoint==null))
{
dPoint.clear();
repaint();
}
}
private void setCurrentColor(Color currentColor) {
this.currentColor = currentColor;
}
private Color getCurrentColor() {
return currentColor;
}
}
Test main class.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class STTestMain extends JFrame {
STDrawingArea drawingArea = new STDrawingArea();
public STTestMain()
{
//JFrame settings
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Spelling Trainer");
setResizable(false);
setVisible(true);
//Panel of buttons
JPanel buttonContainer = new JPanel();
JButton btnPenColor = new JButton("Red Pen");
buttonContainer.add(btnPenColor);
//Drawing Area instantiation
//Adding things to JFrame
getContentPane().add(drawingArea);
getContentPane().add(buttonContainer,BorderLayout.PAGE_END);
pack();
//button listener
btnPenColor.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
drawingArea.changePenColor(Color.RED);
}
});
}
public static void main(String args[])
{
STTestMain test = new STTestMain();
}
}
One way:
Use your ArrayList to draw the current curve as it is being drawn, but
Use a BufferedImage to draw your completed curves
You would do this on mouseReleased and would draw the current curve to the BufferedImage using the current color.
You'll also need to re-initialize your ArrayList of points after drawing to the BufferedImage.
Don't forget to dispose of the BufferedImage's Graphics object after you're done using it.
Draw the BufferedImage in your paintComponent method after super.paintComponent but before drawing your current curve.
This way when you change the color of your drawing, only the current curve is effected.
EDIT
You've mentioned in a comment that you're not familiar with BufferedImage, and are looking for another way. I suppose you could create a class that holds an ArrayList of Points together with a Color, and then on each mouseReleased create an object of this class and add it to an ArrayList in your drawing panel. Then your paintComponent method could iterate through that ArrayList, drawing the list of Points with their associated color, but my gut tells me that you're an intelligent guy and that you'd pick up on how to use a BufferedImage in no time. I really think it's the best solution. And if you try it and it flops, show us your code, and we'll likely be able to help you.
EDIT 2
The BufferedImage constructor will need the image width, height and an image type -- something I'm not 100% familiar with. I usually use BufferedImage.TYPE_INT_RGB for general purpose drawing, and BufferedImage.TYPE_INT_ARGB for general purpose that needs an alpha too. Then you'll extract a Graphics object out of the BufferedImage, say getGraphics() if all you need is a Graphics object and not a Graphics2D object. Then when you initialize the BufferedImage in your constructor, fill it with a Color.white, just as you for your JPanel. Then dispose the Graphics object. Then each time you want to draw, you getGraphics, draw with it, just like you do in the paintComponent method, dispose of the Graphics when done, and finally draw the BufferedImage in the paintComponent via the drawImage method.
EDIT 3
Example program that doesn't do quite what you are trying to do but does illustrate use of a BufferedImage with drawing. This program changes the color each time a new path or curve is drawn.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
public class STTestSimple {
private static void createAndShowUI() {
STDrawPanel drawPanel = new STDrawPanel();
STMouseAdapter mAdapter = new STMouseAdapter(drawPanel);
drawPanel.addMouseListener(mAdapter);
drawPanel.addMouseMotionListener(mAdapter);
JFrame frame = new JFrame("Drawing");
frame.getContentPane().add(drawPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class STDrawPanel extends JPanel {
private static final int ST_WIDTH = 700;
private static final int ST_HEIGHT = 500;
private static final Color BACKGROUND_COLOR = Color.white;
private static final float STROKE_WIDTH = 6f;
private static final Stroke STROKE = new BasicStroke(STROKE_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
private static final Color[] colors = {Color.black, Color.blue, Color.red,
Color.green, Color.orange, Color.MAGENTA};
private BufferedImage bImage = new BufferedImage(ST_WIDTH, ST_HEIGHT,
BufferedImage.TYPE_INT_RGB);
private Color color = Color.black;
private ArrayList<Point> points = new ArrayList<Point>();
private int colorIndex = 0;
public STDrawPanel() {
Graphics g = bImage.getGraphics();
g.setColor(BACKGROUND_COLOR);
g.fillRect(0, 0, ST_WIDTH, ST_HEIGHT);
g.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bImage, 0, 0, null);
Graphics2D g2 = (Graphics2D) g;
drawCurve(g2);
}
private void addCurveToBufferedImage() {
Graphics2D g2 = bImage.createGraphics();
drawCurve(g2);
g2.dispose();
}
private void drawCurve(Graphics2D g2) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(STROKE);
g2.setColor(color);
if (points != null && points.size() > 1) {
for (int i = 0; i < points.size() - 1; i++) {
int x1 = points.get(i).x;
int y1 = points.get(i).y;
int x2 = points.get(i + 1).x;
int y2 = points.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(ST_WIDTH, ST_HEIGHT);
}
public void curveStart(Point point) {
points.clear();
points.add(point);
}
public void curveEnd(Point point) {
points.add(point);
addCurveToBufferedImage();
points.clear();
repaint();
colorIndex++;
colorIndex %= colors.length;
setColor(colors[colorIndex]);
}
public void curveAdd(Point point) {
points.add(point);
repaint();
}
public void setColor(Color color) {
this.color = color;
}
}
class STMouseAdapter extends MouseAdapter {
private STDrawPanel drawPanel;
public STMouseAdapter(STDrawPanel drawPanel) {
this.drawPanel = drawPanel;
}
#Override
public void mousePressed(MouseEvent e) {
drawPanel.curveStart(e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
drawPanel.curveEnd(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e) {
drawPanel.curveAdd(e.getPoint());
}
}
Custom Painting Approaches gives two ideas on how you might do this.
Thanks hovercraft, i've done it looking at your code and fiddling around lol.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
public class STDrawingArea extends JPanel {
/**
*
*/
private static final int DA_WIDTH = 700;
private static final int DA_HEIGHT = 500;
private static final Color DA_BGCOLOR = Color.WHITE;
private static final long serialVersionUID = 1L;
ArrayList<Point> points = new ArrayList<Point>();
private Color currentColor;
BufferedImage bImage = new BufferedImage(DA_WIDTH, DA_HEIGHT, BufferedImage.TYPE_INT_RGB);
public STDrawingArea()
{
setBorder(BorderFactory.createLineBorder(Color.black));
//Basic Settings for bImage
Graphics g2d = bImage.getGraphics();
g2d.setColor(DA_BGCOLOR);
g2d.fillRect(0, 0, DA_WIDTH, DA_HEIGHT);
g2d.dispose();
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e)
{
points.clear();
points.add(e.getPoint());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e)
{
points.add(e.getPoint());
repaint();
}
});
addMouseListener(new MouseAdapter(){
public void mouseReleased(MouseEvent e)
{
points.add(e.getPoint());
points.clear();
System.out.println("mouseReleased X: "+e.getX()+"mouseReleased Y: "+e.getY());
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(DA_WIDTH,DA_HEIGHT);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawIntoBufferedImage();
g.drawImage(bImage,0,0,null);
freehandLines(g);
}
public void drawIntoBufferedImage()
{
Graphics g = bImage.getGraphics();
freehandLines(g);
g.dispose();
}
public void freehandLines(Graphics g)
{
if(points != null && points.size() > 1)
{
g.setColor(getCurrentColor());
for(int i = 0; i < points.size()-1;i++)
{
int x1 = points.get(i).x;
int y1 = points.get(i).y;
int x2 = points.get(i+1).x;
int y2 = points.get(i+1).y;
g.drawLine(x1, y1, x2, y2);
}
}
}
//clear drawings method
public void clearDrawings()
{
if(points!=null)
{
points.clear();
Graphics g = bImage.getGraphics();
g.setColor(DA_BGCOLOR);
g.fillRect(0, 0, DA_WIDTH, DA_WIDTH);
g.dispose();
repaint();
}
}
public void setCurrentColor(Color currentColor) {
if(currentColor == null)
{
currentColor = Color.BLACK;
}else{
this.currentColor = currentColor;
}
}
public Color getCurrentColor() {
if (currentColor == null)
return Color.BLACK;
else
return currentColor;
}
}
main class
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class STTestMain extends JFrame {
STDrawingArea drawingArea = new STDrawingArea();
public STTestMain()
{
//JFrame settings
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Spelling Trainer");
setResizable(false);
setVisible(true);
//Panel of buttons
JPanel buttonContainer = new JPanel();
JButton btnRedPen = new JButton("Red Pen");
JButton btnGreenPen = new JButton("Green Pen");
JButton btnClear = new JButton("Clear");
buttonContainer.add(btnRedPen);
buttonContainer.add(btnGreenPen);
buttonContainer.add(btnClear);
//Drawing Area instantiation
//Adding things to JFrame
getContentPane().add(drawingArea);
getContentPane().add(buttonContainer,BorderLayout.PAGE_END);
pack();
//button listener
btnRedPen.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
drawingArea.setCurrentColor(Color.RED);
}
});
btnGreenPen.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
drawingArea.setCurrentColor(Color.GREEN);
}
});
btnClear.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
drawingArea.clearDrawings();
}
});
}
public static void main(String args[])
{
STTestMain test = new STTestMain();
}
}