initramfs
initramfs

Reputation: 8405

JPanel inside JScrollPane Painting Issue

I have placed a JPanel object inside a JScrollPane and scrolling works as expected. By overriding paintComponent() I have attempted to do custom painting within the JPanel object. However when the JPanel object is placed within the JScrollPane the JPanel no longer paints properly (instead shows only its background color).

Because my application demands the JPanel to be updated constantly, a separate thread is constructed to repaint the JPanel at a specific interval.

The following excerpts of code show my current project:

a) paintComponent() from my JPanel (This method has been cut down to only the painting, the actual paint will be a constantly updating BufferedImage supplied from another thread instead of this big pink static box):

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);

    //Render Frame
    // 'RXDisplayCanvas' is the JPanel.
    Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();

    G2D.setColor(Color.PINK);
    //800 and 600 are arbitrary values for this example, real values are calculated at runtime. The value calculation code is verified to work (as its used elsewhere in a similar scenario)
    G2D.fillRect(0, 0, 800, 600);
    G2D.dispose();
}

b) The 'updater' thread that periodically repaints the frame:

@Override
public void run() {
    long MaxFrameTime;
    long Time;

    while(isVisible()){
        // 'FPSLimit' is a integer value (default to 30)
        MaxFrameTime = Math.round(1000000000.0 / FPSLimit);
        Time = System.nanoTime();

        try{
            SwingUtilities.invokeAndWait(new Runnable(){
                @Override
                public void run() {
                    // 'RXDisplayCanvas' is the JPanel.
                    RXDisplayCanvas.repaint(); //When using this, the JPanel does not display correctly.

                    //RXDisplayCanvas.paintImmediately(0, 0, RXDisplayCanvas.getWidth(), RXDisplayCanvas.getHeight()); When using this, the JPanel renders correctly but flickers.
                }
            });
        }catch(InterruptedException | InvocationTargetException e){}

        Time = System.nanoTime() - Time;
        if(Time < MaxFrameTime){
            try{
                Thread.sleep(Math.round((MaxFrameTime - Time)/1000000.0));
            }catch(InterruptedException ex){}
        }
    }
}

I have taken into account that repaint() does not cause a immediate repainting of the screen but the issue lies with the incorrect rendering of the screen. When the program is left alone, it merely renders the background color of the JPanel until the JScrollPane is scrolled in which it renders correctly for one frame before the next repaint() call paints the incorrect display.

When switching repaint() out for paintImmediately() (in excerpt b) the frame rendered correctly but heavy flickering was present where it constantly alternated between painting the background color and painting the pink box. I have tried adding and removing layout managers, disabling the repaint manager as well enabling and disabling the 'double buffered' flag for both components in which all resulted in one of the two behaviors mentioned above (rendering only background or flickering).

Can anyone aid me on this issue?

N.B: I am well aware of Java's variable naming convention, since this is a private project I am choosing to start variable names with capital letters because I think it looks nicer, please do not post comments regarding this.

Upvotes: 1

Views: 1335

Answers (1)

David Kroukamp
David Kroukamp

Reputation: 36423

1) I am not sure about this:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    // 'RXDisplayCanvas' is the JPanel.
    Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();
    ..
    G2D.dispose();
}

I would recommend doing:

public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D G2D = (Graphics2D)g;
    G2D.setColor(Color.PINK);
    G2D.fillRect(0, 0, 800, 600);
}

Note how I have omitted the getGraphics, and use the current passed in graphic context of paintComponent.

Also note I do not call g2d.dipose() as this causes problem it should only be done on Graphics that you create Component.getGraphics() but in your case you shouldn't even be creating the Graphics context as it already has been created and passed in to the paintComponent method. (see this similar question)

2) No need for SwingUtilities.invokeXXX block for repaint() as it is thread safe. But especially no need for SwingUtilities.invokeAndWait (as this is a blocking call and wait until all pending AWT events gets processed and run() method completes) which is not good and may also be adding to the onscreen visual artefacts you see.

3) I have tried adding and removing layout managers, disabling the repaint manager as well enabling and disabling the 'double buffered' flag for both components in which all resulted in one of the two behaviours mentioned above (rendering only background or flickering). undo all that as I cannot see how that affected the painting.

It would be even more helpful if I had an SSCCE which illustrates the unwanted behaviour. As I could attempt to reproduce your error but I most likely wont be able to (due to the specific conditions that apply to your app that may be causing these visual artefacts)

Upvotes: 7

Related Questions