My problem is, when I press a button paintComponent should be called then a figure should be drawn on the JPanel, Unfortunately paintComponent draws the figure when the program is loaded, in that case the button is useless.
I made a small version of my program, to make it easy and fast to read and detect the problem.
This code here is not the original one but it demonstrates the same problem.
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class TestPaint extends JPanel implements ActionListener {
private JButton button_1 = new JButton( "Draw Oval" );
public TestPaint() {
add(button_1);
}
#Override
public void actionPerformed(ActionEvent e) {
if ( e.getSource() == button_1 )
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(10, 10, 100, 100);
}
}
To run the program
import javax.swing.JFrame;
public class RunPaint {
public static void main(String[] args) {
TestPaint paint_g = new TestPaint();
JFrame frame = new JFrame("Testing");
frame.add(paint_g);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
As a simple solution you can create an instance variable for your class:
private Boolean buttonPressed = false;
Then in your actionListener you set the value to true.
and in your paintComponent() method you add code like:
if (buttonPressed)
g.drawOval(...);
A better (and more complicated solution) is to keep a List of objects to paint. Initially the List will be empty, and when you press the button you add an object to the List. Then the painting code just iterates through the List to paint the objects.
Check out Custom Painting Approaches for more ideas. The example code doesn't do exactly this, but it does show how to paint from a List.
Let your actionPerformed() implementation add the desired geometric figure to a List<Shape> and have paintComponent() iterate through the list to render the shapes. A complete example is seen here.
Related
I'm practising to draw a shape on a JPanel by clicking on a Jbutton, but I cannot. It's been five hours that I'm surfing the web, but I cannot find the way to do it.
This is what I want to do: if I click on "Rectangle" button a rectangle appears under the buttons and if I click on "Circle" button a circle appears.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Shape extends JFrame {
JButton rec, circle;
static String botSelected;
Shape (){
frameSet ();
}
void frameSet(){
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(600,300);
rec = new JButton ("Rectangle");
circle = new JButton("Circle");
JPanel panel = new JPanel();
frame.add(panel);
panel.add(rec);
panel.add(circle);
Click clk = new Click();
rec.addActionListener(clk);
circle.addActionListener(clk);
}
public void paint (Graphics g){
super.paint(g);
if (botSelected.equals("Rectangle"))
g.fillRect(50,50,50,50);
else if (botSelected.equals("Circle"))
g.fillOval(50,50,50,50);
}
public static void main (String [] arg){
Shape s = new Shape();
}
}
class Click implements ActionListener{
public void actionPerformed (ActionEvent e){
Shape.botSelected = e.getActionCommand();
}
}
The first thing I would do is have a read through Painting in Swing and Performing custom painting to better understand how the painting process works.
Next you need to understand that JFrame is a bad choice for painting to. Why? Because it's multilayered.
A JFrame contains a JRootPane, which contains a JLayeredPane the contentPane, glassPane and the JMenuBar and in your example, it also contains a JPanel.
With the (default) exception of the glassPane, all these components are opaque.
While it's possible to have something drawn in the paint method show it, if any of the other components paint themselves, it will be wiped clean - this is because Swing components can actually be painted independently of each other, with having to have the parent paint itself first.
A better solution is to start by extending from JPanel and override its paintComponent method.
For simplicity, I'd also encourage you to implement the ActionListener against this class as well, it will allow the actionPerformed method to access the properties of the component and, in your case, call repaint to trigger a paint cycle when you want to update the UI.
Here is a derived example from your code.
As #MadProgrammer said, don't extend JFrame.
In the following example, here are the major changes :
give a non-null value to botSelected, or the first calls to paintComponent will give you a NullPointerException
the class now extends JPanel, and overrides paintComponent for custom painting
the ActionListener is an anonymous class, because you don't need a separate class, and it has direct access to the fields from Shape
botSelected is no longer static (see above point)
.
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Shape extends JPanel {
JButton rec, circle;
String botSelected = "";// don't let it be null, it would make paintComponent crash on startup
Shape() {
frameSet();
}
void frameSet() {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(600, 300);
rec = new JButton("Rectangle");
circle = new JButton("Circle");
frame.add(this);
this.add(rec);
this.add(circle);
// anonymous class, has access to fields from the outer class Shape
ActionListener clk = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
botSelected = e.getActionCommand();
repaint();
}
};
rec.addActionListener(clk);
circle.addActionListener(clk);
}
//custom painting of the JPanel
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
if (botSelected.equals("Rectangle")) {
g.fillRect(50, 50, 50, 50);
} else if (botSelected.equals("Circle")) {
g.fillOval(50, 50, 50, 50);
}
}
public static void main(final String[] arg) {
Shape s = new Shape();
}
}
I've made a JFrame with Diferent JButtons and i'd like to get an image from another class. Any ideas? Or how draw on the same class but on the action performed?
Because it doesnt let me to do any drawings...my complier always gives me error messages
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.*;
public class red extends JFrame {
public JButton b;
public JButton b1;
public JButton b2;
public JButton b3;
public JButton b4;
public static Image p;
public static Graphics g;
public red() throws IOException {
gui1 x = new gui1();
setTitle(" ");
setSize(1200,700);
setLayout(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
b= new JButton("click");
b1= new JButton();
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e0){
b1.setBounds(0, 0, 200, 200);
b.show(false);
add(x);
}
});
b.setBounds(0, 0, 100, 100);
add(b1);
add(b);
setVisible(true);
}
public static void main(String[] args) throws IOException {
red k = new red();
}
}
import java.awt.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class gui1 extends Canvas {
public static Image p;
public void paint(Graphics g){
g.drawImage(p, 700, 200, 100, 100, this);
}
{
try {
p= ImageIO.read(new File("Lighthouse.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Phew! I see A LOT of errors in your code (even after I corrected the compilation errors):
You're not following the Java naming conventions:
Class names should be nouns, in mixed case with the first letter of each internal word capitalized
while red is a noun it should be more descriptive and be capitalized. The same goes for gui1
You're extending JFrame which in plain english would say: red is a JFrame, you should really avoid this and create your GUI based on JPanels instead... see Java Swing using extends JFrame vs callint it inside of class
You're setting size (a REAAAAAAALLY big one window for the JButton sizes you're using), instead use pack()
You're using null-layout, while pixel-perfect GUIs might seem like the easiest way to create complex GUIs for Swing newbies, the more you use them the more problems related to this you'll find in the future, they are hard to maintain and cause random problems, they don't resize, etc. Please read Null layout is evil and Why is it frowned upon to use a null layout in Swing? for more information about why you should avoid its use and why you should change your GUI to work with Layout Managers along with Empty Borders for extra spacing between components.
You're making use of a deprecated method JFrame#show() you should be using JFrame#setVisible(...) instead.
Related to point #4, you shouldn't be calling setBounds(...) method, but let that calculations to the layout managers.
You're not placing your program on the Event Dispatch Thread (EDT), Swing is not thread safe, you can fix this by changing your main() method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're mixing AWT and Swing components, instead of using AWT's Canvas use Swing's JPanel which has more functionality and support.
Images will become embedded resources once they're packaged in a JAR file, so it's wise to start treating them as if they already were, not as external files as shown in the embedded-resource tag.
Once you change from Canvas to JPanel you should override its paintComponent(...) method and not paint(...) and call it's super.paintComponent(g) method as the first line, also don't forget to add the #Overrides annotation. See the tutorial on Swing custom painting.
You're abusing the use of static keyword, see how does the static keyword works?
After seeing all the above errors I recommend you to go back and Learn the basics of the language before starting with a graphical environment which will only add more difficulty to your learning.
From what I understand you want to draw an image on a button click, if that's the case then you can wrap your image in a JLabel and add that JLabel to a JPanel which then is added to a parent JPanel which is later added to the JFrame:
As you can see in the GIF above, the icon is displayed after user presses the button.
Obviously this can be improved for the GUI to be more "attractive" with combinations of layout managers and empty borders as stated before.
This was done with the following code:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ImageDrawingFromOneClassToAnother {
private JFrame frame;
private JPanel pane;
private JPanel leftPane;
private JPanel rightPane;
private ImageIcon icon;
private JButton button;
private JLabel label;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageDrawingFromOneClassToAnother().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
icon = new ImageIcon(this.getClass().getResource("king.png")); //Read images as if they were already embedded resources
button = new JButton("Draw image");
label = new JLabel(""); //Create an empty label
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setIcon(icon); //On button click, we set the icon for the empty label
}
});
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200); //Set a size for the main panel
}
};
pane.setLayout(new GridLayout(1, 2)); //The main panel
leftPane = new JPanel(); //The button panel
leftPane.setLayout(new BoxLayout(leftPane, BoxLayout.PAGE_AXIS));
leftPane.add(button);
rightPane = new JPanel(); //The panel where the image will be drawn
rightPane.add(label);
//We add both (button and image) panels to the main panel
pane.add(leftPane);
pane.add(rightPane);
frame.add(pane); //Add the main panel to the frame
frame.pack(); //Calculate its preferred size
frame.setVisible(true); //Set it to be visible
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I am trying to create text-fields on frame by getting input at run-time. Is it possible? Or I have to create another frame for that. I tried this code, but it's not working. Please Help me out, and tell me what's wrong with this code.
import java.awt.BorderLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Check extends JFrame implements ActionListener
{
JTextField txtqty;
JTextField[] tfArr;
JPanel p1,p2;
JButton bsmbt;
public Check()
{
GUIDesign();
}
public void GUIDesign()
{
p1 = new JPanel();
txtqty = new JTextField(10);
JButton bsmbt= new JButton("OK");
p1.add(txtqty);
p1.add(bsmbt);
p2=new JPanel();
p2.setLayout(null);
add(p1,BorderLayout.NORTH);
setSize(500, 500);
setVisible(true);
setLocation(100, 100);
bsmbt.addActionListener(this);
}
public static void main(String[] args)
{
new Check();
}
public void TFArray(JTextField[] temp)
{
int x,y,width,height;
x=10;y=30;width=50;height=20;
int no_of_textboxes = Integer.parseInt(txtqty.getText());
temp=new JTextField[no_of_textboxes];
for(int i=0;i<no_of_textboxes;i++)
{
temp[i]= new JTextField(10);
temp[i].setBounds(x, y, width, height);
x+=(width+10);
p2.add(temp[i]);
}
add(p2);
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(this, txtqty.getText());
TFArray(tfArr);
}
}
->Method TFArray() isn't working.
You have many errors in your code:
public void TFArray(JTextField[] temp): method names should start with lowerCamelCase
You're extending JFrame, you shouldn't extend JFrame, because when you extend it your class is a JFrame, JFrame is rigid so you can't place it inside anything else, instead you might consider creating a JFrame instance and if you ever need to extend JComponent extend from JPanel.
JButton bsmbt= new JButton("OK"); the variable bsmbt is a local variable inside your constructor, your global variable bsmbt is not used anywhere, and if you try to use it later you'll get a NullPointerException, instead change that line to:
bsmbt= new JButton("OK");
You're using null layout for p2, instead use a proper Layout manager and read Null layout is evil and Why is it frowned upon to use a null layout in swing?. Swing was designed to work with different PLAFs, screen sizes and resolutions, while pixel perfect GUIs (with setBounds()) might seem like the best and faster way to create a complex GUI in Swing, the more GUIs you make, the more errors you'll get due to this.
To solve your problem call revalidate() and repaint()
The above code creates 2 textfields. but when I again put some value and submit it, it doesn't seem to reflect any changes.
That might be because you're overriding x, y, height and width variables each time you enter TFArray method. But that is a guess, if you want a real answer, follow the suggestions above and post a proper and valid Minimal, Complete, and Verifiable example
I have a pretty simple JFrame (it consists only of one text field and a lot of stuff I paint), though whenever repaint() is called the text field changes. I'm pretty sure it's repaint(), as it happens even when I drag the frame from one monitor to the next as well as whenever I call it in the code.
It starts off fine when I run the program:
However, whenever repaint() is called, this happens:
If I start typing in the field, the rest immediately pops back up and works fine. Ultimately, my end goal is to reset a large portion of my frame to what is painted in paintComponent() while still having the text field visible.
I'm relatively new to Graphics and painting, any help is greatly appreciated!
EDIT
In creating the SSCCE, I discovered the problem with the text field was being caused by the Graphics2D method rotate that was in my paintComponent method. I don't know why it's happening, but I can work around it and consider that problem solved.
Now, I'm having a new problem: my paintComponent method is being called one too many times. Below is a SSCCE of this problem, the order I want things to happen is:
Call to paintComponent()
(when button is pressed) Call to repaint()
(when button is pressed, but after repaint) Call to paintStuff()
That all happens, however afterwards paintComponent is somehow being called, erasing everything painted by paintStuff().
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class tester extends JFrame {
private JPanel contentPane;
private JButton button;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
tester frame = new tester();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public tester() {
setBounds(100, 100, 450, 300);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new JPanel(){
public void paintComponent(Graphics g1) {
System.out.println("draw rectangle - should be first");
super.paintComponent(g1);
g1.drawRect(50,50,50,50);
}
};
contentPane.setLayout(null);
setContentPane(contentPane);
button = new JButton("Click me!");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("repaint - should be second");
repaint();
paintStuff(contentPane.getGraphics());
}
});
button.setBounds(10, 11, 95, 20);
contentPane.add(button);
}
public void paintStuff(Graphics g){
System.out.println("draw circle - should be last");
g.drawOval(100,100,10,10);
}
}
paintStuff(contentPane.getGraphics());
No, don't use the getGraphics() method to do painting. To repaint the component you simply use:
contentPane.repaint();
and then you move the drawOval(...) statement to the paintComponent() method.
If you want to draw the oval conditionally then you need to create a Boolean property for your custom painting. Then the code would look something like:
super.paintComponent(g1);
g1.drawRect(50,50,50,50);
if (drawOval)
g1.drawOval(100,100,10,10);
Then in your class you would create a method like:
public void setDrawOval(Boolean drawOval)
{
this.drawOval = drawOval;
repaint();
}
So in the ActionListener you simply use:
contentPane.setDrawOval(true);
I am trying to learn java and i am practicing with a simple program with 2 simple buttons. Here is my code :
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Askhsh 3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ColorJPanel application = new ColorJPanel();
frame.add(application);
frame.setSize(500,500);
frame.setVisible(true);
}
}
And the class ColorJPanel:
import java.awt.*;
import javax.swing.*;
public class ColorJPanel extends JPanel{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.setBackground(Color.WHITE);
JButton arxikopoihsh = new JButton("Αρχικοποίκηση");
JButton klhrwsh = new JButton("Κλήρωση");
add(arxikopoihsh);
add(klhrwsh);
this.revalidate();
this.repaint();
}
}
As you can see the only thing i want to do for now is to place 2 simple buttons that do nothing! Here is my output:
http://imageshack.us/photo/my-images/847/efarmogh.jpg/
When i am running the application i am seeing the buttons filling the window!
Note that if i remove the "this.revalidate();" command i have to resize the window to see the buttons !
Thanks very much for your time :)
Don't add components in paintComponent. This method is for painting only, not for program logic or to build GUI's. Know that this method gets called many times, often by the JVM and most of the time this is out of your control, and also know that when you ask for it to be called via the repaint() method, this is only a suggestion and the paint manager may sometimes choose to ignore your request. The paintComponent method must be lean and fast as anything that slows it down will slow down the perceived responsiveness of your application.
In your current code, I don't even see a need to have a paintComponent method override, so unless you need it (if doing for instance custom painting of the component), I suggest that you get rid of this method (and the calls to repaint and revalidate). Instead, add your components in the class's constructor and make sure to pack your top level container after adding components and before calling setVisible(true). Most important -- read the Swing tutorials as this is all covered there.
e.g.,
Main.java
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Askhsh 3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ColorJPanel application = new ColorJPanel();
frame.add(application);
frame.pack();
frame.setVisible(true);
}
}
ColorJPanel.Java
import java.awt.*;
import javax.swing.*;
public class ColorJPanel extends JPanel{
public static final int CJP_WIDTH = 500;
public static final int CJP_HEIGHT = 500;
public ColorJPanel() {
this.setBackground(Color.WHITE);
JButton arxikopoihsh = new JButton("Αρχικοποίκηση");
JButton klhrwsh = new JButton("Κλήρωση");
add(arxikopoihsh);
add(klhrwsh);
}
// let the component size itself
public Dimension getPreferredSize() {
return new Dimension(CJP_WIDTH, CJP_HEIGHT);
}
}