1

My code is supposed to draw a random sized image three times in random locations. For some reason, when I run this code using BlueJ, all that shows up is a gray screen. I think it is because PaintComponent isn't being called, but I am not quite sure. What went wrong with my code and how can I fix it?

class PanelHolder extends JPanel{//class PanelHolder extends JPanel
//variables
public boolean threesharks;
public int xcoord;
public int ycoord;
public int ratio;
public Image i;
public int w;
public int h;
public boolean background=true;
Color water = new Color(136, 180, 231);


public PanelHolder(){//constructor

    i = Toolkit.getDefaultToolkit().getImage("$harkz.png");

}
public void randomxy(){
    for(int x=0;x<3;x++){
        threesharks=true;
        ycoord=(int)(Math.random()*300+200);//use math.random to figure out coordinates and sizing
        xcoord=(int)(Math.random()*1000+0);//make a loop
        ratio=(int)(Math.random()*5+1);
        w=ratio*523;
        h=ratio*195;
        repaint();
        System.out.println("I'm in randomxy");

        //call repaint() each time
        //after three times, make threesharks=false
    }
    threesharks=false;
}
public void paintComponent(Graphics g){
    if(threesharks){
        setBackground(water);
        System.out.print("hi!");
        if(background){
            super.paintComponent(g);//set backgroun
            background=false;
        }
        g.drawImage(i, xcoord, ycoord, w, h, this);
    }
    }

}

odeng
  • 49
  • 1
  • 9
  • You should always call `super.paintComponent`, you're risking some nasty paint artifactes if your don't – MadProgrammer Sep 17 '15 at 01:42
  • 1) For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). 2) One way to get image(s) for an example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). 3) See [Detection/fix for the hanging close bracket of a code block](http://meta.stackexchange.com/q/251795/155831) for a problem I could no longer be bothered fixing. – Andrew Thompson Sep 17 '15 at 01:43
  • When do you call `randomxy`? – MadProgrammer Sep 17 '15 at 01:43
  • You don't seem to understand how painting works in Swing, I suggest you take a look at [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html) and [Performing Custom Painting](http://docs.oracle.com/javase/tutorial/uiswing/painting/) for starters. Painting is destructive, each time `paintComponent` is called, you are expected to repaint the entire state of your component, from scratch – MadProgrammer Sep 17 '15 at 01:45
  • @MadProgrammer I tried changing the repaint(); to super.paintComponent(), but the compiler gives me an error and says "required:java.awt.Graphics; found: no arguments; reason: actual and formal argument lists differ in length. – odeng Sep 17 '15 at 01:48
  • @MadProgrammer Thanks for the suggestion! I'll go check the page out. Is there any way to draw part of the component instead? – odeng Sep 17 '15 at 01:49
  • @odeng At the beginning of the `paintComponent` method use `super.paintComponent(g)`. – TNT Sep 17 '15 at 01:51
  • Call `super.paintComponent` at the start of your `paintComponent` method. Don't use `setBackground` in your paint methods, this is very dangrous – MadProgrammer Sep 17 '15 at 01:53
  • @TNT If I did that, the background would just paint every time I called the method and it would go over the image. Is there any way I could make it work in the if else block? – odeng Sep 17 '15 at 01:53
  • `the background would just paint every time` - yes, that is what should happen. `it would go over the image.` - no, because THEN you paint the image, so it paints on top of the background. ` I think it is because PaintComponent isn't being called,` - could be. I don't see where you override the `getPreferredSize()` method, so depending on the layout manager you use the size might be 0, which means there is nothing to paint. – camickr Sep 17 '15 at 01:54
  • @MadProgrammer Still nothing. I think the issue is that paintComponent isn't being called, but I can't figure out the problem. – odeng Sep 17 '15 at 01:56
  • @odeng Yes, but you run into the same problem, as you paint method is setup wrong. *"If I did that, the background would just paint every time I called the method and it would go over the image. Is there any way I could make it work in the if else block?"* - Yes, that's how painting works, it's destructive, you are expected to repaint the ENTIRE state of the component each time `paintComponent` is called – MadProgrammer Sep 17 '15 at 01:56
  • @odeng `paintComponent` IS been called, but you've not called `randomxy`, which means `threesharks` is `false` and nothing paints. The fact that you set `threesharks` to `false` at the end of the `randomxy` is also an issue. `repaint` does NOT paint immediatly, it makes a request to the `RepaintManager`, which may schedule a paint event at some time in the future. You DO NOT control the painting process in Swing, you have to work within it – MadProgrammer Sep 17 '15 at 01:58
  • @MadProgrammer Thanks! In that case I think I'll just try to draw the image three times instead of using a loop so I don't have to call repaint. – odeng Sep 17 '15 at 01:59
  • `but I can't figure out the problem.` - You have been given suggestions. If they didn't help then where is the MCVE/SSCCE you were asked to post 30 minutes ago? – camickr Sep 17 '15 at 02:20
  • @camickr Sorry I was in the middle of something. I'm trying all the suggestions right now and I'll get back to you if it works. Thanks for all the suggestions! – odeng Sep 17 '15 at 02:25

1 Answers1

1

You seem to have a misunderstanding with how painting works in Swing. Swing will call your paintComponent when ever it thinks your component needs to be repainted, this might occur for many different reasons, many of which you don't have control over.

Painting in Swing is destructive, that is, each time your paintComponent method is called, you are expected to repaint the entire state of the component from scratch, painting is not accumalitive.

This means that you need to store the state of things you want to paint in some meaningful manner and re-use these values are needed.

Have a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing

Random Ponies

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        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 PanelHolder());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    class PanelHolder extends JPanel {//class PanelHolder extends JPanel
//variables

        public boolean threesharks;
        public BufferedImage i;
        public boolean background = true;
        Color water = new Color(136, 180, 231);

        private Point[] points;
        private Image[] images;

        public PanelHolder() {
            //constructor

            try {
                i = ImageIO.read(...);
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            randomxy();
        }

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

        public void randomxy() {
            points = new Point[3];
            images = new Image[3];
            for (int x = 0; x < 3; x++) {
                points[x] = new Point();

                double ratio = (Math.random() * 6d) + 0.1d;
                int width = (int) (ratio * i.getWidth());
                int height = (int) (ratio * i.getHeight());
                points[x].y = Math.max(0, (int) (Math.random() * 800) - height);//use math.random to figure out coordinates and sizing
                points[x].x = Math.max(0, (int) (Math.random() * 800) - width);//make a loop

                images[x] = i.getScaledInstance(width, height, Image.SCALE_SMOOTH);

            }
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);//set backgroun
            g.setColor(water);
            g.fillRect(0, 0, getWidth(), getHeight());
            if (points != null && images != null) {
                for (int index = 0; index < points.length; index++) {
                    g.drawImage(images[index], points[index].x, points[index].y, this);
                }
            }
        }
    }

}

This is a rough example, which uses Image#getScaledInstance which is not generally recommended, but works for the example.

Have a look at The Perils of Image.getScaledInstance() for more details

Have a look at Quality of Image after resize very low -- Java and Java: maintaining aspect ratio of JPanel background image for possible (scaling) alternatives

I'd also have a look at Reading/Loading an Image for a better mechanism for loading images

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366