Custom paint in JScrollPane

T

Thomas Richter

Hi folks,

is there a certain rule for making up components when overloading the
"paint" method of a JPanel? I've having here a class that does this:

class ImgPanel extends JPanel {
...
public ImgPanel(Tree t) // no matter what this is right now
{
super(false);
int w,h;
tree = t;
w = tree.getWidth();
h = tree.getHeight();
dim = new Dimension(w,h);
setSize(w,h);
setPreferredSize(dim);
setOpaque(false);
}

public void paintComponent(Graphics g)
{
tree.draw(g,0,0,this);
}
}

Now, everything is fine as long as I add this to the top level content
pane of a JFrame - it's rendered correctly. However, as soon as I add
this into a JScrollPane, trouble begins. The image gets rendered, but
something then erases the image again with the default grey background,
writing on top of my rendered image. I've already tried to set the
JScrollPane to non-opaque, but no go. Any idea what's going on here?

So long,
Thomas
 
A

Andrew Thompson

class ImgPanel extends JPanel {
...
public ImgPanel(Tree t) // no matter what this is right now

What is a 'Tree'? (And why are you so sure it is 'right now'?)
 
B

Babu Kalakrishnan

Thomas said:
Hi folks,

is there a certain rule for making up components when overloading the
"paint" method of a JPanel? I've having here a class that does this:

class ImgPanel extends JPanel {
...
public ImgPanel(Tree t) // no matter what this is right now
{
super(false);
int w,h;
tree = t;
w = tree.getWidth();
h = tree.getHeight();
dim = new Dimension(w,h);
setSize(w,h);
^^^^^^^^^^^^
The above code is superfluous if you're adding your component to any
container with a LayoutManager present.
setPreferredSize(dim);
setOpaque(false);
}

public void paintComponent(Graphics g)
^^^^^^ -> "protected" is better since you don't want anyone

calling it directly anyway
{
tree.draw(g,0,0,this);
}
}

Now, everything is fine as long as I add this to the top level content
pane of a JFrame - it's rendered correctly. However, as soon as I add
this into a JScrollPane, trouble begins. The image gets rendered, but
something then erases the image again with the default grey background,
writing on top of my rendered image. I've already tried to set the
JScrollPane to non-opaque, but no go. Any idea what's going on here?

I would bet it has something to do with what you're doing in the draw
method of your Tree class. (And also the fact that your draw method
should require a reference to the ImgPanel in addition to the Graphics
context makes me very suspicious that you're doing something naughty
with the code in there :))


BK
 
T

Thomas Richter

Hi,
What is a 'Tree'? (And why are you so sure it is 'right now'?)

Nothing that is really important right here. All it does is basically:

g.drawImage(bi,x,y,io);

where g is the Graphics object (from above),
bi is a buffered image,
x and y are coordinates (here always 0,0)
and io is the image observer.

Nothing else happens in there. A tree is a collection of renderable
buffered images that are drawn in a nice way, but as far as graphics
manipulations are concerned, the above line is pretty much all of it.

So long,
Thomas
 
T

Thomas Richter

Hi,
^^^^^^^^^^^^
The above code is superfluous if you're adding your component to any
container with a LayoutManager present.

In fact, removing or adding it didn't make any difference. It was added
here because I tried whether the problem would be layout related.
I would bet it has something to do with what you're doing in the draw
method of your Tree class.

Nothing, really: Basically, it is this line:

g.drawImage(bi,x,y,io);

where g is the graphics object (unaltered from above),
bi is a buffered image (yes, this one is filled correctly)
x and y are the anchor coordinates and here always 0,0,
io is the image observer, also used unaltered from the above:

This is the full code:

public void draw(Graphics g,int x,int y,ImageObserver io)
{
g.drawImage(source.getImage(),x,y,io);
}

source.getImage() returns a buffered image. Given the fact that
it works fine without the JScrollPane and I haven't altered any
code related to this "Tree" class, I'd say that the BufferedImage
is fine.
(And also the fact that your draw method
should require a reference to the ImgPanel in addition to the Graphics
context

Why should it? I don't understand, please explain. The graphics context
should be the graphics of the panel I'm part of, thus rendering into it
should render the graphics in the right spot. Which other graphics context
should I render into instead?
makes me very suspicious that you're doing something naughty
with the code in there :))

If so, please enlighten me. I don't get it, sorry.

So long,
Thomas
 
A

Andrew Thompson

Hi,


Nothing that is really important right here. All it does is basically:

g.drawImage(bi,x,y,io);

where g is the Graphics object (from above),

Where?

Inside paint(Graphics g)? paintComponent(Graphics g)?
myLeftFoot(Graphics g)?

Provide an *exact* example[1] of what you are doing,
and the experts (like Babu) will probably spot your
error immediately.

[1] <http://www.physci.org/codes/sscce.jsp>
 
C

Chris Uppal

Thomas said:
However, as soon as I add
this into a JScrollPane, trouble begins. The image gets rendered, but
something then erases the image again with the default grey background,
writing on top of my rendered image.

How are you adding it to the JScrollPane ? I had a quick try at reproducing
this, but it worked perfectly for me. I'll append the test code in case it's
any use for comparison.

