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.
Related
So I have a button listener that is external in scope to the function setPaintFlag() and I need to call it, but java is giving me runtime errors. In addition my drawing panel is not showing up. Can anyone spot my mistake? the purpose of the program is to draw either a circle or a square in a random position with a random size and a color. The program compiles and the window will show up, only the buttons are visible but they do not fire. the error visible when i hover over the setPaintFlag() is The method setPaintFlag(int) is undefined for the type PaintLab.buttonListener
A more verbose error message after a button press is
Exception in thread "AWT-EventQueue-0" java.lang.Error: Unresolved compilation problems:
The method setPaintFlag(int) is undefined for the type PaintLab.ButtonListener
The method setPaintFlag(int) is undefined for the type PaintLab.ButtonListener
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PaintLab extends JPanel {
private JButton circle, square;
private JPanel drawingPanel;
public PaintLab() {
circle = new JButton("Draw Circle");
circle.addActionListener(new ButtonListener());
square = new JButton("Draw Square");
square.addActionListener(new ButtonListener());
JPanel buttonPanel = new JPanel();
JPanel drawingPanel = new DrawingPanel(400, 400);
add(drawingPanel);
BoxLayout bX = new BoxLayout(buttonPanel, BoxLayout.X_AXIS);
buttonPanel.setLayout(bX);
buttonPanel.add(circle);
buttonPanel.add(square);
add(buttonPanel);
}
private class DrawingPanel extends JPanel { // outer
private boolean paintFlag = false;
private int theMode = -1;
private int width, height;
private DrawingPanel(int width, int height) { // inner
this.width = width;
this.height = height;
}
public void setPaintFlag(int current) { // inner
paintFlag = true;
theMode = current;
}
public void paintComponent(Graphics g) { // inner
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int r = (int) (Math.random() * 255) + 1;
int gn = (int) (Math.random() * 255) + 1;
int b = (int) (Math.random() * 255) + 1;
g2.setColor(new Color(r, gn, b));
int w = (int) (Math.random() * width) + 1;
int h = (int) (Math.random() * height) + 1;
int x = (int) (Math.random() * width) + 1;
int y = (int) (Math.random() * height) + 1;
if (theMode == 0) {
g2.drawOval(x, y, w, h);
g2.fillOval(x, y, w, h);
} else if (theMode == 1) {
g2.drawRect(x, y, w, h);
g2.fillRect(x, y, w, h);
}
g2.dispose();
}
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
if (event.getSource() == circle) {
setPaintFlag(0);
drawingPanel.repaint();
} else if (event.getSource() == square) {
setPaintFlag(1);
drawingPanel.repaint();
}
}
}
}
And this is the main.
public class paintLabMain {
public static void main(String[] args) {
JFrame frame=new JFrame("Paint Lab");
frame.setPreferredSize(new Dimension (300,600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new PaintLab());
frame.pack();
frame.setVisible(true);
}
}
The buttons don't fire because they have no action listener:
Circle = new JButton("Draw Circle")
Circle.addActionListener(new buttonListener())
The error message is because you call setPaintFlag(int current) form ButtonListener but it is defined in DrawingPanel.
Edit:
Please see comments (The code can't compile because I removed parts of it and left a skeleton):
public class PaintLab extends JPanel {
private JButton circle, square;
private DrawingPanel drawingPanel;//declare drawingPanel as field
public PaintLab() {
circle = new JButton("Draw Circle");
circle.addActionListener(new ButtonListener());
square = new JButton("Draw Square");
square.addActionListener(new ButtonListener());
JPanel buttonPanel = new JPanel();
/*JPanel already declared as field*/
drawingPanel = new DrawingPanel(400, 400);
add(drawingPanel);
BoxLayout bX = new BoxLayout(buttonPanel, BoxLayout.X_AXIS);
buttonPanel.setLayout(bX);
buttonPanel.add(circle);
buttonPanel.add(square);
add(buttonPanel);
}
private class DrawingPanel extends JPanel { // outer
//code removed
}
public void paintComponent(Graphics g) { // inner
//code removed
}
//add setters
void setPaintFlag(boolean paintFlag) {
this.paintFlag = paintFlag;
}
void setTheMode(int theMode) {
this.theMode = theMode;
}
}
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
if (event.getSource() == circle) {
drawingPanel.setPaintFlag(true);
drawingPanel.repaint();
} else if (event.getSource() == square) {
drawingPanel.setPaintFlag(false);
drawingPanel.repaint();
}
}
}
}
I am tasked with how to paint an arrayList of shapes in java.
I feel i have most of it right, there are two methods i am confused about however
the method in ShapeChooserPanel called public void setPoints(int x, int y)
Since Xs and Ys are arrays, how would i set the x and y values?
And the other is one is the final method in ShapeChooserPanel I cannot find out how to print the Shapes in the array, It should paint the current shape at the place the mouse was clicked.
My code is below
Main class:
import javax.swing.JFrame;
public class Lab2 {
public static void main (String[] args) {
JFrame myFrame = new JFrame("Lab 2");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.add(new ShapeChooserPanel());
myFrame.pack();
myFrame.setVisible(true);
}
}
ShapeChooserPanel class
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.ArrayList;
public class ShapeChooserPanel extends JPanel {
private int currentX;
private int currentY;
private Color currentColor;
private int currentShape;
private JButton clearBtn;
private JRadioButton circle, square, triangle, box;
private DrawingPanel drawingPanel;
private JPanel controlsPanel;
//constants representing shape choice
private final int CIRCLE = 0;
private final int SQUARE = 1;
private final int TRIANGLE = 2;
private final int BOX = 3;
//constant delta used for setting distance between points
private final int DELTA = 25;
private int[] Xs;
private int[] Ys;
//store all the shapes to be painted UNCOMMENT when you have Shape.java defined
ArrayList<Shape> shapes;
public ShapeChooserPanel(){
//provide some default values paints a circle at (10,10) in blue
currentX = 10;
currentY = 10;
Xs = new int[4];//we will use all 4 points for the square, but only the first 3 for the triangle
Ys = new int[4];
setPoints(currentX,currentY);
currentShape = CIRCLE;
currentColor = Color.red;
shapes = new ArrayList<Shape>();
//instantiate the controls panel and set its layout to display everything in a single column
controlsPanel = new JPanel();
controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.Y_AXIS));
//TODO: add clear button *
// TODO: define radio buttons *
clearBtn = new JButton("Clear");
clearBtn.addActionListener(new ClearListener());
circle = new JRadioButton("Red Circle");
circle.addActionListener(new ShapeListener());
square = new JRadioButton("Cyan Square");
square.addActionListener(new ShapeListener());
triangle = new JRadioButton("Green Triangle");
triangle.addActionListener(new ShapeListener());
box = new JRadioButton("Blue Box");
box.addActionListener(new ShapeListener());
ButtonGroup group = new ButtonGroup();
group.add(clearBtn);
group.add(circle);
group.add(square);
group.add(triangle);
group.add(box);
controlsPanel.add(clearBtn);
controlsPanel.add(circle);
controlsPanel.add(square);
controlsPanel.add(triangle);
controlsPanel.add(box);
//TODO: add radio buttons to group *
//TODO add listeners to radio buttons *
//TODO: add radio buttons to controls panel *
drawingPanel = new DrawingPanel();
drawingPanel.setBorder(BorderFactory.createLineBorder(Color.black));
//TODO: set a border around the drawing panel *
drawingPanel.setPreferredSize(new Dimension(200,200));
drawingPanel.addMouseListener(new PanelListener());
add(drawingPanel);
add(controlsPanel);
setPreferredSize(new Dimension (300,400));
}//end constructor
public void setPoints(int x, int y) {
//TODO: set Xs and Ys *
Xs[0] = x;
Ys[0] = y;
}
private class ClearListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
shapes.removeAll(shapes);
drawingPanel.repaint();
}
}
private class PanelListener implements MouseListener {
public void mouseClicked(MouseEvent me) {
currentX = me.getX();
currentY = me.getY();
//TODO: find coordinates of this mouse click *
//TODO: add a new shape to the shapes list*
shapes.addAll(shapes);
setPoints(currentX, currentY);
//TODO: call setPoints with current x and y values *
drawingPanel.repaint();
}
public void mouseExited(MouseEvent me){}
public void mouseEntered(MouseEvent me){}
public void mouseReleased(MouseEvent me){}
public void mousePressed(MouseEvent me){}
}
//Class to listen for radio button changes
private class ShapeListener implements ActionListener{
public void actionPerformed(ActionEvent me){
//TODO: determine which radio button was clicked *
if(me.getSource() == circle){
currentShape = CIRCLE;
currentColor = Color.red;
}
if(me.getSource() == square){
currentShape = SQUARE;
currentColor = Color.cyan;
}
if(me.getSource() == triangle){
currentShape = TRIANGLE;
currentColor = Color.green;
}
if(me.getSource() == box){
currentShape = BOX;
currentColor = Color.blue;
}
//TODO: set current shape and color *
drawingPanel.repaint();
}
}
private class DrawingPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
//TODO: paint all the shapes in our list
}
}
}
and my Shape class
import java.awt.Color;
public class Shape {
private int x,y;
private int type;
private Color c;
public Shape(int x, int y, int type, Color c) {
this.x = x;
this.y = y;
this.type = type;
this.c = c;
}
public int x(int x) {
return x;
}
public int y(int y) {
return y;
}
public int type(int type) {
return type;
}
public Color c(Color c) {
return c;
}
}
super.paintComponent(g) invokes the paintComponent method from the superclass of JPanel (the JComponent class) to erase whatever is currently drawn on the panel. This is useful for animation, but not for setting color.
I guess you'll have to set the color of the shapes in your Arraylist<Shape> shapes when you will create them, nonetheless it could be helpfull to see the Shape class. Maybe you could create a function changeColor(Color ColorToBeSet) in this shape class and loop through the shapes ArrayList to call it at the end of your ShapeListener
You could...
Define an "abstract" concept of a shape, which has the basic properties (location and size) and which can paint itself...
public abstract class Shape {
private int x, y;
private int width, height;
private Color c;
public Shape(int x, int y, int width, int height, Color c) {
this.x = x;
this.y = y;
this.c = c;
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColor() {
return c;
}
public abstract void paint(Graphics2D g2d);
}
Then you can implement the individual shapes...
public class Rectangle extends Shape {
public Rectangle(int x, int y, int width, int height, Color c) {
super(x, y, width, height, c);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
public class Oval extends Shape {
public Oval(int x, int y, int width, int height, Color c) {
super(x, y, width, height, c);
}
#Override
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.drawOval(getX(), getY(), getWidth(), getHeight());
}
}
Then, you can simply call the paint method of each instance of Shape as required...
public class TestPane extends JPanel {
private List<Shape> shapes;
public TestPane() {
shapes = new ArrayList<>(25);
shapes.add(new Rectangle(10, 10, 20, 20, Color.RED));
shapes.add(new Oval(15, 15, 40, 20, Color.RED));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
shape.paint(g2d);
g2d.dispose();
}
}
}
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 have 4 classes:
Draw, Rectangle (extends Draw), FreeHand (extends Draw) and a test class.
I add rectangles and lines drawn by free hand to an arrayList.
I have a menubar with choices Back and Clear. Back removes the last drawn object. It is done by removing the last object in the arraylist. Clear clears the windows. It is done by clear the arraylist from all items.
Now to my problem: The window does not clear. I don't know how to write the code to make it repaint properly so that the items removes from the window.
Can you please help me with how the code for this would look like, and where I put it. I appreciate it, thank you.
My problem no 2:
After I have removed the last item in the arraylist I need to draw all the items in the arrayList. I have tried
for (Draw d : shapeList) {
d.draw(g2);
}
But it does not work. Any suggestions?
Class Draw:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public abstract class Draw extends JPanel {
public int startX, startY, endX, endY, width, height, w, h;
public String color = "Black";
public Draw(int startX, int startY, int width, int height) {
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
}
public abstract void draw(Graphics2D g);
#Override
public int getX() {
return startX;
}
public void setX(int startX) {
this.startX = startX;
}
#Override
public int getY() {
return startY;
}
public void setY(int startY) {
this.startY = startY;
}
#Override
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
#Override
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public void setColor(String color) {
this.color = color;
}
}
Class Rectangle:
import java.awt.Color;
import java.awt.Graphics2D;
public class Rectangle extends Draw {
public Rectangle(int x, int y, int width, int height) {
super(x, y, width, height);
}
#Override
public void draw(Graphics2D g2) {
switch (color) {
case "Red":
g2.setColor(Color.RED);
break;
case "Green":
g2.setColor(Color.GREEN);
break;
case "Blue":
g2.setColor(Color.BLUE);
break;
case "Yellow":
g2.setColor(Color.YELLOW);
break;
case "Orange":
g2.setColor(Color.ORANGE);
break;
case "Black":
g2.setColor(Color.BLACK);
break;
}
g2.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
Class FreeHand:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
public class FreeHand extends Draw {
public FreeHand(int x, int y, int width, int height) {
super(x, y, width, height);
}
/* public FreeHand() {
super();
}*/
#Override
public void draw(Graphics2D g2) {
//Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(3));
switch (color) {
case "Red":
g2.setColor(Color.RED);
break;
case "Green":
g2.setColor(Color.GREEN);
break;
case "Blue":
g2.setColor(Color.BLUE);
break;
case "Yellow":
g2.setColor(Color.YELLOW);
break;
case "Orange":
g2.setColor(Color.ORANGE);
break;
case "Black":
g2.setColor(Color.BLACK);
break;
}
g2.drawLine(getX(), getY(), getWidth(), getHeight());
}
}
Testclass:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class JavaApplication30 extends JFrame implements ActionListener {
public ArrayList<Draw> shapeList = new ArrayList<>();
int startX, startY, endX, endY, w, h;
private JPanel topPanel;
private JPanel bottomPanel;
private JPanel orangePanel;
private JPanel greenPanel;
private JPanel bluePanel;
private JPanel blackPanel;
private JPanel redPanel;
private JPanel yellowPanel;
private JPanel leftPanel;
private JPanel rightPanel;
private JPanel colorPanel;
private JMenuBar menuBar;
private JMenu menu;
private JMenuItem menuItem1;
private JMenuItem menuItem2;
private JMenuItem menuItem3;
private JComboBox comboBox;
private JLabel leftLabel;
private JLabel commaLabel;
private JLabel colorLabel;
private static JLabel xLabel;
private static JLabel yLabel;
private final String labelText = "Coordinates: ";
private final String comma = ",";
private final String color = "Color: ";
private final String[] boxOptions = new String[] {"Rectangle", "Freehand"};
private String pickedColor = "Black";
private String x = "";
private String y = "";
Container cp = getContentPane();
private int count = 0;
public JavaApplication30(String title) {
super(title);
this.setLayout(new BorderLayout());
this.setLocationRelativeTo(null);
this.setSize(840, 500);
this.initComponents();
this.initMenu();
this.setVisible(true);
}
private void initComponents() {
cp.setBackground(Color.WHITE);
comboBox = new JComboBox(boxOptions);
topPanel = new JPanel(new GridLayout(1,7));
bottomPanel = new JPanel(new GridLayout(1,2));
orangePanel = new JPanel();
greenPanel = new JPanel();
bluePanel= new JPanel();
blackPanel = new JPanel();
redPanel = new JPanel();
yellowPanel = new JPanel();
colorPanel = new JPanel();
rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
comboBox.setSelectedIndex(0);
comboBox.addActionListener(this);
topPanel.setPreferredSize(new Dimension(0,40));
bottomPanel.setPreferredSize(new Dimension(0,30));
colorPanel.setPreferredSize(new Dimension(60,20));
leftLabel = new JLabel(labelText);
commaLabel = new JLabel(comma);
colorLabel = new JLabel(color);
bottomPanel.setBackground(Color.LIGHT_GRAY);
orangePanel.setBackground(Color.ORANGE);
greenPanel.setBackground(Color.GREEN);
bluePanel.setBackground(Color.BLUE);
blackPanel.setBackground(Color.BLACK);
redPanel.setBackground(Color.RED);
yellowPanel.setBackground(Color.YELLOW);
colorPanel.setBackground(Color.BLACK);
topPanel.add(orangePanel);
topPanel.add(greenPanel);
topPanel.add(bluePanel);
topPanel.add(blackPanel);
topPanel.add(redPanel);
topPanel.add(yellowPanel);
topPanel.add(comboBox);
rightPanel.add(colorLabel);
rightPanel.add(colorPanel);
bottomPanel.add(leftPanel);
bottomPanel.add(rightPanel);
this.add(topPanel, BorderLayout.PAGE_START);
this.add(bottomPanel, BorderLayout.PAGE_END);
}
#Override
public void paint(Graphics g) {
if(count == 0) {
cp.repaint();
}
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1));
for (Draw d : shapeList) {
d.draw(g2);
}
if (startX != 0 && startY != 0 && endX != 0 && endY != 0) {
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
g2.setPaint(Color.WHITE);
g2.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
r.setColor(pickedColor);
r.draw(g2);
}
}
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (e.getSource().equals(menuItem1)) {
shapeList.clear();
//Code to clear window
}
if (e.getSource().equals(menuItem2)) {
shapeList.remove(shapeList.size() - 1);
//Code to clear window
Graphics g = getGraphics();
Graphics2D g2 = (Graphics2D) g;
for (Draw d : shapeList) {
d.draw(g2);
}
}
if (e.getSource().equals(menuItem3)) {
//Exit
}
if (e.getSource().equals(comboBox)) {
JComboBox cb = (JComboBox)e.getSource();
if (cb.getSelectedItem().equals("Rectangle")) {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
endX = startX;
endY = startY;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
endX = e.getX();
endY = e.getY();
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
shapeList.add(r);
r.setColor(pickedColor);
startX = 0;
startY = 0;
endX = 0;
endY = 0;
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
});
}
else if (cb.getSelectedItem().equals("Freehand")) {
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Graphics g = getGraphics();
Graphics2D g2 = (Graphics2D) g;
FreeHand fh = new FreeHand(startX, startY, e.getX(), e.getY());
shapeList.add(fh);
fh.setColor(pickedColor);
fh.draw(g2);
startX = e.getX();
startY = e.getY();
}
});
}
}
}
private void initMenu() {
menuBar = new JMenuBar();
menu = new JMenu("File");
menuBar.add(menu);
menuItem1 = new JMenuItem("Clear");
menuItem2 = new JMenuItem("Back");
menuItem3 = new JMenuItem("Exit");
menu.add(menuItem1);
menu.add(menuItem2);
menu.addSeparator();
menu.add(menuItem3);
menu.setMnemonic(KeyEvent.VK_A);
KeyStroke ks1 = KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK); //Crtl+n
KeyStroke ks2 = KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_MASK); //Ctrl+i
KeyStroke ks3 = KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK); //Ctrl+e
menuItem1.setAccelerator(ks1);
menuItem2.setAccelerator(ks2);
menuItem3.setAccelerator(ks3);
menuItem1.addActionListener(this);
menuItem2.addActionListener(this);
menuItem3.addActionListener(this);
setJMenuBar(menuBar);
}
public static void main(String args[]) {
new JavaApplication30("Draw");
}
}
Your problem looks to be that your paint method is not calling the super's paint method since this will have the component clean itself of all "dirty" image bits. But having said that, you shouldn't draw directly in the JFrame. Instead draw in the paintComponent method of a JComponent or JPanel, and in that method be sure to call the super's paintComponent method:
public class MyDrawingPanel extends JPanel {
#Override
proteced void paintComponent(Graphics g) {
super.paintComponent(g); // don't forget this!
// do your drawing here
}
}
Also, why does your Draw class, and thus all classes that derive from it, extend JPanel when it is not being used as a JPanel? You are giving a lot of unnecessary overhead to these classes this way.
Edit
You ask:
So you mean I should move Everything in the paint-method to the paintComponent-method in Draw? Why protected?
No. I mean that Draw should not extend JPanel, but instead should be a logical class, not a Swing component-derived class. I think that you should create a new class, say called MyDrawingPanel where you do all of your drawing. Please see my code snippet above. Also paintComponent is declared in JComponent to be protected, not public, and I see no advantage to making it public when overriding it, so I recommend leaving it protected.
Please read the Swing Info Links to see the Swing graphics tutorials and give them a read.
Edit 2
You're also using a getGraphics() call on a component to get your Graphics object, not good as this will return an unstable Graphics object. Instead do all drawing in the paintComponent method or on a BufferedImage (that again is drawn in paintComponent).
Edit 3
Some of my code examples:
Minimal way to make a cleanable drawing area
Changing JPanel Graphics g color drawing line
Graphics in repaint draws random lines
I am just starting to put together a logging tool for my own use that would log statistics from gym/running and the only experience I have with swing/awt is active rendering for games where you have full control over the Graphics2D object and don't rely on implementing swing components with overriden paints.
Anyway, I was hoping to create a dummy JComponent that I can add to one of my panels (this panel will display graphics, statistics etc depending on what I select from another different sidepanel with options) that does nothing else but listen for mouseevents inside the panel mentioned earlier and draws a selection rectangle on mousedrags so that I can zoom in the data if higher resolutions exist. I just don't know how, I have added the component to the panel but it registers nothing inside the panel, instead it seems to have a local space that is limited to the bottom of the panel/frame.
Here is the component
package gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
private Color color = new Color(0, 255, 0, 100);
public MarkerRectangle(int width, int height) {
setPreferredSize(new Dimension(width, height));
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse clicked# " + e.getX() + "," + e.getY());
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered # " + e.getX() + "," + e.getY());
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse left # " + e.getX() + "," + e.getY());
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed# " + e.getX() + "," + e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse was released # " + e.getX() + "," + e.getY());
}
#Override
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse being dragged, currently# " + e.getX() + ","
+ e.getY());
}
#Override
public void mouseMoved(MouseEvent e) {
System.out.println("I registered a move# " + e.getX() + "," + e.getY());
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
The panel
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
this.add(graph);
this.add(new MarkerRectangle(800, 600));
this.setBackground(Color.gray);
this.setPreferredSize(new Dimension(800, 600));
}
}
The graph, holds random data atm
public class Graph extends JComponent {
private GeneralPath data;
private Stroke stroke;
public Graph() {
this.data = new GeneralPath();
this.stroke = new BasicStroke(3f);
this.setPreferredSize(new Dimension(750, 550));
init();
}
private void init() {
data.moveTo(0, 0);
double cntr = 0;
double[][] points = new double[10][1];
for (double[] point : points) {
cntr += 100;
point[0] = Math.random() * 100;
point[1] = Math.random() * 100;
data.lineTo(cntr, point[1]);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(stroke);
g2d.draw(data);
g2d.dispose();
}
}
I want to implement something like the above because eventually I imagine the GUI to become quite complex and simple events like drawing a rectangle to mark data should not be in the main controller (so as to prevent a lot of if-tests and clutter in code).
Screenshot of what I get:
What I want:
EDIT
While the accepted answer below is the better solution I am posting this in the event that someone may want to use it. It will not work if you resize smaller the prefferedSize.
public class Test {
public static void main(String[] args) {
GeneralJFrame frame = new GeneralJFrame(1200, 800);
frame.addPanel(new GraphPane(new Graph()), BorderLayout.CENTER);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.SOUTH);
frame.addPanel(new ButtonPane(), BorderLayout.WEST);
frame.addPanel(new ButtonPane(), BorderLayout.EAST);
frame.addPanel(new ButtonPane(), BorderLayout.NORTH);
frame.start();
}
}
public class GraphPane extends JPanel {
public GraphPane(Graph graph) {
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridheight = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 0;
this.setLayout(new GridBagLayout());
this.add(graph);
this.add(new MarkerRectangle(), c);
this.setBackground(new Color(205, 201, 201));
}
}
public class MarkerRectangle extends JComponent implements MouseListener,
MouseMotionListener {
private boolean draw;
private int startX, endX, startY, endY;
public MarkerRectangle() {
this.addMouseListener(this);
this.addMouseMotionListener(this);
setOpaque(false);
setPreferredSize(new Dimension(800, 600));
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
draw = true;
}
#Override
public void mouseReleased(MouseEvent e) {
draw = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
#Override
// Draw rectangle
protected void paintComponent(Graphics g) {
System.out.println(getSize());
if (!draw)
return;
int w = endX-startX;
int h = endY - startY;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(255, 165, 0));
g2d.fillRect(startX, startY, w, h);
g2d.dispose();
}
}
public class ButtonPane extends JPanel {
public ButtonPane() {
add(new JButton("HELLO"));
setBackground(Color.gray);
setBorder(BorderFactory.createEtchedBorder(Color.white,
Color.gray.darker()));
}
}
public class GeneralJFrame {
private JFrame frame;
public GeneralJFrame(int width, int height) {
this.frame = new JFrame("Le Muscles");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void addPanel(JPanel panel, String location) {
this.frame.add(panel, location);
}
public void start() {
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Outputs: (orange is dragged with mouse)
I still don't fully understand your question. As I see it, you've
created several JPanels
Given one JPanel a MouseListener and MouseMotionListener
You've added that JPanel to the bottom of another JPanel.
The JPanel that's sitting on the bottom registers all mouse events as it has been told to do
So your program is behaving as expected based on your code.
The question I have is this: how is it not behaving as you expect it to?
If you expect that adding a JPanel with MouseListeners and MouseMotionListeners attached will result in all the JPanels of the GUI to be listened to, well of course that won't happen. To have more of the GUI register mouse events, you'll have to add MouseListeners and MouseMotionListeners to those components. And that is my answer so far to your question as I see it. If I didn't answer the true question you currently face, then please clarify it for us.
You state:
What I want is an invisible (transparent) panel on top of the blue one in the above screenshot that is just as large as the blue one, not a subpanel that is sitting in the bottom. I want the blue one to contain this one (should not be a problem since it is just a jcomponent). What I hope to achieve is a sort over "invisible" overlay that registers mousevents so I don't have to implement these events in the blue panel.
Consider using a JLayer for this. As per the JLayer API:
JLayer is a good solution if you only need to do custom painting over compound component or catch input events from its subcomponents.
OK, I've experimented with it a bit and came up with something like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Path2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
#SuppressWarnings("serial")
public class GraphPane2 extends JPanel {
private static final int GRAPH_WIDTH = 1000;
private static final int GRAPH_HEIGHT = 750;
private Graph2 graph2 = new Graph2(GRAPH_WIDTH, GRAPH_HEIGHT);
public GraphPane2() {
LayerUI<Graph2> myLayerUI = new MyLayerUI<Graph2>();
JLayer<Graph2> panelLayer = new JLayer<Graph2>(graph2, myLayerUI);
setLayout(new BorderLayout());
add(panelLayer);
myLayerUI.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (MyLayerUI.MOUSE_RELEASED.equals(evt.getPropertyName())) {
Rectangle rect = (Rectangle) evt.getNewValue();
System.out.println(rect);
}
}
});
}
private static void createAndShowGui() {
GraphPane2 mainPanel = new GraphPane2();
JFrame frame = new JFrame("Graph Pane2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyLayerUI<V extends JComponent> extends LayerUI<V> {
private static final Color FILL_COLOR = new Color(0, 128, 0, 128);
public static final String MOUSE_RELEASED = "mouse released";
private Point pressedPt;
private Point draggedPt;
private Rectangle rect;
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
if (rect != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FILL_COLOR);
g2.fill(rect);
}
}
public void installUI(JComponent c) {
super.installUI(c);
((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK);
}
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
((JLayer)c).setLayerEventMask(0);
}
#Override
public void eventDispatched(AWTEvent e, JLayer<? extends V> l) {
MouseEvent mEvt = (MouseEvent) e;
int id = mEvt.getID();
int btn = mEvt.getButton();
if (id == MouseEvent.MOUSE_PRESSED && btn == MouseEvent.BUTTON1) {
pressedPt = mEvt.getPoint();
rect = new Rectangle(pressedPt.x, pressedPt.y, 0, 0);
}
if (id == MouseEvent.MOUSE_PRESSED && btn != MouseEvent.BUTTON1) {
pressedPt = null;
}
if (id == MouseEvent.MOUSE_DRAGGED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
}
if (id == MouseEvent.MOUSE_RELEASED && pressedPt != null) {
draggedPt = mEvt.getPoint();
int x = Math.min(draggedPt.x, pressedPt.x);
int y = Math.min(draggedPt.y, pressedPt.y);
int width = Math.abs(draggedPt.x - pressedPt.x);
int height = Math.abs(draggedPt.y - pressedPt.y);
rect = new Rectangle(x, y, width, height);
firePropertyChange(MOUSE_RELEASED, null, rect);
}
l.repaint();
}
}
#SuppressWarnings("serial")
class Graph2 extends JPanel {
private static final int MAX_DATA_POINTS = 100;
private static final int STEP = 10;
private static final Stroke STROKE = new BasicStroke(3f);
private Path2D path2D;
private int width;
private int height;
private int[] data = new int[MAX_DATA_POINTS + 1];
private Random random = new Random();
public Graph2(int width, int height) {
this.width = width;
this.height = height;
init();
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
path2D = new Path2D.Double();
int w = getWidth();
int h = getHeight();
double x = 0;
double y = ((double) MAX_DATA_POINTS - data[0]) * h
/ MAX_DATA_POINTS;
path2D.moveTo(x, y);
for (int i = 1; i < data.length; i++) {
x = (i * w) / (double) MAX_DATA_POINTS;
y = ((double) MAX_DATA_POINTS - data[i]) * h
/ (double) MAX_DATA_POINTS;
path2D.lineTo(x, y);
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (path2D != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(STROKE);
g2d.draw(path2D);
}
};
private void init() {
// create and fill random data
data[0] = 0;
boolean up = true;
// max and min data values -- used for normalization
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 1; i < data.length; i++) {
up = random.nextInt(4) < 3 ? up : !up;
if (up) {
data[i] = data[i - 1] + random.nextInt(STEP);
} else {
data[i] = data[i - 1] - random.nextInt(STEP);
}
if (data[i] > max) {
max = data[i];
}
if (data[i] < min) {
min = data[i];
}
}
// normalize the data
for (int i = 0; i < data.length; i++) {
int datum = (MAX_DATA_POINTS * (data[i] - min)) / (max - min);
data[i] = datum;
}
}
}
This will look like: