import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Main {
public static void main(String[]args){
#SuppressWarnings("unused")
Gui g = new Gui();
}
}
#SuppressWarnings("serial")
class Gui extends JFrame{
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
double width = screen.getWidth();
double height = screen.getHeight();
JPanel canvas = new JPanel();
Point mloc = new Point();
JButton br = new JButton("Red");
JButton bb = new JButton("Blue");
JButton bg = new JButton("Green");
JButton wipe = new JButton("Wipe");
JLabel brushwidth = new JLabel("Width = ",JLabel.CENTER);
public JSlider s = new JSlider();
JButton image = new JButton("Image");
Point start = null;
Point current = null;
boolean entered = false;
Color c = Color.red;
public double bwidth = 3;
Gui(){
super("PaintPot");
setSize((int)width/4,(int)height/2);
setVisible(true);
setResizable(false);
setLayout(null);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(s);
s.setSize(getWidth()-20, 20);
s.setLocation(10, 450);
s.setBackground(Color.gray);
s.setForeground(Color.orange);
s.setMajorTickSpacing(50);
s.setMinorTickSpacing(1);
s.setValue((int)bwidth);
s.addChangeListener(new ChangeListener(){
#Override
public void stateChanged(ChangeEvent e) {
bwidth = s.getValue();
brushwidth.setText("Width = "+s.getValue());
}
});
brushwidth.setText("Width = "+s.getValue());
add(brushwidth);
brushwidth.setSize(70,30);
brushwidth.setLocation(90,410);
brushwidth.setBackground(Color.gray);
brushwidth.setForeground(Color.orange);
brushwidth.setOpaque(true);
brushwidth.setVisible(true);
add(wipe);
wipe.setSize(70,30);
wipe.setLocation(10, 410);
wipe.setBackground(Color.gray);
wipe.setForeground(Color.orange);
wipe.setVisible(true);
add(br);
br.setSize(60,30);
br.setLocation(10, 10);
br.setBackground(Color.red);
br.setForeground(Color.white);
br.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
c = Color.red;
}
});
br.setVisible(true);
add(bb);
bb.setSize(60,30);
bb.setLocation(80, 10);
bb.setBackground(Color.blue);
bb.setForeground(Color.white);
bb.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
c = Color.blue;
}
});
bb.setVisible(true);
add(bg);
bg.setSize(70,30);
bg.setLocation(150, 10);
bg.setBackground(Color.green);
bg.setForeground(Color.white);
bg.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
c = Color.green;
}
});
bg.setVisible(true);
add(image);
image.setSize(70,30);
image.setLocation(230, 10);
image.setBackground(Color.gray);
image.setForeground(Color.orange);
wipe.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
}
});
image.setVisible(true);
canvas(this,canvas);
}
public void canvas(JFrame f, JPanel p){
p.setSize(425,350);
p.setBorder(BorderFactory.createLineBorder(Color.black, 3));
p.setLocation(10, 50);
p.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent arg0) {}
#Override
public void mouseEntered(MouseEvent arg0) {entered = true;}
#Override
public void mouseExited(MouseEvent arg0) {entered = false;}
#Override
public void mousePressed(MouseEvent arg0) {}
#Override
public void mouseReleased(MouseEvent arg0) {}
});
p.addMouseMotionListener(new MouseAdapter(){
public void mouseDragged(MouseEvent e){
mloc = e.getLocationOnScreen();
if(entered = true){
paintComponent(getGraphics());
}
}
});
f.add(p);
}
public void paintComponent(Graphics g){
g.drawOval(mloc.x, mloc.y, (int)bwidth, (int)bwidth);
}
}
I'm trying to get this app to paint inside the JPanel but I can't seem to get it to work,
I want it to draw a line whenever I move my mouse. It's just the public void paint bit I can't get to grips with nothing seem to work.
Thanks
JFrame does not have a paintComponent() method.
Custom painting is done by overriding the paintComponent() method of a JPanel (or JComponent) and then you add the panel to the JFrame.
Read the section from the Swing tutorial on Custom Painting for more information and examples. You will also need to override the getPreferredSize() method.
Also, don't use a null layout. Swing was designed to be used with layout managers.
This if(entered = true){ is an assigment operator not a conditional. Instead you want if(entered == true){
paintComponent is meant to be overriden and not called explicitly. Don't explicitly call paintComponent when what you mean to do is call repaint()
JFrame has no paintComponent method, so you aren't actually overriding any paint functionality. For JFrame you should override paint, though I'd advise against it, and paint with JPanel or JComponent
In a paintComponent or paint method you should also be calling super.paintComponent or super.paint, respectively, as to not break the paint chain.
Related
I am here making a menu for my game where I can press 'Start' button to navigate through the screens. I am trying to implement a seperate button for the class however the way I have laid out the CustomButtons class, it works in a way where I can only make one button which has a function, in order to tackle this issue I decided to make a separate 'Buttons' method which contains the parameters for the button. I have called this within the paint component to ensure that it is being displayed to the screen however only the text 'START' is being displayed to the screen. The background color of the button, the borders, the font etc. isn't being changed alongside the call.
public class CustomButton extends JButton implements MouseListener {
Dimension size = new Dimension(100, 50);
boolean hover = false;
boolean click = false;
boolean isMethodCalled = false;
String text = "";
public CustomButton(String text, Button bb) {
setVisible(true);
setFocusable(true);
setContentAreaFilled(false);
setBorderPainted(false);
this.text = text;
addMouseListener(this);
}
public void Button(Graphics g) {
g.setColor(new Color(255, 255, hover ? 180 : 102 ));
g.fillRect(0, 0, 250, 7);
g.fillRect(0, 0, 7, 150);
g.setColor(Color.ORANGE); // button background color
g.fillRect(14, 14, 222, 122);
g.setColor(Color.WHITE); // text color
g.setFont(Font.decode("arial-BOLD-24"));
FontMetrics metrics = g.getFontMetrics();
int width = metrics.stringWidth(text);
g.drawString(text, 17, 40);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Button menu = new Button();
}
public void setButtonText(String text) {
this.text = text;
}
public String getButtonText(String text) {
return text;
}
public void mouseEntered(MouseEvent e) {
hover = true;
}
public void mouseExited(MouseEvent e) {
hover = false;
}
public void mousePressed(MouseEvent e) {
click = true;
}
public void mouseReleased(MouseEvent e) {
click = false;
}
public void mouseClicked(MouseEvent e) {
}
}
Anyone have any idea how I can make it so the button once it is called from the 'Buttons' method works so it is displayed exactly as it should be if all the graphics settings were to be set within the paintComponent method?
This is not what is currently happening. I do not want this to happen:
This is what I want to happen to the button:
To have the custom appearance you need, it's better that your custom button extends JLabel. Below code demonstrates how we can write a custom button by extending JLabel.
This button supports click events. We can add ActionListeners to listen to click events. It changes the background color to brown when user hovers mouse over it.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
public class CustomButton extends JLabel implements MouseListener {
private List<ActionListener> actionListeners = new ArrayList<>();
public CustomButton(String text) {
super(text);
setOpaque(true);
setForeground(Color.white);
setBackground(Color.orange);
setFont(getFont().deriveFont(40.0f));
addMouseListener(this);
}
public void addActionListener(ActionListener listener) {
actionListeners.add(listener);
}
public void removeActionListener(ActionListener listener) {
actionListeners.remove(listener);
}
#Override
public void mouseClicked(MouseEvent e) {
for (ActionListener listener : actionListeners) {
listener.actionPerformed(new ActionEvent(this, 0, "click"));
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
setBackground(new Color(185, 122, 87));
}
#Override
public void mouseExited(MouseEvent e) {
setBackground(Color.orange);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.darkGray);
frame.getContentPane().setLayout(new FlowLayout());
CustomButton customButton = new CustomButton("START");
customButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Button clicked");
}
});
frame.getContentPane().add(customButton);
frame.setBounds(300, 200, 400, 300);
frame.setVisible(true);
}
}
I'm Trying to choose a color from the JColorChooser via the MouseEventClicked and then set the color chosen to the penColor but for some reason it's not working. I think i have a problem in the MouseClicked function.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
public class Paint3<penColor> extends JFrame implements MouseMotionListener, MouseListener, ChangeListener {
int size = 2;
private Point pt = new Point();
private JSlider penSize = new JSlider(JSlider.VERTICAL,1,10,3);
JColorChooser jc = new JColorChooser();
public Color penColor = new Color(0,0,0);
public JPanel DrawingPanel = new JPanel(){
public void paint(Graphics g){
g.fillOval((int) pt.getX(), (int)pt.getY(), 5,5);
}
};
public Paint3(){
super("My Painter");
Label l1 = new Label("Drag Mouse To Draw");
JPanel jp2 = new JPanel();
this.add((DrawingPanel), BorderLayout.NORTH);
DrawingPanel.add((l1),BorderLayout.NORTH);
this.add((jp2),BorderLayout.SOUTH);
jp2.add(jc);
this.addMouseMotionListener(this);
this.addMouseListener(this);
this.add(penSize, BorderLayout.EAST);
penSize.addChangeListener((ChangeListener) this);
penSize.setMinorTickSpacing(1);
penSize.setPaintTicks(true);
setSize(700,700);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void mouseDragged(MouseEvent me){
Graphics2D g = (Graphics2D)getGraphics();
g.setStroke(new BasicStroke(size));
g.setColor(penColor);
g.drawLine(pt.x,pt.y, me.getX(), me.getY());
pt = me.getPoint();
}
#Override
public void mouseMoved(MouseEvent me){
pt = me.getPoint();
}
#Override
public void mouseClicked(MouseEvent me){
if(me.getModifiers()== MouseEvent.BUTTON3_MASK){
//penColor = jc; //(this, "Change Pen Color" , penColor);
//penColor = jc.getColor();
penColor = jc.getColor();
}
}
#Override
public void mouseEntered(MouseEvent me){}
#Override
public void mouseExited(MouseEvent me){}
#Override
public void mousePressed(MouseEvent me){}
#Override
public void mouseReleased(MouseEvent me){}
#Override
public void stateChanged(ChangeEvent me){
JSlider source = (JSlider)me.getSource();
if(!source.getValueIsAdjusting()){
size = (int)source.getValue();
}
}
public static void main(String[] args){
new Paint3();
}
}
Well, just try this code:
#Override
public void mousePressed(MouseEvent me){
penColor = jc.getColor();
}
You will modify the color in the mousePressed and not in the mouseClicked
I hope it will help you, best regards
This is not the way to do it. A MouseListener doesn't work on a javax.swing.JColorChooser the way you expect it to. Alteast not as the way you have mentioned in your post.
You need to import javax.swing.colorchooser.ColorSelectionModel and add a javax.swing.ChangeListener to it.
JColorChooser jc = new JColorChooser();
ColorSelectionModel model = jc.getSelectionModel();
model.addChangeListener(
#Override
public void stateChanged(ChangeEvent event) {
penColor = jc.getColor();
}
);
So I want to set my Rectangle's color to whatever the JColorChooser picks, but unfortunately, I don't think that it recognises it as it just stays black, unless I assign it a real color like Color.BLUE.
This is the rectangle which should get the color from the other class:
TestProjectJPanel jpp = new TestProjectJPanel();
public void paintComponent(Graphics g){
super.paintComponent(g);
Rectangle r = new Rectangle(500,300,250,400);
g.setColor(jpp.bodyColour);
g.fillRect((int)r.getX(),(int)r.getY(),(int)r.getHeight(),(int)r.getWidth());
g.setColor(Color.BLUE);
g.drawString("banana", 50, 60);
}
and this is the class which has the color itself
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestProjectJPanel extends JFrame {
public JButton b;
public JButton u;
public JButton l;
public JButton r;
public String s;
public Color color = (Color.WHITE);
public JPanel panel;
public Color bodyColour;
public Color doorColour;
public Color wheelColour;
public TestProjectJPanel(){
JFrame f = new JFrame();
panel = new JPanel();
panel.setBackground(color);
// bodyColour button
b = new JButton("Body Colour");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
bodyColour = JColorChooser.showDialog(null, "Pick the colour", bodyColour);
if (bodyColour == null)
bodyColour = Color.RED;
}
});
u = new JButton("Wheel Colour");
u.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
wheelColour = JColorChooser.showDialog(null, "Pick the colour", wheelColour);
if (wheelColour == null)
wheelColour = (Color.BLACK);
}
});
l = new JButton("Door Colour");
l.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
doorColour = JColorChooser.showDialog(null, "Pick the colour", doorColour);
if(doorColour==null)
doorColour = (Color.RED);
}
});
r = new JButton("Change Name");
r.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
s = JOptionPane.showInputDialog("What name do you want to change it to?");
}
});
}
}
This is the whole code, which the Rectangle is included in.
import javax.swing.*;
import java.util.*;
import java.awt.*;
public class TestProjectDialog extends JPanel {
TestProjectJPanel jpp = new TestProjectJPanel();
public void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle r = new Rectangle(500, 300, 250, 400);
g.setColor(jpp.bodyColour);
g.fillRect((int) r.getX(), (int) r.getY(), (int) r.getHeight(), (int) r.getWidth());
g.setColor(Color.BLUE);
g.drawString("banana", 50, 60);
}
public static void main(String[] args){
TestProjectJPanel jpp = new TestProjectJPanel();
/* JOptionPane.showMessageDialog(null, "Just about to draw a REALLY GOOD 2D car \n just need input please.");
jpp.s = JOptionPane.showInputDialog("Imagine a car, what is it's name?");
if(jpp.s == null || (jpp.s != null && ("".equals(jpp.s))))
{
JOptionPane.showMessageDialog(null, "Invalid input/pressed cancel, closing program.");
System.exit(0);
}
JOptionPane.showMessageDialog(null, "Ah okay, so it's name is " + jpp.s); */
JFrame f = new JFrame("My 2D Car Drawing");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jpp.setBackground(Color.WHITE);
f.setSize(1440,900);
f.add(new TestProjectDialog(), BorderLayout.CENTER);
f.add(jpp.b, SpringLayout.SOUTH); // bodyColour
// f.add(jpp.u, SpringLayout. NORTH); // wheelColour
// f.add(jpp.l, SpringLayout.WEST); // doorColour
// f.add(jpp.r, SpringLayout.EAST); // changeName
f.setVisible(true);
}
}
Could anyone help me and give me a solution for why it's not recognising the color?
Without a runnable example that demonstrates your problem, it is impossible to 100% sure, but from the sounds of it, you have created two more instances of TestProjectJPanel and/or paint panel, so what you think you're drawing to isn't actually on the screen.
However, it also sounds like you're not adhering to basic OO design, ensuring that the object is self managed, but instead, allowing the object to ascertain it's state from the properties of another object.
Instead, your paint/rectangle pane should be self contained, that is, all the properties it needs should be managed by it (this could also include a model, but that's going beyond the problem at hand).
For example, the ControlPane manages the interactions between the user (choosing a color) and the setting of that color on the PaintPane, which deals only with painting the results...
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class ColorTest {
public static void main(String[] args) {
new ColorTest();
}
public ColorTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
PaintPane paintPane = new PaintPane();
ControlPane controlPane = new ControlPane(paintPane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(paintPane);
frame.add(controlPane, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
public ControlPane(PaintPane paintPane) {
JButton colorButton = new JButton("Color");
add(colorButton);
colorButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Color color = JColorChooser.showDialog(ControlPane.this, "Background", paintPane.getFillColor());
if (color != null)
paintPane.setFillColor(color);
}
});
}
}
public class PaintPane extends JPanel {
private Color fillColor = Color.RED;
public PaintPane() {}
public Color getFillColor() {
return fillColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(50, 50, 100, 100);
g2d.dispose();
}
}
}
I have the following program which has some very strange and unwanted behavior when it runs. Its supposed to have two buttons, "Start" and "Stop, but when I click "Start" another button shows up right below "Start". Here's a print screen of what I'm talking about:
What am I doing wrong and how do I fix this ugly problem?
Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if(!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int)(Math.random()*256);
int blue = (int)(Math.random()*256);
int green = (int)(Math.random()*256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth()-wd);
int y = rand.nextInt(getHeight()-ht);
g.fillRect(x,y,wd,ht);
}
} // close inner class
}
Also I'm trying to get the Start button to do two things. One is to of course start the animation but when the Stop button is pressed and I press Start again, I want it to clean the screen so to speak and start the animation again a new. Any tips on that?
You do not call super.paintComponent(Graphics g) in overriden paintComponent(..) method which you should in order to honor the paint chain and thus the painting of other components.
This call should also be the first call within the method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//do painting here
}
A probem might arise that drawings are not persistent. You must than have a way to store drawings and redraw every time. The most common is an ArrayList which will hold objects to be drawn (thus you cann add to the list remove etc), you would than iterate over the list and redraw each object in paintComponent. See my answer here for an example.
Also please remember to create and manipulate Swing components on Event Dispatch Thread :
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create UI and components here
}
});
Dont call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return an appropriate height which fits all components, than call JFrame#pack() before setting JFrame visible (but after adding all components).
No need for getContentPane().add(..) as of Java 6+ add(..) defaults to contentPane
Do not re declare Random i.e Random r=new Random() each time paintComponent is called as this will make the distributions of the values less random rather initiate it once when class is created and call methods on the instance
Here is the fixed code (with above fixes implemented):
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
});
}
final DrawPanel myDraw = new DrawPanel();
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
frame.add(myDraw, BorderLayout.CENTER);
frame.add(startButton, BorderLayout.NORTH);
frame.add(stopButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if (!isClicked) {
}
myDraw.clearRects();
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
private ArrayList<MyRectangle> rects = new ArrayList<>();
private Random rand = new Random();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
addRect();
for (MyRectangle r : rects) {
g.setColor(r.getColor());
g.fillRect(r.x, r.y, r.width, r.height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public void clearRects() {
rects.clear();
}
public void addRect() {
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green)));
}
} // close inner class
}
class MyRectangle extends Rectangle {
Color color;
public MyRectangle(int x, int y, int w, int h, Color c) {
super(x, y, w, h);
this.color = c;
}
public Color getColor() {
return color;
}
}
I wish I could offer a solution, but as of yet I haven't found one. I can tell you the root of the "problem" here lies in the way you are drawing the Center section of your BorderLayout. You are overriding the whole paintComponent() function for this program and having whatever it creates put into the Center of your BoarderLayout. In this case, each time you click a button, the program calls the repaint to draw the image of a clicked button, but since you have also added ANY of the drawn objects to the Center panel, it also is drawn there. Since this specific repaint doesn't specify a location, it goes in the upper left corner.
I fixed your button problem on my Windows XP computer by invoking SwingUtilities.
I formatted your Java code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TwoButtonsTest implements Runnable {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new TwoButtonsTest());
}
#Override
public void run() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
if (!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
} // close inner class
}
To clean the screen when you press the Start button, you're going to have to add some methods to your DrawPanel class.
Here's one way to do it.
class DrawPanel extends JPanel {
protected boolean eraseCanvas;
public void setEraseCanvas(boolean eraseCanvas) {
this.eraseCanvas = eraseCanvas;
}
#Override
public void paintComponent(Graphics g) {
if (eraseCanvas) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
} else {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
}
} // close inner class
I have a simple class that draws a line when mouse dragging or a dot when mouse pressing(releasing).
When I minimize the application and then restore it, the content of the window disappears except the last dot (pixel). I understand that the method super.paint(g) repaints the background every time the window changes, but the result seems to be the same whether I use it or not. The difference between the two of them is that when I don't use it there's more than a pixel painted on the window, but not all my painting. How can I fix this?
Here is the class.
package painting;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
class CustomCanvas extends Canvas{
Point oldLocation= new Point(10, 10);
Point location= new Point(10, 10);
Dimension dimension = new Dimension(2, 2);
CustomCanvas(Dimension dimension){
this.dimension = dimension;
this.init();
addListeners();
}
private void init(){
oldLocation= new Point(0, 0);
location= new Point(0, 0);
}
public void paintLine(){
if ((location.x!=oldLocation.x) || (location.y!=oldLocation.y)) {
repaint(location.x,location.y,1,1);
}
}
private void addListeners(){
addMouseListener(new MouseAdapter(){
#Override
public void mousePressed(MouseEvent me){
oldLocation = location;
location = new Point(me.getX(), me.getY());
paintLine();
}
#Override
public void mouseReleased(MouseEvent me){
oldLocation = location;
location = new Point(me.getX(), me.getY());
paintLine();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me){
oldLocation = location;
location = new Point(me.getX(), me.getY());
paintLine();
}
});
}
#Override
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.red);
g.drawLine(location.x, location.y, oldLocation.x, oldLocation.y);
}
#Override
public Dimension getMinimumSize() {
return dimension;
}
#Override
public Dimension getPreferredSize() {
return dimension;
}
}
class CustomFrame extends JPanel {
JPanel displayPanel = new JPanel(new BorderLayout());
CustomCanvas canvas = new CustomCanvas(new Dimension(200, 200));
public CustomFrame(String titlu) {
canvas.setBackground(Color.white);
displayPanel.add(canvas, BorderLayout.CENTER);
this.add(displayPanel);
}
}
public class CustomCanvasFrame {
public static void main(String args[]) {
CustomFrame panel = new CustomFrame("Test Paint");
JFrame f = new JFrame();
f.add(panel);
f.pack();
SwingConsole.run(f, 700, 700);
}
}
You are not storing the state of the points you are drawing. When the panel is repainted, it only has information for the last point it drew.
Response to comment:
You would need to have a collection of Points, for instance ArrayList<Point> location = new ArrayList<Point>();
Then, in your listeners: location.add(new Point(me.getX(), me.getY()));
Finally, in paintLine():
for (Point location : locations) {
repaint(location.x,location.y,1,1);
}
The collection locations is usually referred to as a Display List. Most graphics programs use them.
Response to comment:
Yes, I expect so. I just tossed off an idea based on your code to give you a starting point. It is almost certainly a bad idea to do exactly as I have described.
Doesn't that mean I will draw all the points(instead of one) everytime I press or drag the mouse?
Yes, but #Dave's approach is perfectly satisfactory for thousands of nodes, as may be seen in GraphPanel. Beyond that, consider the flyweight pattern, as used by JTable renderers and illustrated here.
Addendum: Focusing on your AWTPainting questions, the variation below may illustrate the difference between System- and App-triggered Painting. As the mouse is dragged, repaint() invokes update(), which calls paint(); this is app-triggered. As you resize the window, only paint() is called (no red numbers are drawn); this is system-triggered. Note that there is a flicker when the mouse is released after resizing.
Flickering typically occurs when the entire component's background is cleared and redrawn:
4. If the component did not override update(), the default implementation of update() clears the component's background (if it's not a lightweight component) and simply calls paint().
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
public class AWTPainting {
public static void main(String args[]) {
CustomPanel panel = new CustomPanel();
Frame f = new Frame();
f.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.add(panel);
f.pack();
f.setVisible(true);
}
}
class CustomPanel extends Panel {
public CustomPanel() {
this.add(new CustomCanvas(new Dimension(320, 240)));
}
}
class CustomCanvas extends Canvas {
private MouseAdapter handler = new MouseHandler();
private List<Point> locations = new ArrayList<Point>();
private Point sentinel = new Point();
private Dimension dimension;
CustomCanvas(Dimension dimension) {
this.dimension = dimension;
this.setBackground(Color.white);
this.addMouseListener(handler);
this.addMouseMotionListener(handler);
this.locations.add(sentinel);
}
#Override
public void paint(Graphics g) {
g.setColor(Color.blue);
Point p1 = locations.get(0);
for (Point p2 : locations.subList(1, locations.size())) {
g.drawLine(p1.x, p1.y, p2.x, p2.y);
p1 = p2;
}
}
#Override
public void update(Graphics g) {
paint(g);
g.clearRect(0, getHeight() - 24, 50, 20); // to background
g.setColor(Color.red);
g.drawString(String.valueOf(locations.size()), 8, getHeight() - 8);
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (locations.get(0) == sentinel) { // reference identity
locations.set(0, new Point(e.getX(), e.getY()));
}
}
#Override
public void mouseDragged(MouseEvent e) {
locations.add(new Point(e.getX(), e.getY()));
repaint();
}
}
#Override
public Dimension getPreferredSize() {
return dimension;
}
}
#Andrew, #Dave, #trashgod Hi,
I did some research on this and, finally, this is what I've got. Please correct me if I'm wrong. You cannot override paint() so you call repaint() everytime you need to do app-triggered painting.
Repaint() calls update() which its default behavior is to call paint().
update() is used for incremental painting; that explains the flickering screen when paint() was doing all the work, which practically means it was painting the whole image at every step.
However, my question is if I add "locationsAdded = 0" in the update method that means everytime I drag the mouse I paint the whole image(like in paint), so why doesn't it blink like before?
I've also read something about painting in swing and I didn't understand why update() is never invoked for swing. Can you explain me why?
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
class CustomCanvas extends Canvas{
ArrayList<Point> locations;
int locationsAdded;
Point oldLocation;
Point location;
Dimension dimension;
CustomCanvas(Dimension dimension){
locations = new ArrayList<>();
this.dimension = dimension;
this.init();
addListeners();
}
private void init(){
oldLocation= new Point(0, 0);
location= new Point(0, 0);
}
public void paintLine(Graphics g, int x){
Point p1 = (Point)locations.get(x);
Point p2 = (Point)locations.get(x+1);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
locationsAdded++;
}
#Override
public void paint(Graphics g){
locationsAdded = 0;
g.setColor(Color.red);
for(int i = locationsAdded; i < locations.size()-1; i++){
paintLine(g, i);
}
}
public void update(Graphics g) {
//locationsAdded = 0;
for (int i = locationsAdded; i < locations.size()-1; i++) {
paintLine(g, i);
}
}
private void addListeners(){
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent me){
oldLocation = location;
location = new Point(me.getX(), me.getY());
locations.add(location);
repaint();
}
});
}
#Override
public Dimension getMinimumSize() {
return dimension;
}
#Override
public Dimension getPreferredSize() {
return dimension;
}
}
class CustomFrame extends Panel {
Panel displayPanel = new Panel(new BorderLayout());
CustomCanvas canvas = new CustomCanvas(new Dimension(700, 700));
public CustomFrame(String titlu) {
canvas.setBackground(Color.white);
displayPanel.add(canvas, BorderLayout.CENTER);
this.add(displayPanel);
}
}
public class AWTPainting {
public static void main(String args[]) {
CustomFrame panel = new CustomFrame("Test Paint");
Frame f = new Frame();
f.add(panel);
f.pack();
f.setSize(700,700);
f.show();
}
}
set your layout to Null layout