I am having a problem changing the shapes displayed when the user clicks on the menu item in java using the JFrame. Can anyone suggest how I can solve this? Below is my code:
public class PlayingWithShapes implements ActionListener
{
protected JMenuItem circle = new JMenuItem("Circle");
protected String identifier = "circle";
public PlayingWithShapes()
{
JMenuBar menuBar = new JMenuBar();
JMenu shapes = new JMenu("Shapes");
JMenu colors = new JMenu("Colors");
circle.addActionListener(this);
shapes.add(circle);
menuBar.add(shapes);
menuBar.add(colors);
JFrame frame = new JFrame("Playing With Shapes");
frame.setSize(600,400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(new Shapes());
frame.setJMenuBar(menuBar);
}
public static void main(String args[])
{
Runnable runnable = new Runnable() {
#Override
public void run() {
new PlayingWithShapes();
}
};
EventQueue.invokeLater(runnable);
}
I want to change the shape as circle when clicking on the circle menuItem
#Override
public void actionPerformed(ActionEvent click) {
if(click.getSource() == circle){
Shapes shape = new Shapes();
}
}
public class Shapes extends JPanel
{
How can I then call rectangle?
#Override
public void paintComponent(Graphics shapes)
{
circle(shapes);
}
public void circle(Graphics shapes)
{
shapes.setColor(Color.yellow);
shapes.fillOval(200,100, 100, 100);
}
public void rectangle(Graphics shapes)
{
shapes.setColor(Color.MAGENTA);
shapes.fillRect(200,100,100,100);
}
}
}
Any help is much appreciated.
Suggestions:
Don't create a new Shapes JPanel within your actionPerformed as this achieves nothing.
Instead within actionPerformed change the state of a field of the class, and base the drawing within your paintComponent method on the state held by that field.
For instance, if you only have two different types of shapes, the above field could simply be a boolean, perhaps called drawRectangle, and in actionPerformed you'd change this to true or false and call repaint();. And then in you'd use an if block within paintComponent drawing a Rectangle if it is true or a oval if not.
If you want to have the ability to draw multiple different shapes, create an enum and make the field discussed above a field of this enum type. Then use a switch statement within paintComponent to decide which shape to draw.
If you want to show different shapes at the same time, then you'll need to create a collection of Shape such as an ArrayList<Shape> and add Shape-derived objects to this collection, and then iterate through it in a for loop within paintComponent using a Graphics2D object to draw each Shape. I don't think that you need this right now.
Don't forget to call the super.paintComponent(g); within your method override.
i.e.,
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawRectangle) {
rectangle(g);
} else {
circle(g);
}
}
Related
I'm currently learning Java GUI, and I'm making a Java program where you can edit shapes with text within a GUI. I've created a JFrame which contains a custom MyCanvas object and a text field. The idea is that the MyCanvas object has a MouseListener and will contain shapes, these shapes when clicked will enable the text field in the GUI so that the user can enter a new message to be displayed in the shape. However, I'm running the GUI with a Runnable inside the GUI class's main method, and I can't enable the text box from the MyCanvas class since it's outside of the Runnable. Could anyone help me how to make this possible?
Here's the basic structure of my code (pseudocode):
// GUI class
public class GUI extends JFrame implements ActionListener {
private static MyCanvas c = new MyCanvas(); // canvas
private static TextField editor = new TextField(); // text field
public static void init() {
// initialize GUI elements and disable text box
}
public static void enableTextBox() {
// enables the text field
}
public static void disableTextBox() {
// disables the text field
}
public void actionPerformed(ActionEvent e) {
// get message from text box
}
public static void main(String[] args) {
// Run the GUI frame
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
init();
/* Add a RectTextBox (a Rectangle object with text inside,
* class defined elsewhere)
*/
c.addShape(new RectTextBox("Hello World");
}
}
}
}
// Canvas class
class MyCanvas extends JPanel implements MouseListener {
// ArrayList of blocks on the canvas
ArrayList<RectTextBox> blocks = new ArrayList<RectTextBox>();
// Ctor
public MyCanvas() {
// Initialize canvas and add MouseListener to this canvas
}
public void paintComponent(Graphics g) {
// Paints all blocks on canvas
}
public void addShape(RectTextBox b) {
// Adds the text box b to the blocks ArrayList
}
public void mouseClicked(MouseEvent e) {
// check if any blocks from the ArrayList is clicked
/* enables the text field from GUI to enter messages, then set the
* message entered to the block
*/
}
}
So, you're off to a good start. MyCanvas has functionality which can be called by other classes, for example, addShape...
// Canvas class
class MyCanvas extends JPanel implements MouseListener {
// ArrayList of blocks on the canvas
ArrayList<RectTextBox> blocks = new ArrayList<RectTextBox>();
// Ctor
public MyCanvas() {
// Initialize canvas and add MouseListener to this canvas
}
public void paintComponent(Graphics g) {
// Paints all blocks on canvas
}
public void addShape(RectTextBox b) {
// Adds the text box b to the blocks ArrayList
}
public void mouseClicked(MouseEvent e) {
// check if any blocks from the ArrayList is clicked
/* enables the text field from GUI to enter messages, then set the
* message entered to the block
*/
}
}
Next, you need to pass a reference of MyCanvas to those classes which want to perform some operation on it, as a "really" basic example...
public class EditorPane extends JPanel {
private MyCanvas canvas;
public EditorPane(MyCanvas canvas) {
this.canvas = canvas;
// Build UI as required
}
}
Then when EditorPane wants to make a change, it can use canvas to call the methods it needs to.
Then, we you build your UI, you would make an instance of MyCanvas, pass that reference to both the EditorPane and add it to the UI, for example...
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MyCanvas canvas = new MyCanvas();
EditorPane editor = new EditorPane(canvas);
JFrame frame = new JFrame();
frame.add(canvas);
frame.add(editor, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
Now, personally, I'd fold much of this up into interface to prevent EditorPane from doing more to MyCanvas then it should, but that's a topic for another day ;)
I am working on a simple object drawing program using Swing in Java.
My program simply should draw shapes according to buttons when clicked, and move any shapes with the mouse. I have four buttons which draw rectangle, circle and square on screen. So far I did managed to draw to shapes when you click on buttons. but i want to move the shapes on screen which it did not work out.
The problem is this: When I click on circle shape to drag it around with mouse, it clears all the screen and noting is on the screen.
And, is there a way to clean all the screen when I click on clear button?
Thank you?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PaintProject extends JComponent implements ActionListener,
MouseMotionListener {
private int CircleX=0;
private int CircleY=0;
private int RectX=100;
private int RectY=100;
private int SquareX=300;
private int SquareY=200;
public static void main(String[] args) {
JFrame frame = new JFrame("NEW PAINT PROGRAME!");
JButton CircleButton = new JButton("Circle");
CircleButton.setActionCommand("Circle");
JButton RectangleButton = new JButton("Rectangle");
RectangleButton.setActionCommand("Rectangle");
JButton SquareButton = new JButton("Square");
SquareButton.setActionCommand("Square");
PaintProject paint = new PaintProject();
CircleButton.addActionListener(paint);
RectangleButton.addActionListener(paint);
SquareButton.addActionListener(paint);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(paint);
frame.add(CircleButton);
frame.add(RectangleButton);
frame.add(SquareButton);
frame.addMouseMotionListener(paint);
frame.pack();
frame.setVisible(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
private void drawCircle() {
Graphics g = this.getGraphics();
g.setColor(Color.red);
g.fillOval(CircleX, CircleY, 100, 100);
}
private void drawRectangle() {
Graphics g = this.getGraphics();
g.setColor(Color.green);
g.fillRect(RectX, RectY, 100, 300);
}
private void drawSquare() {
Graphics g = this.getGraphics();
g.setColor(Color.blue);
g.fillRect(SquareX, SquareY, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("Circle")) {
drawCircle();
}
else if (command.equals("Rectangle")) {
drawRectangle();
}
else if (command.equals("Square")) {
drawSquare();
}
}
#Override
public void mouseDragged(MouseEvent e) {
CircleX=e.getX();
CircleY=e.getY();
repaint();
}
}
As noted here and here, getGraphics() is not how to perform custom painting in Swing. Instead, override paintComponent() to render the desired content. It looks like you want to drag shapes using the mouse. A basic approach to moving a selected object is shown here; substitute your fillXxx() invocation for the drawString() shown there. For multiple shapes, use a List<Shape>; the clear command then becomes simply List::clear. A complete example is cited here; it features moveable, selectable, resizable, colored nodes connected by edges.
I've been working on a drinking game program for school.
//this is the game //http://sotallytober.com/games/verbal/mexican/
Anyway, I painted a image in an JPanel using the following code (it's an class that extends JPanel)
public class iconPanel extends JPanel {
ImageIcon image;
Image pic;
public iconPanel(String startScreenImage) {
image = new ImageIcon(startScreenImage);
pic = image.getImage();
this.setBackground(new Color(0, true));
}
#Override
public void paintComponent(Graphics g) {
//Paint background first
g.drawImage (pic, 0, 0, getWidth (), getHeight (), this);
}
Now in my other class, where I have the layout and all the components I declare on top my JPanels like this :
private JPanel pnDrinkPlayerBW;
Then in a method in the same class named MakeComponents I set the JPanel to :
pnDrinkPlayerBW = new iconPanel("img/glass.jpg");
pnDrinkPlayerBW.setPreferredSize(new Dimension(183,61));
Afterwards I add it to the Panel where it has to come, and that panel onto the frame in the method makeLayout() (I don't think that it's useful code, so if you want to see it, ask me)
Then if a button gets pressed, I want to change the glass.jpg image to another image, for example beerGlass0.png, so in the actionlistener in another method actionEvents() I do this:
pnDrinkPlayerBW = new iconPanel("img/beerGlass.png");
pnDrinkPlayerBW.setPreferredSize(new Dimension(183,61));
pnDrinkPlayerBW.repaint();
I'll put the constructor of this class also here, just if people need it :
public SpelScreen(){
makeComponents();
makeLayout();
actionEvents();
} // note : this is'nt the full constructor, just the call for the methods I talked about, SpelScreen extends JFrame..
So what I want to do, is to set in the class SpelScreen a new image for the iconPanel and repaint it using the same instance of the spelscreen.
I am quite new to Java, so don't expect me to rapidly understand complicated code :)
Thanks!
First off you're forgetting to call super.paintComponent in your paintComponent method. Also paintComponent should be protected not public
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(..);
}
Second, I don't think you want to create a new iconPanel and immediately call repaint on it. That will probably do nothing.
Instead have a setter for your pic, then just repaint(); inside that method.
public void setPic(Image pic) {
this.pic = pic;
repaint();
}
Then you can just call the setPic from the the class you created the iconPanel in. For example
iconPanel panel = new iconPanel("...");
... // then in some listener
public void actionPerformed(ActionEvent e) {
Image pic = null;
try {
pic = ImageIO.read(...);
panel.setPic(pic);
} catch ...
}
Another option is just to have an array of images you initialize in the iconPanel. Then in a a listener, you can just change the index the if the image array then call repaint. Something like this
Image[] images = new Image[5];
int imageIndex = 0;
// fill the array with images
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(images[imageIndex], ...);
}
Then you could just change the imageIndex and repaint()
Side Note
You should be using Java naming convention. Class names being with capital letters i.e. iconPanel → IconPanel
Update
Using ImageIcon
public void setImage(ImageIcon img) {
pic = img.getImage();
repaint();
}
I am new to Java....I studied that we can add two things on frame... I added button and in response by clicking on button I want rectangle as output....but i don't understand that..Why i am not getting output.....
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class customizedgui5 implements ActionListener {
JButton button;
JFrame frame;
public static void main(String[] args) {
customizedgui5 hi = new customizedgui5();
hi.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("click me");
button.addActionListener(this);
myclass a = new myclass();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(a, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
frame.revalidate();
}
}
class myclass extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
I would start by taking a look at Performing Custom Painting.
The main problem in your code is you are getting NullPointerException when you click the button because the reference of frame is null.
It is null because you've shadowed it in the constructor (basically, declared another variable of the same name within the constructor)...
// I'm null..
JFrame frame;
public void go() {
// Not the same as frame above...
JFrame frame = new JFrame();
You are also going to not see any changes because of a number of reasons...
The myclass panel has no size. With BorderLayout, this won't be "too" much of a problem, but...
You've drawing outside of the visible range of the panel. The rectangle you are painting won't appear because it is being painted outside of the width and height of the panel.
The rectangle will appear before you press the button as paintComponent will be called to update the state of the panel once it's made visible on the screen...
The first thing you need to is provide some kind of size hints to the BorderLayout. Try adding...
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
To myclass.
You also don't need to repaint the frame, what you really want to repaint is the instance of myclass. Try updating customizedgui5 so that a becomes a instance variable (like frame...
//...
myclass a;
//...
public void go() {
//...
a = new myclass();
//...
}
public void actionPerformed(ActionEvent event) {
a.repaint();
}
Now, the rectangle will still be shown the moment that the panel is made visible on the screen. Sure you could try setting it invisible, but this will affect the layout of the frame, hiding your component to start with, so, instead, we need some kind of flag we can trip so we know when to paint the rectangle. This is easily achieved by using a simple boolean variable, for example...
class myclass extends JPanel {
private boolean paintRect;
public void setPaintRect(boolean paint) {
paintRect = paint;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (paintRect) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
}
Then in you actionPerformed method, you just need to set the flag...
public void actionPerformed(ActionEvent event) {
a.setPaintRect(true);
}
You may also want to take a read through Code Conventions for the Java Programming Language. It will make it easier for people to read your code...
When you click on your button, you're calling the method actionPerformed(ActionEvent event)
Take a look at what you did there. Currently, you repaint and re-validate the frame. If you want to add a rectangle to your frame, you need to do so by adding a new component to the frame that will draw the rectangle.
You could add another instance of your myclass JPanel which paints a rectangle like so:
public void actionPerformed(ActionEvent event) {
frame.getContentPane().add(new myclass(), BorderLayout.NORTH);
frame.repaint();
}
This would add your custom rectangle-drawing panel to the North section of your BorderLayout. If you want to add the rectangle "on top of" your button, you should embed your button within a JPanel, then add the rectangle-drawing panel to that instead of your main JFrame
I need to draw a square,line,circle when pressed the corespondent button. Also I need to do this using FactoryMethod design pattern.
I simply don't get how to draw on the same canvas, and because I have a class for every shape, how do i get the corresponding paint(Graphics g) method?
This is what I have so far:
public interface Shape
{
public void draw();
}
Square class
public class Square extends Canvas implements Shape
{
Graphics g;
Canvas c;
public Canvas getCanvas()
{
return c;
}
public void setCanvas(Canvas c)
{
this.c=c;
}
#Override
public void draw()
{
g.drawRect(20, 30,100,100);
}
public void paint(Graphics g)
{
g.drawRect(20, 30,100,100);
g.setColor(Color.BLUE);
}
}
Factory
public class ClassFactory extends Canvas{
JButton patrat;
Figura d;
String nameButon;
Graphics g;
Canvas c;
public Canvas getCanvas()
{
return c;
}
public void setCanvas(Canvas c)
{
this.c=c;
}
public ClassFactory()
{
super();
this.setBounds(0,0,500,450);
this.setBackground(Color.CYAN);
JButton square=new JButton("square");
patrat.setBounds(510, 10, 80,25);
JPanel panel=new JPanel();
panel.setLayout(null);
panel.setBounds(0,0,600,500);
panel.setBackground(Color.GRAY);
panel.add(this);
this.addComponentListener(p);
panel.add(square);
JFrame f=new JFrame("Draw");
f.setLayout(null);
f.setBounds(50,50,700,600);
f.getContentPane().setBackground(Color.DARK_GRAY);
f.setResizable(false);
f.add(panel);
f.show();
}
public Shape getFigure()
{
Shape d=null;
if(nameButton.equals("square"))
{
d=new Square();
}
return d;
}
}
Suggestions:
The factory should not create a GUI, should not extend Canvas, or really extend anything, it should not create a JFrame or do anything of the sort. It should concern itself only with creating objects of Shape child classes. The GUI creation code should be elsewhere.
Likely the factory's getFigure(...) method will be the one to produce this. It should likely accept a parameter, perhaps a String or an enum, that tells it what sub-class of Shape to produce.
Shape's draw method should likely accept a Graphics parameter so that its children can use it to draw with.
You shouldn't mix AWT components (i.e., Canvas) and Swing components together unnecessarily. Instead, just draw in a JPanel's paintComponent(Graphics g) method, not in a Canvas's paint(Graphics g) method.
In that JPanel have a Shape variable that is not initialized, perhaps called shape.
Inside of paintComponent(...) check if shape is null. If not, draw it by calling shape.draw(g).
In your JButton ActionListeners, have the Factory create a Shape child class object and assign it to the shape variable
Then call repaint() on the JPanel that does the drawing.