-- chris

==============

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

class ImagePanel
extends JPanel
{
private final Tree m_tree;

public ImagePanel(Tree tree)
{
super(false);
m_tree = tree;
int w = tree.getWidth();
int h = tree.getHeight();
setSize(w, h);
setPreferredSize(new Dimension(w, h));
setOpaque(false);
}

public void
paintComponent(Graphics g)
{
m_tree.draw((Graphics2D)g, 0, 0, this);
}
}


class Tree
{
private final int m_width, m_height;

Tree(int width, int height)
{
m_width = width;
m_height = height;
}

public int getWidth() { return m_width; }
public int getHeight() { return m_height; }

public void
draw(Graphics2D g, int x, int y, Object thing)
{
Polygon poly = new Polygon();
poly.addPoint(x + m_width / 2, y);
poly.addPoint(m_width, y + m_height);
poly.addPoint(x, y + m_height);
g.fill(poly);
}
}


public class Test
{
public static void
main(String[] args)
{
Tree tree = new Tree(600, 800);

JFrame frame = new JFrame("Test...");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JComponent image = new ImagePanel(tree);

JScrollPane scroller = new JScrollPane(image);
frame.getContentPane().add(scroller);

frame.pack();
frame.setVisible(true);
}
 
T

Thomas Richter

Where?

Inside tree.draw() obviously. This is the only thing called in this example.
"Tree" doesn't have any kind of paint() method, nor is it a Component,
JComponent or any other type of GUI element. It is just able to render
a graphics:


public void draw(Graphics g,int x,int y,ImageObserver io)
{
g.drawImage(source.getImage(),x,y,io);
}

source.getImage() is a BufferedImage. This image is correct and not
the cause of the lack of rendering.

So long,
Thomas
 
B

Babu Kalakrishnan

Thomas said:
This is the full code:

public void draw(Graphics g,int x,int y,ImageObserver io)
{
g.drawImage(source.getImage(),x,y,io);
}

source.getImage() returns a buffered image. Given the fact that
it works fine without the JScrollPane and I haven't altered any
code related to this "Tree" class, I'd say that the BufferedImage
is fine.

What happens if source.getImage() gets called repeatedly ? Does it
try to recreate the image everytime or does it return a cached copy ?
Why should it? I don't understand, please explain. The graphics context
should be the graphics of the panel I'm part of, thus rendering into it
should render the graphics in the right spot. Which other graphics context
should I render into instead?

As long as it is only for the purpose of acting as an ImageObserver, it
is fine. I was wondering if it was being used to change the state of the
component in some manner.
If so, please enlighten me. I don't get it, sorry.

See above. I withdraw the "naughty" remark :)

BK
 
T

Thomas Richter

Hi Chris,
How are you adding it to the JScrollPane ?

Well, JScrollPane.add(myclass). I suppose this is incorrect then and I
should rather build another JPanel, add the class to this JPanel and then
put the JPanel into the JScrollPane on construction as you did?

Thanks for the example. I'll have to check whether this helps.

So long,
Thomas
 
T

Thomas Richter

Hi,
What happens if source.getImage() gets called repeatedly ? Does it
try to recreate the image everytime or does it return a cached copy ?

No, it returns a cached copy. Recreating the image could be rather
wasteful. It is not very huge in the demo code, but it could become
quite large.
As long as it is only for the purpose of acting as an ImageObserver, it
is fine. I was wondering if it was being used to change the state of the
component in some manner.

I hope this is maybe not related, but could you please explain me what's
this image observer is good for in first place? (Ehem...)
See above. I withdraw the "naughty" remark :)

Accepted. (-:

So long,
Thomas
 
C

Chris Uppal

Thomas said:
Well, JScrollPane.add(myclass). I suppose this is incorrect then and I
should rather build another JPanel, add the class to this JPanel and then
put the JPanel into the JScrollPane on construction as you did?

If you want to "add" the subview later, then you should use
JScrollPane.setViewportView() not add(). (Because a JScrollPane is just
assemblage of a few scrollbar components and a Viewport component, plus some
glue to tie them together, and it's really the Viewport that needs to "contain"
the thing you're adding, not the JScrollPane itself). The documentation for
JScrollPane isn't a masterpiece of clarity, but if you read it a few times then
it should make more sense.

-- chris
 
B

Babu Kalakrishnan

Thomas said:
No, it returns a cached copy. Recreating the image could be rather
wasteful. It is not very huge in the demo code, but it could become
quite large.

I asked because trying to recreate it can lead to problems in painting.
I hope this is maybe not related, but could you please explain me what's
this image observer is good for in first place? (Ehem...)

I think it isn't related at all. Your problem is most likely what Chris
(Uppal) pointed out. "add"ing to the JScrollPane is incorrect. You need
to set the client panel as the "view" of its Viewport.

Look at the API documentation of the java.awt.image.ImageObserver
interface and that of the drawImage method of the Graphics class for
details on qhat an imageObserver is used for. The java.awt.Component
class implementats this interface, and all that the implementation does
is to trigger a repaint() on itself as more pixels arrive (in its
imageUpdate method).

BK
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top