1

I am implementing a paint app in java. I am having trouble with choosing the colors of the shapes that are drawn. I am able to choose the color and draw with it but if i draw a change the color to blue, draw a blue rectangle, change the color to red, draw rectangle in red, it changes the rectangle in blue to red as well, happens with every shape. I think its because Im saving the shapes in an arrayList which changes the color of all the shapes, but, I have different arrayLists for different shapes.

I am trying to save the color in static color1 and using setPaint(color1) to change the color to the chosen one. Any help would be appreciated.

Below I have part of my code:

  public class PaintAppFrame extends JFrame implements MouseListener,    MouseMotionListener, ActionListener {
static final long serialVersionUID = 2L;


static int flag = 0;
// -------------------------------------------------------------------------

private JButton changeColor;
private JButton rectButton;
private JButton rectfillButton;
private JButton lineButton;
static Color color1;


static Graphics2D gr;

public static ArrayList<Shape> rectStruct = new ArrayList<Shape>();
public static ArrayList<Shape> rectFillStruct = new ArrayList<Shape>();
public static ArrayList<Shape> lineStruct = new ArrayList<Shape>();

private static Point mouseStart;
private static Point mouseEnd;

    changeColor = new JButton("CHANGE COLOR");
    changeColor.setActionCommand("color");
    changeColor.addActionListener(this);


    // added the menu goes here
    image = Toolkit.getDefaultToolkit().getImage(".");
    paintPanel = new PaintPanel();
    paintPanel.addMouseMotionListener(this);
    paintPanel.addMouseListener(this);

    // make this a toolbar and images

    Icon lineIcon = new ImageIcon("icons/line.png");
    Icon rgbIcon = new ImageIcon("icons/color.png");
    Icon rectIcon = new ImageIcon("icons/rect.png");
    Icon rectfillIcon = new ImageIcon("icons/fullRect.png");

    changeColor = new JButton(rgbIcon);
    changeColor.setActionCommand("color");
    changeColor.addActionListener(this);

    rectButton = new JButton(rectIcon);
    rectButton.setActionCommand("rectangle");
    rectButton.addActionListener(this);

    rectfillButton = new JButton(rectfillIcon);
    rectfillButton.setActionCommand("rectanglefill");
    rectfillButton.addActionListener(this);

    lineButton = new JButton(lineIcon);
    lineButton.setActionCommand("line");
    lineButton.addActionListener(this);

    JPanel buttons = new JPanel(new GridLayout());
    buttons.setBorder(BorderFactory.createRaisedSoftBevelBorder());
    buttons.setLayout(new GridLayout(16, 2));

    buttons.add(changeColor);
    buttons.add(rectButton);
    buttons.add(rectfillButton);
    buttons.add(lineButton);


    paintCanvas = new JPanel(new BorderLayout());
    paintCanvas.add(paintPanel, "Center");
    paintCanvas.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    paintCanvas.setBackground(new Color(100, 100, 142));
    paintCanvas.setPreferredSize(new Dimension(650, 520));
    paintCanvas.setMaximumSize(new Dimension(2000, 1600));

}

public void mouseDragged(MouseEvent me) {


    if (flag == 1) {
        mouseEnd = new Point(me.getX(), me.getY());
        repaint();
        System.out.println("rect dragged");
    }


    else if (flag == 3) {
        mouseEnd = new Point(me.getX(), me.getY());
        repaint();
    }

    } else if (flag == 7) {
        mouseEnd = new Point(me.getX(), me.getY());
        repaint();
    }



}

public void mouseMoved(MouseEvent me) {
}

public void mouseClicked(MouseEvent me) {

}

public void mouseEntered(MouseEvent me) {
}

public void mouseExited(MouseEvent me) {
}

public void mousePressed(MouseEvent me) {


    if (flag == 1) {
        mouseStart = new Point(me.getX(), me.getY());
        mouseEnd = mouseStart;
        repaint();
    }



    else if (flag == 3) {
        mouseStart = new Point(me.getX(), me.getY());
        mouseEnd = mouseStart;
        repaint();
    }


    else if (flag == 7) {
        mouseStart = new Point(me.getX(), me.getY());
        mouseEnd = mouseStart;
        repaint();
    } 
}

