2

I'm trying to display a string when a button is pressed, but it does not work. I do not know what the problem is. I get no error, but that does not bother me. I'm missing something fundamental, I suppose. Please help!!

//I'm trying to draw a string in the frame when a button is pressed, but it won't work..
//Can't figure out what the problem is.


import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

class AppletTwo extends JFrame implements ActionListener
{

    JFrame frameOne;
    JButton btnOne;

    AppletTwo()
    {

        frameOne = new JFrame("frameOne");
        frameOne.setSize(320,240);
        frameOne.setLayout(new FlowLayout(FlowLayout.LEFT));
        frameOne.setVisible(true);
        frameOne.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        btnOne = new JButton("Print");
        btnOne.addActionListener(this);
        frameOne.add(btnOne);
    }

    public void actionPerformed(ActionEvent ae)
    {

        if(ae.getSource() == btnOne)
        {
            repaint();
        }
    }

    public void paint(Graphics g)
    {
        g.drawString("Never Works",150,150);
    }

    public static void main(String[] args)
    {

        AppletTwo frame1 = new AppletTwo();
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
Meet K.
  • 459
  • 3
  • 15

1 Answers1

6

" I'm missing something fundamental, I suppose. "

Yes, you are:

Main problem:

  • Your class is JFrame which is the component for which you are overriding the paint method. But you create another instance of a JFrame, which is the one you setVisible to. Keep in mind, you haven't drawn anything to this frame. So you are seeing the new instance of frame, not the class frame for which you are painting (and for which you never set visible).

Other problems:

  • You should always call super.paint[Component] after a paint[Component] override

    @Override
    public void paint(Graphics g) {
        super.paint(g);
    }
    
  • Don't paint on top level container like JFrame. Instead paint on a JPanel or JComponent and override is paintComponent method and call super.paintComponent, then add that component to the frame. See Performing Custom Painting

  • Swing apps should be run on the event dispatch thread (EDT). You can do so by wrapping your main code in a SwingUtilities.invokeLater(...). See Initial Threads

    public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable(){
            public void run() {
                AppletTwo frame1 = new AppletTwo();
            }
        }); 
    }
    
  • Generally, you always want to set the frame visible after adding your components.

Other notes:


UPDATE

Example with all the above mentioned points.

import java.awt.BorderLayout;
import java.awt.Dimension;
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;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class SimpleDrawing {

    public SimpleDrawing() {
        final DrawingPanel panel = new DrawingPanel();

        final JTextField field = new JTextField(15);

        JButton button = new JButton("Change name");
        button.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                String someString = field.getText();
                if (!someString.isEmpty()) {
                    panel.setString(someString);
                }
            }
        });

        JPanel bottomPanel = new JPanel();
        bottomPanel.add(field);
        bottomPanel.add(button);

        JFrame frame = new JFrame();
        frame.add(panel);
        frame.add(bottomPanel, BorderLayout.SOUTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

    public class DrawingPanel extends JPanel {
        private String someString = "Stackoverflow";

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawString(someString, 75, 75);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension (300, 100);
        }

        public void setString(String someString) {
            this.someString = someString;
            repaint();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                new SimpleDrawing();
            }
        });
    }  
}
Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks for clearing up the concept. My fundamentals concerning Java are pretty weak. Another, probably stupid, question: If I set a layout for a JFrame (inherited or not) or add a component, say a button, does it somehow prevent the drawString() result from showing up? Can I use a button to display text using drawString()? If possible, do include or point me to an example.. – Meet K. Jun 19 '14 at 18:46
  • No, though it's not recommended to add components to a drawing surface. My answers explains _exactly_ what was causing your drawing not to appear. See section **Main Problem** – Paul Samsotha Jun 19 '14 at 18:49
  • _"Can I use a button to display text using drawString()?"_ . Don't hard code the String value in `drawString`. Use a variable, that you can update, then call `repaint()`. For example, some enters some text in a text area. When you press the button, the text from the text area is put that variable. When you repaint, that new text will be drawn – Paul Samsotha Jun 19 '14 at 18:51
  • When you create the panel, you need to set a preferred size to it before you add it to the frame. With some layouts, if you don't set the preferred size of the panel you you're drawing on, the panel won't show. – Paul Samsotha Jun 19 '14 at 18:53
  • I'm about to knock out right now. Maybe tomorrow I'll throw you a complete example – Paul Samsotha Jun 19 '14 at 18:56
  • Sure enough buddy!! Thanks for everything!! :) – Meet K. Jun 19 '14 at 19:39
  • And for that "Main Problem" matter, I created a JFrame object "f" within the program instead of inheriting it, called f.repaint(), and added a g.drawString within paint(), by initializing (and not initializing, too) g with f.getGraphics(). Still it won't work. Even thought I'm only using a single JFrame. – Meet K. Jun 19 '14 at 19:51
  • **1.** Never, ever, ever, call `getGraphics()` to do custom painting. The correct way is to override `paint[Component]`. The link explains not to extends if you don't add an new functionality. If you are painting, that's new functionality. **2.** Just because they are not the main problem, does _not_ mean you should just skip them. I said _not_ to paint on JFrame, istead on JPanel. Then you don't need to extends JFrame. Just add the panel to the frame. See my updated answer for example. Please read the link on Performing Custom Painting if you have further questions. – Paul Samsotha Jun 20 '14 at 02:01
  • Okay!! Thanks for everything! I've a lot of studying to do. I'm trying to learn as I go. Thanks for everything, mate!! :) – Meet K. Jun 20 '14 at 06:20
  • Just so you understand how to make your current code work, take out the frame instance. Just use the class frame. All you `framOne.someMethod()` just change to `someMethod()` and that should get it working – Paul Samsotha Jun 20 '14 at 06:23
  • Okay. What I've learned so far is: **1**. If drawing is required, use the object of a class that extends JPanel. Use JFrame only as a container for panels. **2**. Avoid using getGraphics() as long as you can. **3**. Panels are better usable when used for containing a single component. Am I right? – Meet K. Jun 20 '14 at 08:03
  • Pretty much except 3. Panels can contain as many components you need. It's common to nest panels inside panels with different layout managers. to get a desired layout. See [Here](http://docs.oracle.com/javase/tutorial/uiswing/layout/index.html). But a panel is the main container you want to use for components. Then just add your panels to the frame. – Paul Samsotha Jun 20 '14 at 08:12
  • Okay!! Done!! That was a ride... Thanks for taking the time to clear up everything for me. :) – Meet K. Jun 20 '14 at 10:12