I'm trying to make a simple application for painting. I have a main method that sets up a JFrame, and then adds a JPanel and a JLabel using flowlayout. The JLabel is for counting clicks.
The panel class implements a mouselistener and mousemotionlistener.
The problem is when I paint something or click on the panel, it adds the label to the JPanel aswell, and depending on where I click, it can add it twice to the panel, it's making me mad. I just can't understand why it's added to the JPanel.
Also, the JPanel is surrounded by a border, and when I click or paint something it adds a new vertical borderline somewhere on the panel, it's random every time.
Code for the two classes:
public class mainepanel extends JPanel implements MouseMotionListener, MouseListener{
Graphics globalGraphics;
int clickCount = 0;
public mainepanel(){
setBorder(BorderFactory.createLineBorder(Color.black));
setPreferredSize(new DimensionUIResource(200,200));
addMouseMotionListener(this);
addMouseListener(this);
validate();
setFocusable(true);
}
public void paintComponent(Graphics g){
globalGraphics = g.create();
}
#Override
public void mouseDragged(MouseEvent e) {
globalGraphics.fillOval(e.getX(), e.getY(), 10,10);
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
clickCount ++;
maine.setLabel(clickCount);
globalGraphics.fillOval(e.getX(), e.getY(), 10,10);
repaint();
}
maine
public class maine extends JFrame{
static JLabel label;
public maine(){
setSize(600,400);
setDefaultCloseOperation(3);
setResizable(false);
label = new JLabel("Clicks:");
setLayout(new FlowLayout());
add(label);
add(new mainepanel());
setVisible(true);
}
public static void setLabel(int clicks){
label.setText("Clicks: " + clicks);
}
public static void main(String[]args){
new maine();
}
}
Perform all the drawing within paintComponent (and be sure to call super.paintComponent) - the MouseListener/MouseMotionListener should only need to change the data model (and if necessary call repaint so the UI reflects the change).
A simple example below with a single circle created with a Mouse click, and moved with a mouse dragged:
Point center = null;
...
#Override
public void mouseClicked(MouseEvent e){
center = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e){
center = e.getPoint();
repaint();
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if ( center != null ){
g.fillOval(center.getX(), center.getY(), 10, 10);
}
}
Related
I am trying to add the MouseHandler to my DrawPanel class to eventually have a status label that updates the mouse location, but while using print statements, it seems like it is not registering any mouse input at all.
private class DrawPanel extends JPanel {
public DrawPanel() {
JPanel mousePanel = new JPanel();
this.add(mousePanel);
MouseHandler handler = new MouseHandler();
mousePanel.addMouseListener(handler);
mousePanel.addMouseMotionListener(handler);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
}
private class MouseHandler extends MouseAdapter implements MouseMotionListener {
#Override
public void mousePressed(MouseEvent event) {
System.out.print("Pressed");
}
#Override
public void mouseReleased(MouseEvent event) {
System.out.print("Released");
}
#Override
public void mouseDragged(MouseEvent event) {
System.out.print("Dragged");
//lblStatus.setText(String.format("(%d,%d)",event.getX(),event.getY()));
}
#Override
public void mouseMoved(MouseEvent event) {
System.out.print("Moved");
//System.out.print("("+event.getX()+","+event.getY()+")");
//lblStatus.setText(String.format("(%d,%d)",event.getX(),event.getY()));
}
}
}
You're creating and adding another JPanel, the mousePanel, and adding it to the DrawPanel JPanel, a container that uses the default FlowLayout. This makes the mousePanel's size its preferred size which is [0, 0] meaning that the mousePanel component is being added but it is too small to be seen or to do anything significant. But why do you even have or need this extra JPanel?
Solution: get rid of the mousePanel, there is no need for it. Instead add your mouse handler to this.
Side issue, no need to implement MouseMotionListener. A MouseAdapter already implements this interface.
For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FooSwing02 extends JPanel {
private JLabel statusLabel = new JLabel("");
public FooSwing02() {
setPreferredSize(new Dimension(800, 650));
add(new JLabel("Mouse Location:"));
add(statusLabel);
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
private class MyMouse extends MouseAdapter {
#Override
public void mouseMoved(MouseEvent event) {
Point p = event.getPoint();
String text = String.format("[%03d, %03d]", p.x, p.y);
statusLabel.setText(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.add(new FooSwing02());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
I am trying to make a drawOval moving by using the two buttons that I set to be North and East so the ball will move between the JButtons, at the center.
Why does not appear at the panel?
Also I am thinking using a function that make this x=x+; and y=y+1 when I pressed left or right.
I do not figure out what can I do.
So this is the code I made:
public class Main extends JFrame implements ActionListener {
JButton left;
JButton right;
JPanel p;
Main(){
JButton left = new JButton("left");
left.addActionListener(this);
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
//The first way I think is better to make it move.
}
});
JButton right = new JButton("right");
right.addActionListener(this);
Panel p = new Panel();
p.setLayout(new BorderLayout());
p.add("West",left);// to the left
p.add("East",right);//to the right
Container c = getContentPane();
c.add(p);
}
public static void main(String[] args) {
Main f=new Main();
f.setTitle("Heracles");
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true); //this is the window
}
public void paintComponent (Graphics g) {
super.paintComponents(g);
Graphics2D g1=(Graphics2D) g;
g.drawOval(3, 5, 45, 46); // The ball
g.fillOval(20, 30, 40, 40);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
To understand why it's not working, you need to understand how the paint system actually works
Just by looking at this snippet it should be obvious something is wrong.
public class Main extends JFrame implements ActionListener {
//...
public void paintComponent (Graphics g) {
super.paintComponents(g);
//...
}
}
You've declare a method called paintComponent but are calling the super method paintComponents (note the s at the end).
Further, when ever you "think" you're overriding a method, you should make use of the #Override attribute, this will cause a compiler error when you've done something wrong
public class Main extends JFrame implements ActionListener {
//...
#Overrride
public void paintComponent (Graphics g) {
super.paintComponents(g);
//...
}
}
The above code will now fail to compile, as JFrame doesn't declare a paintComponent method.
As a general rule, you should avoid extending directly from JFrame (or other top level containers), they are compound components and have a complex hierarchy and functionality.
A better place to start might be with a JPanel
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
JButton left;
JButton right;
JPanel paintPane;
public TestPane() {
JButton left = new JButton("left");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
}
});
JButton right = new JButton("right");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
paintPane = new PaintPane();
setLayout(new BorderLayout());
add(left, BorderLayout.WEST);
add(right, BorderLayout.EAST);
add(paintPane);
}
}
public class PaintPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g1 = (Graphics2D) g;
g1.drawOval(3, 5, 45, 46); // The ball
g1.fillOval(20, 30, 40, 40);
}
}
}
You should take the time to have a look at Painting in Swing and Performing Custom Painting for more details.
Some other concepts you might like to take the time to learn:
Single Responsibility Principle - a class should do one thing and do it well
Observer Pattern - This typically represent in Swing as the listener API
Model-View-Controller - this encompasses the above and defines different layers of responsibility for different parts of the program, it will helper you understand the basic structure of Swing as well
Also I am thinking using a function that make this x=x+; and y=y+1 when I pressed left or right.
Ok, so this is where the "model" part of the MVC will play it's part.
So lets start by defining the basic properties we expect the model to support...
public interface ShapeModel {
public Point getPoint();
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
Here is supports a Point to act as the location and a ChangeListener to act as the observer pattern, which will notify interested parties that the state of the model has changed.
Why start with a interface? As a general concept, you should always prefer to code to interface instead of implementation. In this case, one aspect of the interface which hasn't been defined is, how does the Point get updated? That's of little interest to most parties who want to work with the model, they just want to know when it changes, the mutation of the model can be expressed either directly via the implementation or a "mutable" interface which extends from the this interface
Next, we define a default implementation...
public class DefaultShapeModel implements ShapeModel {
private Point point = new Point(40, 40);
private List<ChangeListener> listeners = new ArrayList<>(25);
#Override
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
fireStateChanged();
}
protected void fireStateChanged() {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
}
This does define how the paint is to be updated.
Finally, we update the TestPane and PaintPane to support the model...
public class TestPane extends JPanel {
JButton left;
JButton right;
JPanel paintPane;
private DefaultShapeModel model;
public TestPane() {
model = new DefaultShapeModel();
JButton left = new JButton("left");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
Point p = model.getPoint();
p.x--;
if (p.x > 0) {
p.x = 0;
}
model.setPoint(p);
}
});
JButton right = new JButton("right");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point p = model.getPoint();
p.x++;
if (p.x + 40 > paintPane.getWidth()) {
p.x = paintPane.getWidth() - 40;
}
model.setPoint(p);
}
});
paintPane = new PaintPane(model);
setLayout(new BorderLayout());
add(left, BorderLayout.WEST);
add(right, BorderLayout.EAST);
add(paintPane);
}
}
public class PaintPane extends JPanel {
private ShapeModel model;
public PaintPane(ShapeModel model) {
this.model = model;
this.model.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
});
}
public ShapeModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g1 = (Graphics2D) g;
Point p = getModel().getPoint();
g1.fillOval(p.x, p.y, 40, 40);
g1.setColor(Color.WHITE);
g1.drawOval(p.x, p.y, 40, 40);
}
}
Why does not appear at the panel?
To display graphic you created, use follow these steps,
Remove paintComponent method and replace it with below code..
public JComponent createOvel() {
return new JComponent() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g1 = (Graphics2D) g;
g.drawOval(3, 5, 45, 46); // The ball
g.fillOval(20, 30, 40, 40);
}
};
}
Then call it in Main() constructor,
p.add("Center", createOvel());
This will display the graphic you created.
I am trying to add a button which will when pressed, clear the contents off the JPanel and return the panel back to its original set-up. How would i go about doing this? I have tried to revalidate, removeall etc but none have worked for me so far. Any suggestions on how i can do this? I will attach the code below, Help would be greatly appreciated.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class WindowBlind extends JFrame
implements ChangeListener, ActionListener {
private JSlider slider;
private int sliderValue = 0;
private JPanel panel;
private JButton open;
private JButton close;
private JButton exit;
private boolean clear;
public static void main(String[] args) {
WindowBlind applic = new WindowBlind();
applic.setLocation(100,100);
applic.setVisible(true);
} // main
public WindowBlind() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("WindowBlind");
setSize(300,300);
Container window = getContentPane();
window.setLayout(new FlowLayout());
panel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScreen(g);
} // paintComponent
};
panel.setPreferredSize(new Dimension(200, 200));
panel.setBackground(Color.white);
window.add(panel);
slider = new JSlider(JSlider.VERTICAL,0,100,0);
slider.setInverted(true); // 0 will be at top, not bottom, of vertical slider
window.add(slider);
slider.addChangeListener(this); // Register for slider events
JButton open = new JButton("Open Slider");
window.add(open);
open.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
JButton close = new JButton("Close Slider");
window.add(close);
close.addActionListener(this);
JButton exit = new JButton("Exit Slider");
window.add(exit);
exit.addActionListener(this);
} // WindowBlind constructor
public void paintScreen(Graphics g) {
g.setColor(Color.cyan);
g.fillRect(70, 40, 60, 100); // The blue sky
g.setColor(Color.lightGray);
g.fillRect(70, 40, 60, sliderValue); // The blind, partially closed
g.setColor(Color.black);
g.drawRect(70, 40, 60, 100); // The window frame
} // paintScreen
// When the slider is adjusted, this method is called automatically
public void stateChanged(ChangeEvent e) {
sliderValue = slider.getValue(); // Fetch the slider's current setting
repaint(); // Force a screen refresh (paintComponent is called indirectly)
} // stateChanged
#Override
public void actionPerformed(ActionEvent e) {
}
}
I'm taking a HUGE guess and assuming you want to reset the slider to it's "default" state, which would suggest that you need to change the sliderValue, something like...
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sliderValue = 0;
slider.repaint();
}
});
A better solution would be to generate a self contained class which encapsulated all this functionality, for example...
public class SliderPane extends JPanel {
private double sliderValue;
public double getSliderValue() {
return sliderValue;
}
public void setSliderValue(double sliderValue) {
this.sliderValue = Math.max(Math.min(1.0, sliderValue), 0);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
g.setColor(Color.cyan);
g.fillRect(0, 0, width, height); // The blue sky
g.setColor(Color.lightGray);
g.fillRect(0, 0, width, (int)(sliderValue * height)); // The blind, partially closed
g.setColor(Color.black);
g.drawRect(0, 0, width, height); // The window frame
}
}
Then you could control the slider value through the setSliderValue method.
This also allows you to specify the slider value as percentage, meaning that the size of the component doesn't matter as the area filled is a percentage of the height
This is because you always call paintScreen from the panel's paintComponent method. I would suggest this midification:
panel = new JPanel() {
boolean drawMe = true;
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(drawMe)
paintScreen(g);
} // paintComponent
};
Whenever you want to clear the panel, do this:
panel.drawMe=false;
panel.invalidate();
so I'm trying to draw a rectangle when the mouse is clicked by creating an object an adding it to the JFrame. But it won't show up once the command is run. Any ideas why?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Gui3 extends JFrame {
private JPanel mousepanel;
private JLabel statusbar;
public Gui3(){
super("The title");
mousepanel = new JPanel();
mousepanel.setBackground(Color.WHITE);
add(mousepanel, BorderLayout.CENTER);
statusbar = new JLabel("Default");
add(statusbar, BorderLayout.SOUTH);
HandlerClass handler = new HandlerClass();
mousepanel.addMouseListener(handler);
mousepanel.addMouseMotionListener(handler);
}
private class HandlerClass implements MouseListener, MouseMotionListener
{
This is where the problem is arising. The program and all its methods work, it's just drawing the rectangle that is the problem. The Object that draws the shape is below.
public void mouseClicked(MouseEvent event) {
statusbar.setText(String.format("Clicked at %d,%d",event.getX(),event.getY()));
DrawShapes shapes = new DrawShapes();
add(shapes);
}
public void mousePressed(MouseEvent event){
statusbar.setText("You pressed down the mouse");
}
public void mouseReleased(MouseEvent event){
statusbar.setText("You released the button");
}
public void mouseEntered(MouseEvent event){
statusbar.setText("You entered the area");
mousepanel.setBackground(Color.RED);
}
public void mouseExited(MouseEvent event){
statusbar.setText("The mouse has left the window");
mousepanel.setBackground(Color.WHITE);
}
//These are mouse motion events
public void mouseDragged(MouseEvent event){
statusbar.setText("You are dragging the mouse");
}
public void mouseMoved(MouseEvent event){
statusbar.setText("You are moving the mouse");
}
}
}
Here is the object that draws the rectangle
import java.awt.*;
import javax.swing.*;
public class DrawShapes extends JPanel {
public void PaintComponent(Graphics g){
g.setColor(Color.BLUE);
g.fillRect(0,0,30,30);
}
}
regarding
public void PaintComponent(Graphics g){
g.setColor(Color.BLUE);
g.fillRect(0,0,30,30);
}
Understand that
PaintComponent != paintComponent
Be sure to use the #Override annotation to let you know when you are or aren't overriding methods that you think you are.
The correct method would look something like:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // don't forget this!
g.setColor(Color.BLUE);
g.fillRect(0, 0, 30, 30);
}
Also, if you want to replace the original JPanel, then use a CardLayout to help you easily do this. Otherwise you must be sure to call revalidate() and repaint() yourself after swapping components in a container.
e.g.,
#Override
public void mouseClicked(MouseEvent event) {
statusbar.setText(String.format("Clicked at %d,%d", event.getX(), event.getY()));
remove(mousepanel);
DrawShapes shapes = new DrawShapes();
getContentPane().add(shapes, BorderLayout.CENTER);
getContentPane().revalidate();
getContentPane().repaint();
}
I am making a custom JFrame. I already have this layout, and it works completely fine:
The frame is undecorated, but I want to be able to move it around. I want my custom panel to be the moving grip for this, so what I did was add a MouseMotionListener to it. The mouseDragged function looks like this:
#Override
public void mouseDragged(MouseEvent e) {
parent.setBounds(e.getX(), e.getY(), parent.getWidth(), parent.getHeight());
}
The parent field is set in the constructor and is final.
When I try to drag the frame with the panel, it works, but not quite right. The frame constantly flickers between two positions on the screen. I am able to move the frame, but it looks horrible. When I don't drag the frame, it doesn't flicker. The two positions are relative to each other, so if you move the frame, the other one moves along (but doesn't stay at the same distance from the other). Another problem is that the frame doesn't move well with the mouse. So, if you move the frame like 100 pixels in the x direction, the frame moves less pixels in the same direction.
How can you make a moving grip for a JFrame without this happening (and what is actually causing it to do this)?
If more code is required, just tell me.
You need to provide a mousePressed also to get the initial point of the click. Then use that point to do some calculations.
Try something like this, where pX and pY are class fields (and assuming listeners are added in the panel constructor
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
// Get x,y and store them
pX = me.getX();
pY = me.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent me) {
parent.setLocation(parent.getLocation().x + me.getX() - pX,
parent.getLocation().y + me.getY() - pY);
}
});
Here's a full example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class UndecoratedExample {
private JFrame frame = new JFrame();
class MainPanel extends JPanel {
public MainPanel() {
setBackground(Color.gray);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
class BorderPanel extends JPanel {
private JLabel label;
int pX, pY;
public BorderPanel() {
label = new JLabel(" X ");
label.setOpaque(true);
label.setBackground(Color.RED);
label.setForeground(Color.WHITE);
setBackground(Color.black);
setLayout(new FlowLayout(FlowLayout.RIGHT));
add(label);
label.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
System.exit(0);
}
});
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
// Get x,y and store them
pX = me.getX();
pY = me.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent me) {
frame.setLocation(frame.getLocation().x + me.getX() - pX,
frame.getLocation().y + me.getY() - pY);
}
});
}
}
class OutsidePanel extends JPanel {
public OutsidePanel() {
setLayout(new BorderLayout());
add(new MainPanel(), BorderLayout.CENTER);
add(new BorderPanel(), BorderLayout.PAGE_START);
setBorder(new LineBorder(Color.BLACK, 5));
}
}
private void createAnsShowGui() {
frame.setUndecorated(true);
frame.add(new OutsidePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new UndecoratedExample().createAnsShowGui();
}
});
}
}
I want my custom panel to be the moving grip for this,
Check out Moving Windows which contains a class that will allow you to drag a window around the screen or any component around its parent container.