I have created a simple paint program. All the functionalities are working, but when I go to create a new line, the program does not recognize that it is creating a new line, and instead just creates one huge line. The code will compile if you run it. Any help would be greatly appreciated. Thank you!
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import javax.swing.*;
public class SimplePaint extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
JButton action = new JButton();
JButton red = new JButton();
JButton blue = new JButton();
JButton yellow = new JButton();
Color initial = Color.MAGENTA;
JButton thin = new JButton();
JButton medium = new JButton();
JButton thick = new JButton();
Stroke stroke = new BasicStroke(3);
private static ArrayList<Point> points = new ArrayList<Point>();
JButton erase = new JButton();
JButton drawing = new JButton();
Point start = null;
Point end = null;
Line2D draw = new Line2D.Float();
JPanel panel = new JPanel();
public SimplePaint(){
getContentPane().add(panel);
setSize(450, 450);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
design();
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
//points.clear();
points.add(e.getPoint());
// repaint();
}
public void mouseReleased(MouseEvent e){
points.add(e.getPoint());
// points.clear();
//repaint();
}
});
addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
points.add(e.getPoint());
repaint();
}
});
blue.addActionListener(this);
red.addActionListener(this);
yellow.addActionListener(this);
thin.addActionListener(this);
medium.addActionListener(this);
thick.addActionListener(this);
erase.addActionListener(this);
drawing.addActionListener(this);
}
public void design(){
panel.setBackground(Color.BLACK);
blue.setBackground(Color.BLUE);
blue.setPreferredSize(new Dimension(50, 25));
panel.add(blue);
red.setBackground(Color.RED);
red.setPreferredSize(new Dimension(50, 25));
panel.add(red);
yellow.setBackground(Color.yellow);
yellow.setPreferredSize(new Dimension(50, 25));
panel.add(yellow);
thin.setText("Thin");
panel.add(thin);
medium.setText("Medium");
panel.add(medium);
thick.setText("Thick");
panel.add(thick);
erase.setText("Erase");
panel.add(erase);
drawing.setText("Draw");
panel.add(drawing);
}
public void actionPerformed(ActionEvent e){
if(e.getSource() == blue){
initial = Color.BLUE;
}else if(e.getSource() == red){
initial = Color.RED;
}else if(e.getSource() == yellow){
initial = Color.YELLOW;
}else if(e.getSource() == thin){
stroke = new BasicStroke(1);
}else if(e.getSource() == medium){
stroke = new BasicStroke(5);
}else if(e.getSource() == thick){
stroke = new BasicStroke(10);
}else if(e.getSource() == erase){
initial = Color.BLACK;
}
//repaint();
}
#Override
public void paint(Graphics g){
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(initial);
g2.setStroke(stroke);
if(points != null && points.size() > 1){
for(int p = 0; p < points.size() - 1; p++){
int x1 = points.get(p).x;
int y1 = points.get(p).y;
int x2 = points.get(p + 1).x;
int y2 = points.get(p + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
}
g2.dispose();
}
public static void main(String []args){
SimplePaint s =new SimplePaint();
s.setVisible(true);
}
}
It looks to me as though you need to be storing the line segments separately rather than in one list of points. To do that you need to define a class to hold them and store a list of segments rather than points.
private class Segment {
private final List<Point> points = new ArrayList<Point>();
private final Color color = initial;
private final Stroke stroke = SimplePaint.this.stroke;
}
private final List<Segment> segments = new ArrayList<>();
Then create a new segment each time the mouse is pressed and add the point to the current segment each time the mouse is dragged:
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
segments.add(0, new Segment());
segments.get(0).points.add(e.getPoint());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
segments.get(0).points.add(e.getPoint());
repaint();
}
});
You don't need to do anything when the mouse is released as the point will already have been added on drag. You can delete that listener.
Your paint method then needs to iterate through all segments:
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
for (Segment segment : segments) {
g2.setColor(segment.color);
g2.setStroke(segment.stroke);
for (int p = 0; p < segment.points.size() - 1; p++) {
Point p1 = segment.points.get(p);
Point p2 = segment.points.get(p + 1);
g2.drawLine(p1.x, p1.y, p2.x, p2.y);
}
}
g2.dispose();
}
I've removed your check to ignore lines with less than two points: the for loop will skip those anyway so it's redundant.
Inserting the new segments at index 0 makes the code to add points simple but has the disadvantage that lines drawn first overwrite later lines. Simple solution would be to use a Deque and peekLast rather than List and get(0).
There are lots of other improvements (particularly performance) you can make but I've tried those changes myself and they work fine.
Related
It runs, the buttons work, but i just can't get it to draw with the paint(). I think it has something to do with the main method on SwingDraw, but i'm not 100% sure. Thanks for any help and sorry for the long code :/
SwingDraw:
// Import Core Java packages
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
public class SwingDraw extends JFrame implements ActionListener, ItemListener{
// Initial Frame size
static final int WIDTH = 1500; // frame width
static final int HEIGHT = 1000; // frame height
// Color choices
static final String[] COLOR_NAMES = new String[]{"None", "Red", "Blue", "Green"};
static final Color COLORS[] = {null, Color.red, Color.blue, Color.green };
// Button control
JButton circle;
JButton roundRec;
JButton threeDRec;
JButton line;
JButton square;
JButton oval;
JButton clear;
JButton delete;
// List to keep track of shapes
JList<String> shapesKeeper;
// Color choice box
JComboBox<String> colorChoice = new JComboBox<>(COLOR_NAMES);
SwingDrawCanvas betterCanvas = new SwingDrawCanvas();
/**
* Constructor
*/
public SwingDraw() {
// Create a frame
setSize(WIDTH, HEIGHT);
setLocation(130, 100);
// add window closing listener
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create panel for controls
JPanel topPanel = new JPanel();
JPanel leftList = new JPanel();
// create button control
JPanel buttonPanel = new JPanel();
topPanel.add(buttonPanel, BorderLayout.NORTH);
circle = new JButton("Circle");
buttonPanel.add(circle);
roundRec = new JButton("Rounded Rectangle");
buttonPanel.add(roundRec);
threeDRec = new JButton("3D Rectangle");
buttonPanel.add(threeDRec);
line = new JButton("Line");
buttonPanel.add(line);
square = new JButton("Square");
buttonPanel.add(square);
oval = new JButton("Oval");
buttonPanel.add(oval);
clear = new JButton("Clear");
buttonPanel.add(clear);
JPanel listPanel = new JPanel();
leftList.add(listPanel, BorderLayout.WEST);
JList<String> shapesKeeper = new JList<>();
listPanel.add(shapesKeeper);
delete = new JButton("Delete Shape");
listPanel.add(delete);
// add button listener
circle.addActionListener(this);
roundRec.addActionListener(this);
threeDRec.addActionListener(this);
line.addActionListener(this);
square.addActionListener(this);
oval.addActionListener(this);
clear.addActionListener(this);
// create panel for color choices
JPanel colorPanel = new JPanel();
JLabel label = new JLabel("Filled Color:");
topPanel.add(colorPanel);
colorPanel.add(label);
JComboBox<String> colorChoice = new JComboBox<>(COLOR_NAMES);
colorPanel.add(colorChoice);
colorChoice.addItemListener(this);
add(topPanel, BorderLayout.NORTH);
add(leftList, BorderLayout.WEST);
setVisible(true);
} // end of constructor
/**
* Implementing ActionListener
*/
public void actionPerformed(ActionEvent event) {
if(event.getSource() == circle) { // circle button
betterCanvas.setShape(SwingDrawCanvas.CIRCLE);
}
else if(event.getSource() == roundRec) { // rounded rectangle button
betterCanvas.setShape(SwingDrawCanvas.ROUNDED_RECTANGLE);
}
else if(event.getSource() == threeDRec) { // 3D rectangle button
betterCanvas.setShape(SwingDrawCanvas.RECTANGLE_3D);
}
else if(event.getSource() == clear){
betterCanvas.setShape(SwingDrawCanvas.CLEAR);
}
else if(event.getSource() == line){
betterCanvas.setShape(SwingDrawCanvas.LINE);
}
else if(event.getSource() == square){
betterCanvas.setShape(SwingDrawCanvas.SQUARE);
}
else if(event.getSource() == oval){
betterCanvas.setShape(SwingDrawCanvas.OVAL);
}
}
/**
* Implementing ItemListener
*/
public void itemStateChanged(ItemEvent event) {
Color color = COLORS[colorChoice.getSelectedIndex()];
betterCanvas.setFilledColor(color);
}
/**
* the main method
*/
public static void main(String[] argv) {
new SwingDraw();
}
}
SwingDrawCanvas:
public class SwingDrawCanvas extends JPanel implements MouseListener, MouseMotionListener {
// Constants for shapes
public static final int CIRCLE = 1;
public static final int ROUNDED_RECTANGLE = 2;
public static final int RECTANGLE_3D = 3;
public static final int LINE = 4;
public static final int SQUARE = 5;
public static final int OVAL = 6;
public static final int CLEAR = 7;
// Coordinates of points to draw
private int x1, y1, x2, y2;
// shape to draw
private int shape = CIRCLE;
/**
* Method to set the shape
*/
public void setShape(int thisShape) {
System.out.println("HEY");
this.shape = thisShape;
System.out.println(shape);
}
// filled color
private Color filledColor = null;
/**
* Method to set filled color
*/
public void setFilledColor(Color color) {
filledColor = color;
}
/**
* Constructor
*/
public SwingDrawCanvas() {
addMouseListener(this);
addMouseMotionListener(this);
} // end of constructor
/**
* painting the component
*/
public void paint(Graphics g) {
System.out.println("ARHFHASJDASHDHAs");
super.paint(g);
System.out.println("AHAHAHAHAHAHAHA");
g.drawString("FUCK", 1, 1);
// the drawing area
int x, y, width, height;
// determine the upper-left corner of bounding rectangle
x = Math.min(x1, x2);
y = Math.min(y1, y2);
// determine the width and height of bounding rectangle
width = Math.abs(x1 - x2);
height = Math.abs(y1 - y2);
if(filledColor != null)
g.setColor(filledColor);
switch (shape) {
case ROUNDED_RECTANGLE :
if(filledColor == null)
g.drawRoundRect(x, y, width, height, width/4, height/4);
else
g.fillRoundRect(x, y, width, height, width/4, height/4);
break;
case CIRCLE :
int diameter = Math.max(width, height);
if(filledColor == null)
System.out.println("HRE BITCHS");
else
System.out.println("HEY FUCK YOU GUY");
break;
case RECTANGLE_3D :
if(filledColor == null)
g.draw3DRect(x, y, width, height, true);
else
g.fill3DRect(x, y, width, height, true);
break;
}
}
/**
* Implementing MouseListener
*/
public void mousePressed(MouseEvent event) {
x1 = event.getX();
y1 = event.getY();
}
public void mouseReleased(MouseEvent event) {
x2 = event.getX();
y2 = event.getY();
repaint();
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
/**
* Implementing MouseMotionListener
*/
public void mouseDragged(MouseEvent event) {
x2 = event.getX();
y2 = event.getY();
repaint();
}
public void mouseMoved(MouseEvent e) {}
}
I'm not very good at this, I'm just looking for any help you guys can give me :D
Instead of having paint(), your function should be name paintComponent(Graphics g).
When creating the paintComponent(Graphics g) functions, you need #Override so that java recognizes the function because you can not call paintComponent(Graphics g) directly.
In addition, any time you want to refresh your panel, you can call repaint() or you can resize your JFrame after the code as compiled. Both of these methods will call the paintComponent(Graphics g) function.
So this is part of my game panel for my game. I got my start button to work, and created a reset button but I don't know how to reset the game every single time it is pressed. Does anyone know how to reset the game to its original position after you press "Reset"? Right now it will display the words "You have died", but it doesn't do anything when I press Reset.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
public class GamePanel extends JPanel
{
private Brick[][] bary;
private static final Color BACKGROUND = Color.black;
private BufferedImage myImage;
private Graphics myBuffer;
private Ball ball = new Ball();
public Bumper bumper;
private Timer t;
private int hits = 0;
private boolean isPlayingGame = false;
public GamePanel()
{
myImage = new BufferedImage(600, 800, BufferedImage.TYPE_INT_RGB);
myBuffer = myImage.getGraphics();
myBuffer.setColor(BACKGROUND);
myBuffer.fillRect(0, 0, 600,800);
JButton sbutton = new JButton("Start");
sbutton.setFocusable(false);
sbutton.addActionListener(new Listener());
add(sbutton);
JButton rbutton = new JButton("Reset");
rbutton.setFocusable(false);
rbutton.addActionListener(new Listener());
add(rbutton);
bary = new Brick[5][14];
bumper = new Bumper(270, 775, 60, 10, Color.BLUE);
ball = new Ball(300, 400, 10, Color.RED);
t = new Timer(5, new Listener());
setFocusable(true);
//addKeyListener(new Key());
}
// tick method is called every 10ms by Arkanoid.java
// only does stuff if game is actually being played
public void tick()
{
if(isPlayingGame)
{
ball.move(600,800, 600, 800);
death(ball);
}
}
// these get called by the BumperLitsener, which is added to the whole frame
public void moveBumperLeft()
{
bumper.setX(bumper.getX()-20);
//System.out.println("moving bumper left");
}
public void moveBumperRight()
{
bumper.setX(bumper.getX()+20);
//System.out.println("moving bumper right");
}
/*
public class Key extends KeyAdapter
{
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_A)
bumper.setX(bumper.getX()+10 );
if(e.getKeyCode() == KeyEvent.VK_S)
bumper.setX(bumper.getX()-10 );
}
}
*/
public void startGame()
{
isPlayingGame = true;
}
public void paintComponent(Graphics g)
{
// draw myBuffer, then draw that onto g
myBuffer.setColor(BACKGROUND);
myBuffer.fillRect(0,0,600,800);
setLayout(new GridLayout(5, 14));
myBuffer.setColor(Color.WHITE);
int b=1;
int d=40;
for(int r = 0; r < bary.length; r++)
{
for(int c = 0; c < bary[0].length; c++)
{
bary[r][c] = new Brick(b, d,40,20, Color.BLUE);
b=b+43;
bary[r][c].draw(myBuffer);
}
d=d+23;
b=1;
}
ball.draw(myBuffer);
bumper.draw(myBuffer);
repaint();
if(ball.getColor()==Color.BLACK)
{
myBuffer.setFont(new Font("MS Comic Sans", Font.ITALIC, 45));
myBuffer.drawString("GAME OVER!", 168, 300);
ball.move(0, 0, 0, 0);
}
g.drawImage(myImage, 0, 0, getWidth(), getHeight(), null);
}
private class Listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
GamePanel.this.getTopLevelAncestor().requestFocus();
startGame(); // starts game when button is pressed
System.out.println("Game is starting!");
}
}
private void death(Ball ball)
{
double d = ball.getY() + ball.getRadius();
if(d>800.0)
{
ball.setX(300);
ball.setY(400);
ball.setdx(ball.getdx()*-1);
ball.setdy(ball.getdy()*-1);
ball.setColor(Color.BLACK);
}
}
/* private void collide(Ball ball, Bumper b)
{
double d = distance(ball.getX(), ball.getY(), b.getX(), b.getY());
if(d <= 37.5)
{
ball.move();
hits++;
}
}*/
// private double distance(double x1, double y1, double x2, double y2)
// {
// return(Math.sqrt(Math.pow(x2 - x1, 2.0) + Math.pow(y2 - y1, 2.0))); // enter the calculation here.
//}
}
Thank you so much!!!
Right now, you have a single Listener class and two buttons that have registered against the listener (sbutton and rbutton). It'd be easier if you had different classes that implemented ActionListener, but let's move past that for now.
You need to make modifications to your actionPerformed method such that:
You can differentiate the source of the action - i.e. which button was pressed - Reset or the Start? Hint: use the getSource() method from ActionEvent
If the button pressed was the Reset button, you need to reset the GUI to its initial state
I posted this code earlier, and got a very good answer, but not a working answer. Could someone please show me how to change my code in order to get this fixed? This will run and compile. It is supposed to draw a line on top of a line, but instead it is drawing under the previous line. I have tried using Collections.reverse(segments); but it doesn't take care of element at zero. I also have tried changing to segments.add(new Segment()); but I am not sure what to change in MouseMotionListener to get this to work accordingly. Any help with this would be great! Thank You! :)
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class SimplePaint extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
JButton action = new JButton();
JButton red = new JButton();
JButton blue = new JButton();
JButton yellow = new JButton();
Color initial = Color.MAGENTA;
JButton thin = new JButton();
JButton medium = new JButton();
JButton thick = new JButton();
Stroke stroke = new BasicStroke(3);
private static ArrayList<Point> points = new ArrayList<Point>();
JButton erase = new JButton();
JButton drawing = new JButton();
Point start = null;
Point end = null;
Line2D draw = new Line2D.Float();
JPanel panel = new JPanel();
private class Segment {
private final List<Point> points = new ArrayList<Point>();
private final Color color = initial;
private final Stroke stroke = SimplePaint.this.stroke;
}
private final List<Segment> segments = new ArrayList<>();
public SimplePaint() {
getContentPane().add(panel);
setSize(450, 450);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
design();
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
segments.add(0, new Segment());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
segments.get(0).points.add(e.getPoint());
repaint();
}
});
addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
points.add(e.getPoint());
repaint();
}
});
blue.addActionListener(this);
red.addActionListener(this);
yellow.addActionListener(this);
thin.addActionListener(this);
medium.addActionListener(this);
thick.addActionListener(this);
erase.addActionListener(this);
drawing.addActionListener(this);
}
public void design() {
panel.setBackground(Color.BLACK);
blue.setBackground(Color.BLUE);
blue.setPreferredSize(new Dimension(50, 25));
panel.add(blue);
red.setBackground(Color.RED);
red.setPreferredSize(new Dimension(50, 25));
panel.add(red);
yellow.setBackground(Color.yellow);
yellow.setPreferredSize(new Dimension(50, 25));
panel.add(yellow);
thin.setText("Thin");
panel.add(thin);
medium.setText("Medium");
panel.add(medium);
thick.setText("Thick");
panel.add(thick);
erase.setText("Erase");
panel.add(erase);
drawing.setText("Draw");
panel.add(drawing);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == blue){
initial = Color.BLUE;
}else if(e.getSource() == red){
initial = Color.RED;
}else if(e.getSource() == yellow){
initial = Color.YELLOW;
}else if(e.getSource() == thin){
stroke = new BasicStroke(1);
}else if(e.getSource() == medium){
stroke = new BasicStroke(5);
}else if(e.getSource() == thick){
stroke = new BasicStroke(10);
}else if(e.getSource() == erase){
initial = Color.WHITE;
stroke = new BasicStroke(15);
}
//repaint();
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
int x1, y1, x2, y2;
for (Segment segment : segments) {
g2.setColor(segment.color);
g2.setStroke(segment.stroke);
for (int p = 0; p < segment.points.size() - 1; p++) {
x1 = segment.points.get(p).x;
y1 = segment.points.get(p).y;
x2 = segment.points.get(p + 1).x;
y2 = segment.points.get(p + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
}
g2.dispose();
}
public static void main(String []args){
SimplePaint s = new SimplePaint();
s.setVisible(true);
}
}
To fix your issue, you need to add to the end of the List and not the beginning:
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
segments.add(new Segment());
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
segments.get(segments.size() - 1).points.add(e.getPoint());
repaint();
}
});
Additionally, the second mouseDragged() implementation appears redundant, if you remove it, it still functions as expected.
// Can be removed
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
});
Also, you should really use a JComponent or JPanel to paint your sketch, #Override paintComponent() as opposed to paint() with this approach. This will help (or should completely) remove the flickering you see. Coupled with a javax.swing.Timer to repaint() as opposed to dragging which could update extremely (and unnecessarily) quickly.
Finally, you should ensure your GUI is running on the event dispatch thread using SwingUtilities.invokeLater(Runnable doRun), since Swing objects are not thread safe.
e.g.
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SimplePaint s = new SimplePaint();
s.setVisible(true);
}
});
}
I am having a problem with my code, if I click on rectangle and do the mouse clicks on the screen it will show the shape and it works fine.. but if I clicked on the circle after that, all the rec
tangles that have been stored stays on same location but changes to circle. how can I prevent the shapes that have been stored from changing?
First Class:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class MouseClick implements ActionListener{
private static int x,y;
private static DrawingObjects object = new DrawingObjects();
JPanel panel = new JPanel();
JFrame frame = new JFrame("MouseClick");
MouseClick(){
dObjects();
}
void dObjects() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(2,2));
frame.add(object);
frame.add(panel);
panel.setBackground(Color.WHITE);
panel.setSize(10, 300);
panel.setLocation(100, 200);
panel.setLayout(new GridLayout(1,2));
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setSize(400, 400);
object.addMouseListener(new AL());
Button rect = new Button("Rectangle");
Button oval = new Button("Circle");
panel.add(rect);
panel.add(oval);
rect.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
object.setType(1);
}
});
oval.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
object.setType(2);
}
}); }
public static void main(String[] args){
new MouseClick();
}
static class AL extends MouseAdapter{
public void mouseClicked (MouseEvent e){
x = e.getX();
y = e.getY();
object.drawing(x, y);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
Second Class:
import javax.swing.*;
import javafx.scene.input.MouseEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.*;
public class DrawingObjects extends JPanel{
private ArrayList<Point> points = new ArrayList<>();
public int shapetype ;
public void drawing(int x, int y){
points.add(new Point(x, y));
repaint();
}
public void setType(int choice){
if(choice==1){
shapetype = 1;
}
else if (choice ==2){
shapetype = 2;
}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(shapetype == 1){
for(Point p : points){
g.fillRect(p.x, p.y, 60, 20);
g.setColor(Color.GREEN);
repaint();
}
}
else if (shapetype == 2){
for(Point p : points){
g.fillOval(p.x, p.y, 20, 20);
g.setColor(Color.RED);
repaint();
}
}
}
}
The problem occurs because the shapetype is not stored along with the points on which the shapes are drawn. As a result, within the paintComponent method, when all shapes in locations stored in points array get redrawn, they use the current value of shapetype. Not the value of shapetype when the shape was created for the first time.
A simple (yet not the best) solution, with minimum code change would be to store the shapetype as follows.
In DrawingObjects class, add another array to store the shapetype.
private ArrayList<Integer> shapeTypes = new ArrayList<>();
Update the drawing method as follows to store the current shapetype along with the point.
public void drawing(int x, int y) {
points.add(new Point(x, y));
shapeTypes.add(shapetype);
repaint();
}
Refer the stored shapetype when repainting the shapes as follows.
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < points.size(); i++) {
Point p = points.get(i);
if (shapeTypes.get(i) == 1) {
g.fillRect(p.x, p.y, 60, 20);
g.setColor(Color.GREEN);
repaint();
} else if (shapeTypes.get(i) == 2) {
g.fillOval(p.x, p.y, 20, 20);
g.setColor(Color.RED);
repaint();
}
}
}
A better design would be to create a separate class with the capability to store the location as well as the shape type with the implementation on how it should be painted.
I am making a GUI with Swing that uses an AffineTransform to scale Graphics2D objects painted on a JInternalFrame. The problem is that it is buggy in the current state and I can't figure out why.
Why isn't my code scaling properly? Why do the graphics "jump" to the top of the panel on a resize?
Here is my self contained example:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.util.*;
public class MainPanel extends JFrame implements ActionListener{
private static final double version = 1.0;
private JDesktopPane desktop;
public static RFInternalFrame frame;
private java.util.List<Point> POINT_LIST = Arrays.asList(
//Top Row
new Point(50, 30),
new Point(70, 30),
new Point(90, 30),
new Point(110, 30),
new Point(130, 30),
new Point(150, 30),
new Point(170, 30),
new Point(190, 30),
new Point(210, 30),
new Point(230, 30),
//Circle of Radios
new Point(140, 60),
new Point(120, 80),
new Point(100, 100),
new Point(100, 120),
new Point(120, 140),
new Point(140, 160),
new Point(160, 140),
new Point(180, 120),
new Point(180, 100),
new Point(160, 80));
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
JFrame frame = new MainPanel();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationByPlatform(false);
frame.setVisible(true);
}
public MainPanel() {
super("MainPanel " + version);
//Make the big window be indented 50 pixels from each edge
//of the screen.
int inset = 50;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(inset, inset,
screenSize.width - inset * 7,
screenSize.height - inset * 4);
//Set up the GUI.
desktop = new JDesktopPane(); //a specialized layered pane
desktop.setBackground(Color.DARK_GRAY);
createRFFrame(); //create first RFFrame
createScenarioFrame(); //create ScenarioFrame
setContentPane(desktop);
setJMenuBar(createMenuBar());
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
}
protected JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
//Set up the lone menu.
JMenu menu = new JMenu("File");
menu.setMnemonic(KeyEvent.VK_D);
menuBar.add(menu);
//Set up the first menu item.
JMenuItem menuItem = new JMenuItem("Add Panel");
menuItem.setMnemonic(KeyEvent.VK_N);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_N, ActionEvent.ALT_MASK));
menuItem.setActionCommand("new");
menuItem.addActionListener(this);
menu.add(menuItem);
//Set up the second menu item.
menuItem = new JMenuItem("Quit");
menuItem.setMnemonic(KeyEvent.VK_Q);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_Q, ActionEvent.ALT_MASK));
menuItem.setActionCommand("quit");
menuItem.addActionListener(this);
menu.add(menuItem);
return menuBar;
}
//React to menu selections.
public void actionPerformed(ActionEvent e) {
if ("new".equals(e.getActionCommand())) { //new
createRFFrame();
} else {
//quit
quit();
}
}
/*
* ActivateAllAction activates all radios on the panel, essentially changes the color
* of each ellipse from INACTIVE to ACTIVE
*/
private class ActivateAllAction extends AbstractAction {
public ActivateAllAction(String name) {
super(name);
int mnemonic = (int) name.charAt(1);
putValue(MNEMONIC_KEY, mnemonic);
}
/*
* This will find the selected tab and extract the DrawEllipses instance from it
* Then for the actionPerformed it will call activateAll() from DrawEllipses
*/
#Override
public void actionPerformed(ActionEvent e) {
Component comp = desktop.getSelectedFrame();
if (comp instanceof DrawEllipses){
DrawEllipses desktop = (DrawEllipses) comp;
desktop.activateAll();
}
}
}
/*
* DeactivateAllAction deactivates all radios on the panel, essentially changes the color
* of each ellipse from ACTIVE to INACTIVE
*/
private class DeactivateAllAction extends AbstractAction {
public DeactivateAllAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
/*
* This will find the selected tab and extract the DrawPanel2 instance from it
* Then for the actionPerformed it will call activateAll() from DrawEllipses
*/
#Override
public void actionPerformed(ActionEvent e) {
Component comp = desktop.getSelectedFrame();
if (comp instanceof DrawEllipses){
DrawEllipses desktop = (DrawEllipses) comp;
desktop.deactivateAll();
}
}
}
/*
* Define a JPanel that will hold the activate and deactivate all JButtons
*/
protected JPanel btnPanel() {
JPanel btnPanel = new JPanel();
btnPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
//Set the layout of the frame to a grid bag layout
btnPanel.setLayout(new GridBagLayout());
//Creates constraints variable to hold values to be applied to each aspect of the layout
GridBagConstraints c = new GridBagConstraints();
//Column 1
c.gridx = 0;
btnPanel.add(new JButton(new ActivateAllAction("Activate All")));
//Column 2
c.gridx = 1;
btnPanel.add(new JButton(new DeactivateAllAction("Deactivate All")));
return btnPanel;
}
//not used currently
protected JPanel drawPanel() {
JPanel drawPanel = new JPanel();
drawPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
drawPanel.add(drawEllipses);
return drawPanel;
}
//Create a new internal frame.
protected void createRFFrame() {
RFInternalFrame iframe = new RFInternalFrame();
iframe.setLayout(new BorderLayout());
DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
iframe.add(drawEllipses);
iframe.add(btnPanel(), BorderLayout.SOUTH);
iframe.setVisible(true);
desktop.add(iframe);
try {
iframe.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
}
protected void createScenarioFrame() {
ScenarioInternalFrame frame = new ScenarioInternalFrame();
frame.setLayout(new BorderLayout());
frame.setVisible(true);
desktop.add(frame);
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
}
//Quit the application.
protected void quit() {
System.exit(0);
}
}
#SuppressWarnings("serial")
class DrawEllipses extends JPanel {
private double translateX; //
private double translateY; //
protected static double scale; //
private static final int OVAL_WIDTH = 15;
private static final Color INACTIVE_COLOR = Color.RED;
private static final Color ACTIVE_COLOR = Color.green;
private java.util.List<Point> points; //
private java.util.List<Ellipse2D> ellipses = new ArrayList<>();
private Map<Ellipse2D, Color> ellipseColorMap = new HashMap<>();
public DrawEllipses(java.util.List<Point> points) {
this.points = points; //
translateX = 0; //
translateY = 0; //
scale = 1; //
setOpaque(true); //
setDoubleBuffered(true); //
for (Point p : points) {
int x = p.x - OVAL_WIDTH / 2;
int y = p.y - OVAL_WIDTH / 2;
int w = OVAL_WIDTH;
int h = OVAL_WIDTH;
Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
ellipses.add(ellipse);
ellipseColorMap.put(ellipse, INACTIVE_COLOR);
}
MyMouseAdapter mListener = new MyMouseAdapter();
addMouseListener(mListener);
addMouseMotionListener(mListener);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
AffineTransform tx = new AffineTransform(); //
tx.translate(translateX, translateY); //
tx.scale(scale, scale); //
Graphics2D g2 = (Graphics2D) g;
g2.setTransform(tx);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ellipse2D ellipse : ellipses) {
g2.setColor(ellipseColorMap.get(ellipse));
g2.fill(ellipse);
}
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
for (Ellipse2D ellipse : ellipses) {
if (ellipse.contains(e.getPoint())) {
Color c = ellipseColorMap.get(ellipse);
c = (c == INACTIVE_COLOR) ? ACTIVE_COLOR : INACTIVE_COLOR;
ellipseColorMap.put(ellipse, c);
}
}
repaint();
}
}
//Used for button click action to change all ellipses to ACTIVE_COLOR
public void activateAll(){
for (Ellipse2D ellipse : ellipses){
ellipseColorMap.put(ellipse, ACTIVE_COLOR);
}
repaint();
}
//Used for button click action to change all ellipses to INACTIVE_COLOR
public void deactivateAll(){
for (Ellipse2D ellipse : ellipses){
ellipseColorMap.put(ellipse, INACTIVE_COLOR);
}
repaint();
}
}
class RFInternalFrame extends JInternalFrame implements ComponentListener {
protected static double scale = 1; //
static int openFrameCount = 0;
static final int xOffset = 300, yOffset = 0;
public RFInternalFrame() {
super("RF Panel #" + (++openFrameCount),
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable
setSize(300, 300);
setMinimumSize(new Dimension(300, 300));
addComponentListener(this);
if (openFrameCount == 1) {
setLocation(0,0);
}
else if (openFrameCount <= 4) {
//Set the window's location.
setLocation(xOffset * (openFrameCount - 1), yOffset * (openFrameCount - 1));
}
else if (openFrameCount == 5) {
setLocation(xOffset - 300, yOffset + 300);
}
else if (openFrameCount == 6) {
setLocation(xOffset + 600, yOffset + 300);
}
}
#Override
public void componentResized(ComponentEvent e) {
String str = "";
if (getWidth() < 300) {
str = "0." + getWidth();
} else {
str = "1." + (getWidth() - 300);
System.out.println(getWidth() - 300);
}
double dou = Double.parseDouble(str);
MainPanel.frame.scale = dou;
repaint();
}
#Override
public void componentMoved(ComponentEvent componentEvent) {
}
#Override
public void componentShown(ComponentEvent componentEvent) {
}
#Override
public void componentHidden(ComponentEvent componentEvent) {
}
}
class ScenarioInternalFrame extends JInternalFrame {
static int openFrameCount = 0;
static final int xOffset = 300, yOffset = 300;
public ScenarioInternalFrame() {
super("Test Scenario" + (++openFrameCount),
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable
//...Create the GUI and put it in the window...
//...Then set the window size or call pack...
setSize(600, 300);
//Set the window's location.
setLocation(xOffset, yOffset);
}
}
As I understand it, the Graphics object already contains a transform that does a translate to account for the height of the title bar of the internal frame. When you replace the transform you lose this translation so your code is painted at the top of the frame under the title bar.
Don't change properties of the Graphics object passed to the paintComponent() method. Instead create a Graphics2D object you can customize.
When you create a new transform you need to apply the existing transform first before adding new transforms.
The basic structure would be something like:
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g.create();
AffineTransform tx = new AffineTransform(); //
tx.concatenate( g2.getTransform() );
tx.scale(...);
g2.setTransform(tx);
// do custom painting
g2.dispose(); // release Graphics resources
This will just help the painting. You still have several problems (which I can't solve):
Your scale value is never getting updated. You should be adding the ComponentListener to the DrawEllipse panel. You might want to create a setScale() method in the panel that you invoked to set the scale when the panel is resized.
Once you do paint the circles scaled, you MouseListener won't work. The location of all the circles will be different because they have been scaled. You might be able to scale each circle as you iterate through the list of circles.
Also, when you have a question post a proper SSCCE that demonstrates the problem. You have a simple question about using a transform on a panel. So create a frame with a panel and paint a couple of circles on the panel to test the concept.
All the other code is irrelevant to the problem. The menu items are irrelevant, the second internal frame is irrelevant. The MouseListener clicking code is irrelevant. We don't have time to read through 100's of lines of code to understand the question.
Edit:
I changed the order of the code. The tx.scale(...) method must be invoked before setting the transform to the Graphics object.
I my experience, painting on Swing will be done with double buffer. Means that you create the drawing buffer (ie. ImageBuffer). you apply all your drawing logic to the Graphics of the drawing buffer, including transformation, and then finally, draw your buffer into the component's graphics.
This is how I solve your problem...
class DrawEllipses extends JComponent { // I change from JPanel to JComponent, this might not be necessary though...
...
...
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// create the drawing buffer.
BufferedImage bi = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics big = bi.getGraphics();
// prepare transform
AffineTransform tx = new AffineTransform(); //
tx.translate(translateX, translateY); //
tx.scale(scale, scale); //
// get the buffer graphics and paint the background white.
Graphics2D g2 = (Graphics2D) big;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, this.getWidth(), this.getHeight());
// apply drawing logic to the Graphics of the buffer
g2.setTransform(tx);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ellipse2D ellipse : ellipses) {
g2.setColor(ellipseColorMap.get(ellipse));
g2.fill(ellipse);
}
// finally, draw the buffer to the component graphics.
g.drawImage(bi, 0, 0, null);
}
Try it... hope it works and helps.