public void mouseReleased(MouseEvent me) {

    } else if (flag == 1) {
        Shape r = createRect(mouseStart.x, mouseStart.y, me.getX(), me.getY());
        rectStruct.add(r);
        mouseStart = null;
        mouseEnd = null;
        repaint();
    }

    else if (flag == 3) {
        Shape r = createFillRect(mouseStart.x, mouseStart.y, me.getX(), me.getY());
        rectFillStruct.add(r);
        mouseStart = null;
        mouseEnd = null;
        repaint();
    }


    else if (flag == 7) {
        Shape r = createLine(mouseStart.x, mouseStart.y, me.getX(), me.getY());
        lineStruct.add(r);
        mouseStart = null;
        mouseEnd = null;
        repaint();
    } 
}

@SuppressWarnings("static-access")
public void actionPerformed(ActionEvent ae) {
    String command = ae.getActionCommand();
    Object source = ae.getSource();
    // instantiate the filechooser
    switch (command) {

    case "color":
        this.setPaintColor();
        break;

    case "rectangle":
        flag = 1;
        FLAG = 0;
        this.paintRect(gr);
        break;

    case "rectanglefill":
        flag = 3;
        this.paintRectFill(gr);
        break;


    case "line":
        flag = 7;
        this.paintLine(gr);
        break;


private void setPaintColor() {
    Color color = JColorChooser.showDialog(null, "Choose Paint Color", Color.black);
    color1 = color;
}


public static void paintRect(Graphics g) {

    gr = (Graphics2D) g;

    for (Shape s : rectStruct) {
        gr.setPaint(color1);
        gr.setStroke(new BasicStroke(2));

        gr.draw(s);
        // gr.setPaint(this.LINE_COLOR);
        // gr.fill(s);
    }
    if (flag == 1) {
        if (mouseStart != null && mouseEnd != null) {

        //makes outline while dragging rectangle
            gr.setPaint(Color.RED);
            Shape r = createRect(mouseStart.x, mouseStart.y, mouseEnd.x, mouseEnd.y);
            gr.draw(r);
        }
    }
}

public static void paintRectFill(Graphics g) {
    gr = (Graphics2D) g;

  for (Shape s : rectFillStruct) {
        gr.setPaint(color1);
        gr.setStroke(new BasicStroke(2));

        gr.draw(s);
        gr.fill(s);
    }
    if (flag == 3) {

        if (mouseStart != null && mouseEnd != null) {
            //makes outline while dragging rectangle
            gr.setPaint(Color.RED);
            Shape r = createFillRect(mouseStart.x, mouseStart.y, mouseEnd.x, mouseEnd.y);
            gr.draw(r);
        }
    }
}


public static void paintLine(Graphics g) {
    gr = (Graphics2D) g;

    for (Shape s : lineStruct) {
        gr.setPaint(color1);
        gr.setStroke(new BasicStroke(2));

        gr.draw(s);
        gr.fill(s);

    }

    if (flag == 7) {
        if (mouseStart != null && mouseEnd != null) {
            //makes outline while dragging rectangle
            gr.setPaint(Color.GREEN);
            Shape r = createLine(mouseStart.x, mouseStart.y, mouseEnd.x, mouseEnd.y);
            gr.draw(r);
        }
    }
}

public static Rectangle2D.Float createRect(int x1, int y1, int x2, int y2) {
    return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
}


public static Rectangle2D.Float createFillRect(int x1, int y1, int x2, int y2) {
    return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
}


public static Line2D.Float createLine(int x1, int y1, int x2, int y2) {
    return new Line2D.Float(x1, y1, x2, y2);
}}
  • 1
    You need to associate a `Color` with each `Shape`, so you know what color each `Shape` should be painted with. Remember, painting is destructive, you are expected to repaint the entire state of your component, from scratch, when `paintComponent` is called. Also, I can't see where `gr` is actually assigned a value, which just scares me – MadProgrammer Nov 21 '15 at 06:32
  • A complete example is cited [here](http://stackoverflow.com/a/11944233/230513). – trashgod Nov 21 '15 at 08:30
  • `I think its because Im saving the shapes in an arrayList` - you need to store a custom Object that contains both the Shape and the Color. See the `DrawOnComponent` example from [Custom Painting Approaches](https://tips4java.wordpress.com/2009/05/08/custom-painting-approaches/) for a working example that uses this approach. – camickr Nov 21 '15 at 16:20
  • Can you add the `PaintPanel` class to your question? And is `FLAG` (in the `actionPerformed` method) an int field? – Freek de Bruijn Nov 21 '15 at 16:53
  • You create a `JPanel` called `buttons` and add four buttons to it, but you do not add this panel with buttons to another component. – Freek de Bruijn Nov 21 '15 at 17:02
  • PaintPanel : http://pastebin.com/BEtp4iWr Full PaintAppFrame: http://pastebin.com/pveSB9gv – user3765848 Nov 21 '15 at 17:38
  • I really appreciate your help guys and @FreekdeBruijn – user3765848 Nov 24 '15 at 01:58

1 Answers1

0

As was already suggested earlier in the comments, your program should store the color for each shape. You could create a simple ColoredShape class that can store a shape and a color:

import java.awt.*;

public class ColoredShape {
    private Shape shape;
    private Color color;

    public ColoredShape(Shape shape, Color color) {
        this.shape = shape;
        this.color = color;
    }

    public Shape getShape() {
        return shape;
    }

    public Color getColor() {
        return color;
    }
}

In the PaintAppFrame class, the following changes could be made to use the new ColoredShape class for the rectangles:

// Field definition change:
//public static ArrayList<Shape> rectStruct = new ArrayList<Shape>();
public static ArrayList<ColoredShape> rectStruct = new ArrayList<ColoredShape>();

// Method mouseReleased change:
//rectStruct.add(r);
rectStruct.add(new ColoredShape(r, color1));

// Method paintRect changes:
//for (Shape s : rectStruct) {
    //gr.setPaint(PaintPanel.LINE_COLOR);
    //[...]
    //gr.draw(s);
for (ColoredShape coloredShape : rectStruct) {
    gr.setPaint(coloredShape.getColor());
    [...]
    gr.draw(coloredShape.getShape());

In the PaintPanel class, these changes could be made to use the new ColoredShape class for the rectangles:

// Field definition added:
private static ArrayList<ColoredShape> redoStructNew = new ArrayList<ColoredShape>();

// Method undo change:
//redoStruct.add(PaintAppFrame.rectStruct.get(i));
redoStructNew.add(PaintAppFrame.rectStruct.get(i));

// Method redo changes:
//if (!PaintAppFrame.rectStruct.isEmpty() && !redoStruct.isEmpty()) {
    //for (int i = 0; i < redoStruct.size(); i++) {
        //PaintAppFrame.rectStruct.add(redoStruct.get(i));
    //[...]
if (!PaintAppFrame.rectStruct.isEmpty() && !redoStructNew.isEmpty()) {
    for (int i = 0; i < redoStructNew.size(); i++) {
        PaintAppFrame.rectStruct.add(redoStructNew.get(i));
    [...]

You can change the code for the other types of shapes in a similar way.

Edit 1: line segments

For line segments, you'll need to store all relevant data as well. Looking at the drawInk method, I think you need: line coordinates, color, and stroke. So instead of storing the coordinates only (see the allStrokes field), you can store a list of line segments in a class like this:

import java.awt.*;
import java.awt.geom.Line2D;

public class LineSegment {
    private Line2D.Double coordinates;
    private Color color;
    private Stroke stroke;

    public LineSegment(Line2D.Double coordinates, Color color, Stroke stroke) {
        this.coordinates = coordinates;
        this.color = color;
        this.stroke = stroke;
    }

    public void draw(Graphics2D graphics2D) {
        graphics2D.setColor(color);
        graphics2D.setStroke(stroke);
        graphics2D.draw(coordinates);
    }
}

Edit 2: drawing order

The order in which the different graphical objects are currently drawn is based on the type of the object instead of when the user added the object. I think the best way to fix this is by storing all graphical objects in a single list, which will decide the drawing order. You can create a super class GraphicalObject that the ColoredShape, Entity, and LineSegment classes extend from. This class can even define shared/overridable methods like draw (already in LineSegment). The GraphicalObject class can be very simple:

import java.awt.Graphics2D;

public abstract class GraphicalObject {
    public abstract void draw(Graphics2D graphics2D);
}

Modify the ColoredShape, Entity, and LineSegment classes like this:

public class LineSegment extends GraphicalObject {
    // [...]

    // Each class should implement the draw method:
    public void draw(Graphics2D graphics2D) {
        // [...]
    }
}

Now you can replace all data structures that store shapes, lines, and entities with a single list of graphical objects:

private List<GraphicalObject> graphicalObjects = new ArrayList<>();

//public static ArrayList<ColoredShape> rectStruct = new ArrayList<ColoredShape>();
// [...]
//public static ArrayList<ColoredShape> lineStruct = new ArrayList<ColoredShape>();

Make sure you use this new list to store all graphical objects that you want to draw in the PaintPanel.paintComponent method (it seems that at the moment some objects are stored in the PaintAppFrame class and others in the PaintPanel class):

paintInkStrokes(g);
paintEraser(g);
PaintAppFrame.paintRect(g);
PaintAppFrame.paintCircle(g);
PaintAppFrame.paintRectFill(g);
PaintAppFrame.paintFillCircle(g);
PaintAppFrame.paintRoundRectangle(g);
PaintAppFrame.paintRoundRectangleFill(g);
PaintAppFrame.paintLine(g);
paintEntities(g);

Some additional advice: I think it would also be possible to make some parts of the code be able to handle all types of shapes, instead of having code for each type. For example in the PaintAppFrame class, the paintRect until paintLine methods have a lot of code in common. Also in the undo and redo methods of the PaintPanel class, there are a lot of blocks of code that are almost the same.

Freek de Bruijn
  • 3,552
  • 2
  • 22
  • 28
  • Hi Freek that sort of works. I have 2 more problems. 1) The fill shapes do not work as gr.fill(s) needs s of type shape. 2) The shapes seem to be working fine but now when I change the color of the shapes the colors of the strokes changes as well, Im guessing thats because they dont use the ColoredShapes class? – user3765848 Nov 24 '15 at 01:53
  • Okay I figured out the fill problem gr.fill(coloredShape.getShape()); – user3765848 Nov 24 '15 at 02:07
  • To retain other colors that you use while drawing shapes, you will need to store and retrieve those as well. It might even be necessary to add more fields to the `ColoredShape` class (like fill/stroke/border color). Check methods `paintRectFill`, `paintCircle`, `paintFillCircle`, `paintRoundRectangle`, `paintRoundRectangleFill`, and `paintLine` for color use. (You can update the code in your question to your latest version if there are any remaining issues, to make sure we are looking at the same code.) – Freek de Bruijn Nov 24 '15 at 15:44
  • PaintAppFrame: http://pastebin.com/ZBN50wew PaintPanel: http://pastebin.com/JwKJWQYM – user3765848 Nov 24 '15 at 23:33
  • ^I posted the version I have. I have the colors of the shapes working fine. Now where Im facing problem is when I do a free hand line with a paint brush and then when I create one of the shapes, the color of the first stroke drawn changes to the color I drew for the shape. I believe its because the THIN_STROKE and THICK_STROKE are of the Stroke shape? – user3765848 Nov 24 '15 at 23:35
  • For the line segments, you'll need to store related information like color and stroke as well. See for example the `LineSegment` class I've added to the answer above. – Freek de Bruijn Nov 25 '15 at 15:20
  • I discovered another bug now :( When I draw a filled shape with color, and then I draw on it with another color, after that if I draw another shape, the previous drawn shape paints itself again and makes the drawing on top of the shape to disappear. PaintPanel: http://pastebin.com/5jmZdBST PaintAppFrame: http://pastebin.com/cLd4HQbq I think its because of the paintComponent method in PaintPanel where its calling all the paint shapes methods again. But how will I paint without that overriden paintComponent method? – user3765848 Nov 27 '15 at 00:32
  • You are right, this is caused by the way the `PaintPanel.paintComponent` method works: the order in which the different graphical objects are drawn, is based on the type of the object instead of when the user added the object. I think the best way to fix this is by storing all graphical objects in a single list, which will decide the drawing order. You can create a super class `GraphicalObject` that the `ColoredShape`, `Entity`, and `LineSegment` classes extend from. This class can even define shared/overridable methods like `draw` (already in `LineSegment`). – Freek de Bruijn Nov 27 '15 at 11:05
  • would you mind giving me an example of GraphicalObject in the answer? – user3765848 Nov 29 '15 at 18:58
  • I dont understand what you mean on how to change the PaintPanel.paintComponent method. How would I call the new list in that method? – user3765848 Nov 30 '15 at 21:22
  • Paint all the graphical objects in the new list (instead of the objects in the old lists). – Freek de Bruijn Nov 30 '15 at 21:26
  • i.e. in the draw method in the GraphicalObject class? – user3765848 Nov 30 '15 at 21:39
  • In the `PaintPanel.paintComponent` method. – Freek de Bruijn Nov 30 '15 at 21:42
  • I'm getting the impression that these questions and answers interactions are no longer productive. Once you have one issue solved, another issue comes up and you add another question. I think Stack Overflow works best when a question focuses on a single issue and new focused questions are created for other issues. Also try to keep your questions on-topic and relevant to other readers (see http://stackoverflow.com/help/on-topic). – Freek de Bruijn Nov 30 '15 at 22:09