Reputation: 30990
Item is a simple model class.
ItemComponent is a view for an Item which just draws simple rectangles in a given spot. A bunch of ItemComponent instances are put into a parent component that is added to the JFrame of the application (just a simple shell right now).
The view has two different display styles. I want to adjust some properties of the model, and possibly change the state (which controls the style), and then call update() to repaint.
The problem is, as far as I can tell... paint() is only EVER called once. repaint() seems to have no effect.
What's wrong?
I'm not a Swing programmer and cobbled this together from examples, so I expect it may be something trivial here I don't understand.
public class ItemComponent extends JComponent implements ItemView {
private static final Color COLOR_FILL_NORMAL = new Color(0x008080ff);
private static final Color COLOR_FILL_TARGET = Color.LIGHT_GRAY;
private static final Color COLOR_OUTLINE = new Color(0x00333333);
Item item;
RoundRectangle2D rect;
State state = State.NORMAL;
float alpha = 1.0f;
public ItemComponent(Item item) {
this.item = item;
this.rect = new RoundRectangle2D.Double(0, 0, 0, 0, 5, 5);
item.setView(this);
}
public void setState(State state) {
this.state = state;
}
public void update() {
System.out.println("ItemComponent.update");
setLocation(item.getLeft(), 1);
setSize(item.getWidth(), getParent().getHeight()-1);
rect.setRoundRect(0, 0, getWidth()-1, getHeight()-1, 5, 5);
repaint();
//paintImmediately(getBounds());
}
@Override
public void addNotify() {
update();
}
@Override
public void paint(Graphics g) {
System.out.println("paint");
Graphics2D g2 = (Graphics2D)g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
if (state == State.NORMAL) {
System.out.println("draw normal");
g2.setPaint(COLOR_FILL_NORMAL); // light blue
g2.fill(rect);
g2.setPaint(COLOR_OUTLINE);
g2.draw(rect);
}
else if (state == State.TARGET) {
System.out.println("draw target");
g2.setPaint(COLOR_FILL_TARGET);
g2.fill(rect);
float[] dashPattern = { 8, 5 };
g2.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, dashPattern, 0));
g.setColor(COLOR_OUTLINE);
g2.draw(rect);
}
}
}
Hint: I traced into repaint() and found a point where isDisplayable() was being checked, and it's returning false. It makes sure that getPeer() != null.
So my component has no peer? What's up with that? It's been added to a container which itself is added to the rest of the app. And it gets painted once, so I know it's visible.
Upvotes: 1
Views: 4094
Reputation:
You would want to call repaint()
to cause your JComponent
to repaint itself instead of update.
Rather than overriding paint( Graphics g )
, instead override paintComponent( Graphics g )
and place your custom rendering code within.
Make sure to call super.paintComponent( g )
as the first line of the overridden method because some key initialization happens.
Also important to note is that depending on the LayoutManager of the container your ItemComponent is being added to, it may be necessary to set the size explicitly. I see you are attempting to do that in the update() method. You should only need to set the size once, perhaps best placed in the code that constructs your component and adds it to a container.
For what you are trying to do, it should only be necessary to override paintComponent(). You should remove all other overridden methods.
Call repaint()
from setState()
. If the size of the component depends on the state passed in, you may want to call setSize()
just before repaint()
.
Hope that helps.
Upvotes: 0
Reputation: 30990
My apologies to people who looked at this already. I trimmed down the code a bit for posting and inadvertently left out a key part:
@Override
public void addNotify() {
update();
}
I was using this to do some setup as soon as it's been added. Turns out it's pretty critical that you don't override this, or at least you need to call super.addNotify(), or a whole bunch of important initialization doesn't happen.
Changing it to this fixed the problem:
@Override
public void addNotify() {
super.addNotify();
update();
}
Upvotes: 2
Reputation: 22272
The answer is somewhere here. Particularly the section on Painting in Swing. I'm puzzled that moving the rendering code into paintComponent() didn't work as suggested by Johannes Rössel. As the documentation there says..
The Paint Methods
The rules that apply to AWT's lightweight components also apply to Swing components -- for instance, paint() gets called when it's time to render -- except that Swing further factors the paint() call into three separate methods, which are invoked in the following order:
protected void paintComponent(Graphics g) protected void paintBorder(Graphics g) protected void paintChildren(Graphics g)
Swing programs should override paintComponent() instead of overriding paint().
Upvotes: 1