This question already has answers here:
How to serialize Java 2D Shape objects as XML?
(2 answers)
Closed 8 years ago.
I have a program that lets the user add rectangles and circles to JPanel using Graphics. What I want to be able to do is save the current state of the current JPanel (i.e. all of the shapes and their locations) into a file and be able to load that file back and restore that state. I have a Shapes class that extends JPanel and does all of the drawing and keeps track of the shapes with an ArrayList.
Will I be able to just simply save the state of the panel? Or will I have to just save the Shapes data into a file and redraw the shapes when a file is "opened"?
Can anyone guide me on how I can save the current state of my JPanel and re-open it? Thanks
public class UMLEditor {
public static void main(String[] args) {
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
public UMLWindow() {
addMenus();
}
public void addMenus() {
getContentPane().add(shapeList);
JMenuBar menubar = new JMenuBar();
JMenu file = new JMenu("File");
JMenuItem openMenuItem = new JMenuItem("Open File");
openMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// Open saved state
});
JMenuItem saveMenuItem = new JMenuItem("Save");
saveMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// Save current state
}
});
file.add(openMenuItem);
file.add(saveMenuItem);
JMenu shapes = new JMenu("Shapes");
file.setMnemonic(KeyEvent.VK_F);
JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
shapeList.addSquare(100, 100);
}
});
JMenuItem circleMenuItem = new JMenuItem("New Circle");
circleMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
shapeList.addCircle(100, 100);
}
});
shapes.add(rectangleMenuItem);
shapes.add(circleMenuItem);
menubar.add(file);
menubar.add(shapes);
setJMenuBar(menubar);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
int currentIndex;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addSquare(int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
shapes.add(rect2);
repaint();
}
public void addCircle(int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Ellipse2D.Double(getWidth() / 2 - width / 2,
getHeight() / 2 - height / 2, width, height), true);
shapes.add(rect2);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Path2D rect : shapes) {
g2.draw(rect);
}
}
class MyMouseAdapter extends MouseAdapter {
private boolean pressed = false;
private Point point;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = 0; i < shapes.size(); i++) {
if (shapes.get(i) != null
&& shapes.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed) {
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
shapes.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
pressed = false;
}
}
}
You could add a line of code in addCircle and addSquare that stores their H and W (or instances of the shapes themselves) in a serialized array (which could be saved to a .dat file). Then read the file and shapes.add(tehShape) for each shape entry in the dat file and repaint when needed. Used this method on an android app when i needed a persistent storage of customized ListView items. Not pretty, but it worked like a charm once properly set up. Upside of this is that you will be saving identical object instances in the dat file, downside is that they wont be human-readable.
Related
package Dynamic_Beat;
public class DynamicBeat extends JFrame {
private Image screenImage;
private Graphics screenGraphic;
private ImageIcon startButtonBasicImage = new ImageIcon(Main.class.getResource("../images/START_Basic.png"));
private ImageIcon startButtonEnteredImage = new ImageIcon(Main.class.getResource("../images/START_Entered.png"));
private Image background = new ImageIcon(Main.class.getResource("../images/Rhythm_power.png")).getImage();
private JLabel menuBar = new JLabel(new ImageIcon(Main.class.getResource("../images/menuBar.png")));
private JButton startButton = new JButton(startButtonBasicImage);
private int mouseX, mouseY;
public DynamicBeat() {
setUndecorated(true);
setTitle("Dynamic Beat");
setSize(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setBackground(new Color(0, 0, 0, 0));
setLayout(null);
startButton.setBounds(40, 200, 400, 100);
startButton.setBorderPainted(false);
startButton.setContentAreaFilled(false);
startButton.setFocusPainted(false);
startButton.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
startButton.setIcon(startButtonEnteredImage);
startButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
#Override
public void mouseExited(MouseEvent e){
startButton.setIcon(startButtonBasicImage);
startButton.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
#Override
public void mousePressed(MouseEvent e){
Music buttonEnteredMusic = new Music("buttonPressedMusic.wav", false);
buttonEnteredMusic.start();
startButton.setVisible(false);
quitButton.setVisible(false);
}
});
add(startButton);
menuBar.setBounds(0, 0, 1280, 30);
menuBar.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
}
});
menuBar.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getXOnScreen();
int y = e.getYOnScreen();
setLocation(x - mouseX, y - mouseY);
}
});
add(menuBar);
Music introMusic = new Music("intro_music.mp3", true);
introMusic.start();
}
public void paint(Graphics g) {
screenImage = createImage(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
screenGraphic = screenImage.getGraphics();
screenDraw(screenGraphic);
g.drawImage(background, 0,0,null);
}
public void screenDraw(Graphics g) {
g.drawImage(background, 0, 0, null);
paintComponents(g);
this.repaint();
}
}
I'm using M1 mac. We are working on a mini project to make simple games at IntelliJ with java.
There was no problem with setting the size, inserting the right background picture, and adding music.
And I tried to make the game start button and click the button to move on to the main screen. But I can't see the game start button itself. However, when you put the mouse on it, the cursor changes and recognizes it well and moves on to the main screen. But I can't see the image of the button itself.
What's wrong with me?
please help.
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 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.
I am trying to add shapes onto a window using JPanel and then be able to click and drag them around the window. This works if I only have one shape; but when I add more shapes, the click and drag is very funky. It does drag but not with the mouse, it isn't proportional and doesn't drag with the mouse.
Any help is appreciated. Thanks!
public class SimpleDraw {
public static void main(String[] args) {
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
// Display the window.
frame.setVisible(true);
}
}
class UMLWindow extends JFrame {
Squares squares = new Squares();
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
}
public void addMenus() {
getContentPane().add(squares);
JMenuBar menubar = new JMenuBar();
JMenu shapes = new JMenu("Shapes");
JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
shapes.add(rectangleMenuItem);
menubar.add(shapes);
setJMenuBar(menubar);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> squares = new ArrayList<Path2D>();
// private Path2D rect = new Path2D.Double();
int currentIndex;
public void addSquare(int x, int y, int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
squares.add(rect2);
// rect = rect2;
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Path2D rect : squares) {
g2.draw(rect);
}
repaint();
}
class MyMouseAdapter extends MouseAdapter {
private boolean pressed = false;
private Point point;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = 0; i < squares.size(); i++) {
if (squares.get(i) != null
&& squares.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed) {
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
squares.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
pressed = false;
}
}
}
Lot of problems...
Most important, no you don't want to add a bunch of MouseListeners/MouseMotionListeners to your JPanel. You only want to add one, and have it control any and all squares that the JPanel holds.
Don't put a repaint() in your paintComponent method as that's a poor way to try to create an animation loop, a loop that you have absolutely no control over. Plus there's no need. The MouseAdapter should drive all the animation by itself.
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
public Squares() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
private List<Path2D> squares = new ArrayList<Path2D>();
// private Path2D rect = new Path2D.Double();
int currentIndex;
public void addSquare(int x, int y, int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
squares.add(rect2);
repaint(); // !!
// rect = rect2;
// !! MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
// addMouseListener(myMouseAdapter);
// addMouseMotionListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Path2D rect : squares) {
g2.draw(rect);
}
// !! repaint();
}
I know it's a stupid question for the Java cracks out there.
At the moment I'm writing a little paintingprogramm.
So my problem is, that i want to set the stroke size in a little dropdownmenu.
The code for the dropdown is here:
DefaultComboBoxModel model = new DefaultComboBoxModel();
model.addElement("1");
model.addElement("5");
model.addElement("10");
JComboBox comboBox = new JComboBox(model);
panel.add(comboBox);
And the Code of the stroke size is here:
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Stroke stroke = new BasicStroke(10);
graphics2D.setStroke(stroke);
graphics2D.drawLine(oldX, oldY, currentX, currentY);
repaint();
oldX = currentX;
oldY = currentY;
}
The 10 in the BasicStroke is just some random number. I wanted to realize it, that it reads from the dropdown (standart 1), saves it in a variable and the variable get used in the BasicStroke function.
Here is the whole code (Netbeans project):
package paint;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Paint {
public static void main(String[] args) {
PaintWindow frame = new PaintWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
}
class PaintWindow extends JFrame {
public PaintWindow() {
setTitle("Paint it");
setSize(700, 600);
panel = new JPanel();
panel.setPreferredSize(new Dimension(64, 64));
drawPad = new PadDraw();
//Creates a new container
Container content = this.getContentPane();
content.setLayout(new BorderLayout());
//sets the panel to the left, padDraw in the center
content.add(panel, BorderLayout.NORTH);
content.add(drawPad, BorderLayout.CENTER);
//add the color buttons:
makeColorButton(Color.BLACK);
makeColorButton(Color.BLUE);
makeColorButton(Color.MAGENTA);
makeColorButton(Color.RED);
makeColorButton(Color.ORANGE);
makeColorButton(Color.YELLOW);
makeColorButton(Color.GREEN);
makeColorButton(Color.CYAN);
DefaultComboBoxModel model = new DefaultComboBoxModel();
model.addElement("1");
model.addElement("5");
model.addElement("10");
JComboBox comboBox = new JComboBox(model);
panel.add(comboBox);
//creates the clear button
JButton clearButton = new JButton("Clear");
clearButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
drawPad.clear();
}
});
panel.add(clearButton);
panel = new JPanel();
JLabel jlabel = new JLabel("Copyright© by Jan Büttiker");
panel.setPreferredSize(new Dimension(0, 20));
panel.add(jlabel);
content.add(panel, BorderLayout.SOUTH);
}
/*
* makes a button that changes the color
* #param color the color used for the button
*/
public void makeColorButton(final Color color) {
JButton tempButton = new JButton();
tempButton.setBackground(color);
tempButton.setPreferredSize(new Dimension(56, 56));
panel.add(tempButton);
tempButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
drawPad.changeColor(color);
}
});
}
private JPanel panel;
private PadDraw drawPad;
}
class PadDraw extends JComponent {
//this is gonna be the image that you draw on
Image image;
//this is what we'll be using to draw on
Graphics2D graphics2D;
//these are gonna hold the mouse coordinates
int currentX, currentY, oldX, oldY;
public PadDraw() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
//if the mouse is pressed it sets the oldX & oldY
//coordinates as the mouses x & y coordinates
public void mousePressed(MouseEvent e) {
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
//while the mouse is dragged it sets currentX & currentY as the mouses x and y
//then it draws a line at the coordinates
//it repaints it and sets oldX and oldY as currentX and currentY
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Stroke stroke = new BasicStroke(10);
graphics2D.setStroke(stroke);
graphics2D.drawLine(oldX, oldY, currentX, currentY);
repaint();
oldX = currentX;
oldY = currentY;
}
});
}
//this is the painting bit
//if it has nothing on it then
//it creates an image the size of the window
//sets the value of Graphics as the image
//sets the rendering
//runs the clear() method
//then it draws the image
public void paintComponent(Graphics g) {
if (image == null) {
image = createImage(getSize().width, getSize().height);
graphics2D = (Graphics2D) image.getGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
clear();
}
g.drawImage(image, 0, 0, null);
}
//this is the clear
//it sets the colors as white
//then it fills the window with white
//thin it sets the color back to black
public void clear() {
graphics2D.setPaint(Color.white);
graphics2D.fillRect(0, 0, getSize().width, getSize().height);
graphics2D.setPaint(Color.black);
repaint();
}
public void changeColor(Color theColor) {
graphics2D.setPaint(theColor);
repaint();
}
}
It's my first post, so please be kind, I'm still in the learning phase ^^.
Thanks for the help and have a nice day
For that purposes you need to use ItemListener on your JComboBox. read more about ComboBox in tutorial.
1) add instance variable public float strokeSize = 10.0f; to your PadDraw as default value.
2) use Stroke stroke = new BasicStroke(strokeSize); instead of Stroke stroke = new BasicStroke(10);
3) Create your ComboBox in next way, it will change strokeSize value if you change value in JComboBox:
DefaultComboBoxModel<Float> model = new DefaultComboBoxModel<Float>();
model.addElement(1f);
model.addElement(5f);
model.addElement(10f);
final JComboBox<Float> comboBox = new JComboBox<Float>(model);
comboBox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent arg0) {
Float selectedItem = (Float) comboBox.getSelectedItem();
if(selectedItem != null){
drawPad.strokeSize = selectedItem;
}
}
});