I have this code for Java Paint App. I have added a slider that changes from 1 to 20 with a default size of 1.This slider is for changing the strokeSize dynamically,so i have created a variable strokeSizeVal and the slider + Listener for it and the new stroke size updates and works just fine, but it updates for all the shapes that are added in the array..Even for shapes that are already drawn. I save individual stroke weights in a multidimensional array along with the objects. How can i change this,so that it doesn’t update all the shapes in the array and is valid only for newly created shapes?
I guess this line makes the problem: graphSettings.setStroke(strokeSizeCounter.next());
thanks
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.*;
import java.awt.*;
import java.awt.geom.*;
import java.text.DecimalFormat;
import java.util.*;
@SuppressWarnings("serial")
public class Lesson48 extends JFrame{
JButton brushBut, lineBut, ellipseBut, rectBut, strokeBut, fillBut;
JSlider transSlider;
JLabel transLabel;
DecimalFormat dec = new DecimalFormat("#.##");
JSlider strokeSizeSlider;
JLabel strokeSizeLabel;
Graphics2D graphSettings;
int strokeSize;
//graphSettings.setStroke(new BasicStroke(5f));
int currentAction = 1;
float transparentVal = 1.0f;
Color strokeColor = Color.BLACK, fillColor = Color.BLACK;
public static void main(String[] args) {
new Lesson48();
}
public Lesson48() {
this.setSize(1000, 600);
this.setTitle("Java Paint");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel buttonPanel = new JPanel();
Box theBox = Box.createHorizontalBox();
brushBut = makeMeButtons("./src/Brush.png", 1);
lineBut = makeMeButtons("./src/Line.png", 2);
ellipseBut = makeMeButtons("./src/Ellipse.png", 3);
rectBut = makeMeButtons("./src/Rectangle.png", 4);
strokeBut = makeMeColorButton("./src/Stroke.png", 5, true);
fillBut = makeMeColorButton("./src/Fill.png", 6, false);
theBox.add(brushBut);
theBox.add(lineBut);
theBox.add(ellipseBut);
theBox.add(rectBut);
theBox.add(strokeBut);
theBox.add(fillBut);
transLabel = new JLabel(" Transparent: 1");
transSlider = new JSlider(1, 99, 99);
ListenerForSlider lForSlider = new ListenerForSlider();
transSlider.addChangeListener(lForSlider);
theBox.add(transLabel);
theBox.add(transSlider);
// changing the strokeSize dynamically
strokeSizeLabel = new JLabel(" Stroke: 1");
strokeSizeSlider = new JSlider(1, 20, 1);
ListenerForSlider2 sForSlider = new ListenerForSlider2();
strokeSizeSlider.addChangeListener(sForSlider);
theBox.add(strokeSizeLabel);
theBox.add(strokeSizeSlider);
buttonPanel.add(theBox);
this.add(buttonPanel, BorderLayout.SOUTH);
this.add(new DrawingBoard(), BorderLayout.CENTER);
this.setVisible(true);
}
public JButton makeMeButtons(String iconFile, final int actionNum) {
JButton theBut = new JButton();
Icon butIcon = new ImageIcon(iconFile);
theBut.setIcon(butIcon);
theBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
currentAction = actionNum;
}
});
return theBut;
}
public JButton makeMeColorButton(String iconFile, final int actionNum, final boolean stroke) {
JButton theBut = new JButton();
Icon butIcon = new ImageIcon(iconFile);
theBut.setIcon(butIcon);
theBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(stroke) {
strokeColor = JColorChooser.showDialog(null, "Pick a Stroke", Color.BLACK);
} else {
fillColor = JColorChooser.showDialog(null, "Pick a Fill", Color.BLACK);
}
}
});
return theBut;
}
private class DrawingBoard extends JComponent{
ArrayList<Shape> shapes = new ArrayList<Shape>();
ArrayList<Color> shapeFill = new ArrayList<Color>();
ArrayList<Color> shapeStroke = new ArrayList<Color>();
ArrayList<Float> transPercent = new ArrayList<Float>();
ArrayList<Stroke> shapeStrokeSize = new ArrayList<Stroke>();
Point drawStart, drawEnd;
public DrawingBoard() {
this.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if(currentAction != 1) {
drawStart = new Point(e.getX(), e.getY());
drawEnd = drawStart;
repaint();
}
}
public void mouseReleased(MouseEvent e) {
if(currentAction != 1) {
Shape aShape = null;
if(currentAction == 2) {
aShape = drawLine(drawStart.x, drawStart.y, e.getX(), e.getY());
} else
if(currentAction == 3) {
aShape = drawEllipse(drawStart.x, drawStart.y, e.getX(), e.getY());
} else
if(currentAction == 4) {
aShape = drawRectangle(drawStart.x, drawStart.y, e.getX(), e.getY());
}
shapes.add(aShape);
shapeFill.add(fillColor);
shapeStroke.add(strokeColor);
transPercent.add(transparentVal);
//shapeStrokeSize.add(strokeSize);
drawStart = null;
drawEnd = null;
repaint();
}
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
if(currentAction == 1) {
int x = e.getX();
int y = e.getY();
Shape aShape = null;
strokeColor = fillColor;
aShape = drawBrush(x, y, 2, 2);
shapes.add(aShape);
shapeFill.add(fillColor);
shapeStroke.add(strokeColor);
transPercent.add(transparentVal);
//shapeStrokeSize.add(strokeSize);
}
drawEnd = new Point(e.getX(), e.getY());
repaint();
}
});
}
public void paint(Graphics g) {
graphSettings = (Graphics2D)g;
graphSettings.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphSettings.setStroke(new BasicStroke(strokeSize));
Iterator<Color> strokeCounter = shapeStroke.iterator();
Iterator<Color> fillCounter = shapeFill.iterator();
Iterator<Float> transCounter = transPercent.iterator();
Iterator<Stroke> strokeSizeCounter = shapeStrokeSize.iterator();
for(Shape s : shapes) {
//graphSettings.setStroke(strokeSizeCounter.next());
//graphSettings.draw(s);
graphSettings.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transCounter.next()));
graphSettings.setPaint(strokeCounter.next());
graphSettings.draw(s);
graphSettings.setPaint(fillCounter.next());
graphSettings.fill(s);
}
if(drawStart != null && drawEnd != null) {
graphSettings.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
graphSettings.setPaint(Color.LIGHT_GRAY);
Shape aShape = null;
if(currentAction == 2) {
aShape = drawLine(drawStart.x, drawStart.y, drawEnd.x, drawEnd.y);
} else
if(currentAction == 3) {
aShape = drawEllipse(drawStart.x, drawStart.y, drawEnd.x, drawEnd.y);
} else
if(currentAction == 4) {
aShape = drawRectangle(drawStart.x, drawStart.y, drawEnd.x, drawEnd.y);
}
graphSettings.draw(aShape);
}
}
private Rectangle2D.Float drawRectangle(int x1, int y1, int x2, int y2){
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 Rectangle2D.Float(x, y, width, height);
}
private Ellipse2D.Float drawEllipse(int x1, int y1, int x2, int y2){
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 Ellipse2D.Float(x, y, width, height);
}
private Line2D.Float drawLine(int x1, int y1, int x2, int y2){
return new Line2D.Float(x1, y1, x2, y2);
}
private Ellipse2D.Float drawBrush(int x1, int y1, int brushStrokeWidth, int brushStrokeHeight){
return new Ellipse2D.Float(x1, y1, brushStrokeWidth, brushStrokeHeight);
}
}
private class ListenerForSlider implements ChangeListener{
public void stateChanged(ChangeEvent e) {
if(e.getSource() == transSlider) {
transLabel.setText(" Transparent: " + dec.format(transSlider.getValue() * .01));
transparentVal = (float) (transSlider.getValue() * .01);
}
}
}
private class ListenerForSlider2 implements ChangeListener{
public void stateChanged(ChangeEvent e) {
if(e.getSource() == strokeSizeSlider) {
strokeSizeLabel.setText(" Stroke: " + strokeSizeSlider.getValue());
strokeSize = strokeSizeSlider.getValue();
}
}
}
}
If this were my project, I'd draw to an off-screen buffer, a BufferedImage, and then display that image in my painting method. This way, the current stroke width would only be used on the newest drawing. I'd also:
super.paintComponent(g)if overridingpaintComponent)For example in the code below I have a BufferedImage is created to fill the drawing JPanel
I have a nested class called MyMouse that extends MouseAdapter. This allows it to be used as both a MouseListener and a MouseMotionListener, and in fact this is what I do in the constructor:
In this mouse adapter I override the mousePressed, mouseDragged, and mouseReleased methods.
In mousePressed, I extract a Graphics2D object from the buffered image and get my starting drawing point, my x1, and y1:
I also get the stroke width selection from the slider and use it to set my Graphics object's Stroke:
Just for fun, I generate a random color for each brush stroke
I then draw a "point" by drawing a line of length 0, and then call
repaint():In mouseDragged and mouseReleased I check to make sure that my graphics object is not null. If it is OK, I get my second point, x2, y2, and draw a line between x1, y1 and x2, y2:
I then reset x1 and y1 with x2, y2 in anticipation of drawing the next line, and then call
repaint()In mouseReleased I do similar things as above, but then I dispose of the Graphics2D object obtained from the image (it is OK to dispose of a Graphics object that the coder creates, but never do this to a Graphics object given to you by the JVM, such as given via a
paintComponentmethod):Then, all the painting method has to do (again here, it is paintComponent, not paint) is draw the buffered image, if it is not null:
Don't forget to call the super's method so that housekeeping painting will be done.
The whole shooting match: