MadProgrammer made a comment here stating that you should NEVER EVER use Component.getGraphics
, which I generally agree with: In virtually all situations, it is wrong.
But take the case of this class I created years ago. I'm posting what I think is the relevant code. You can find the full code here
public class MouseDragOutliner extends MouseAdapter implements MouseMotionListener {
private class MyRunnable implements Runnable {
public void run() {
Graphics g = mComponent.getGraphics();
if (g == null) {
return;
}
Graphics2D g2 = (Graphics2D) g;
Stroke s = g2.getStroke();
g2.setStroke(DASH_STROKE);
int x = Math.min(mStart.x, mEnd.x);
int y = Math.min(mStart.y, mEnd.y);
int w = Math.abs(mEnd.x - mStart.x);
int h = Math.abs(mEnd.y - mStart.y);
if (w == 0 || h == 0) return;
g2.setXORMode(Color.WHITE);
if (mCustomShape != null) {
Rectangle r = mCustomShape.getBounds();
AffineTransform scale = AffineTransform.getScaleInstance(w / (double)r.width, h / (double)r.height);
AffineTransform trans = AffineTransform.getTranslateInstance(x - r.x, y-r.y);
g2.transform(trans);
g2.transform(scale);
g2.draw(mCustomShape);
} else {
if (mShape == RECTANGLE) g2.drawRect(x, y, w, h);
else if (mShape == OVAL) g2.drawOval(x, y, w, h);
else if (mShape == LINE) g2.drawLine(mStart.x, mStart.y, mEnd.x, mEnd.y);
}
g2.setStroke(s);
}
}
public void doMouseDragged(MouseEvent me) {
mEnd = me.getPoint();
if (mStart != null) {
mComponent = me.getComponent();
mComponent.repaint();
SwingUtilities.invokeLater(mRunner);
}
}
}
doMouseDragged
is called from both mouseDragged
and mouseMoved
(when configured to). In case it's not obvious, what it does is call repaint
on the component (so the component is ready to paint on), then run the mRunner
after. I've used this in a number of situations quite successfully, with the benefits being that the component that the MouseDragOutliner
doesn't need any customization (in paintComponent
, specifically) to accomodate.
If it is truly never okay to call Component.getGraphics
, can someone suggest a better solution for this?
Thanks!
http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/drag/MouseDragOutliner.java#l66
I never like to say never. I think you just need to be aware of the drawbacks when using an approach that is not recommended.
The main point about getGraphics()
is that the painting is only temporary. Whenever Swing determines the component needs to be repainted you will lose the custom painting. For example, try creating your outline and then minimize or maximize the frame and the outline will be lost. If you use Alt+Tab to switch application in Windows you will also lose the painting.
I agree in this case it is not a high probability the user will do something like that, but it may cause problems and result in a bug report. So you need to decide if you care about this problem or not. Who knows, maybe this is a requirement so it is not a big deal?
As suggested by @kiheru you can probably use the JLayer class. I don't think JLayer was part of the API when you created your code. I am not that familiar with it but the Swing tutorial has a section on Responding to Events when using the JLayer class, which seems to indicate you can do what you want.
When using a JLayer you can't dynamically add the functionality to any component (the way your code does). You need to add the functionality at design time, but I don't think that should be a problem.