打电话给 Component.getGraphics 真的不行吗?
Is it truly never okay to call Component.getGraphics?
MadProgrammer 发表评论 指出你永远不应该使用 Component.getGraphics,我通常同意这一点:在几乎所有情况下,它都是错误的。
但以我多年前创建的 class 为例。我正在发布我认为是相关代码的内容。你可以找到完整的代码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 从 mouseDragged 和 mouseMoved(当配置为)调用。如果不是很明显,它所做的是在组件上调用重绘(因此组件已准备好在其上绘制),然后 运行 之后的 mRunner。我已经在许多情况下非常成功地使用了它,其好处是 MouseDragOutliner 不需要任何自定义(特别是在 paintComponent 中)来容纳的组件。
如果调用 Component.getGraphics 真的永远不可能,有人可以为此提出更好的解决方案吗?
谢谢!
http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/drag/MouseDragOutliner.java#l66
我从来不喜欢说永远。我认为您只需要了解使用不推荐的方法时的缺点。
关于getGrapahics()
的要点是,这幅画只是暂时的。每当 Swing 确定组件需要重新绘制时,您将丢失自定义绘制。例如,尝试创建轮廓,然后最小化或最大化框架,轮廓将丢失。如果你在Windows中使用Alt+Tab切换应用程序你也会丢掉这幅画。
我同意在这种情况下,用户做那样的事情的可能性不大,但它可能会导致问题并导致错误报告。所以你需要决定你是否关心这个问题。谁知道,也许这是一项要求,所以没什么大不了的?
正如@kiheru 所建议的,您或许可以使用 JLayer class。当您创建代码时,我认为 JLayer 不是 API 的一部分。我不太熟悉它,但是 Swing 教程中有一段关于使用 JLayer class 时的 Responding to Events,这似乎表明你可以做你想做的事。
使用 JLayer 时,您无法将功能动态添加到任何组件(您的代码的方式)。您需要在设计时添加功能,但我认为这不是问题。
MadProgrammer 发表评论
但以我多年前创建的 class 为例。我正在发布我认为是相关代码的内容。你可以找到完整的代码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 从 mouseDragged 和 mouseMoved(当配置为)调用。如果不是很明显,它所做的是在组件上调用重绘(因此组件已准备好在其上绘制),然后 运行 之后的 mRunner。我已经在许多情况下非常成功地使用了它,其好处是 MouseDragOutliner 不需要任何自定义(特别是在 paintComponent 中)来容纳的组件。
如果调用 Component.getGraphics 真的永远不可能,有人可以为此提出更好的解决方案吗?
谢谢!
http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/drag/MouseDragOutliner.java#l66
我从来不喜欢说永远。我认为您只需要了解使用不推荐的方法时的缺点。
关于getGrapahics()
的要点是,这幅画只是暂时的。每当 Swing 确定组件需要重新绘制时,您将丢失自定义绘制。例如,尝试创建轮廓,然后最小化或最大化框架,轮廓将丢失。如果你在Windows中使用Alt+Tab切换应用程序你也会丢掉这幅画。
我同意在这种情况下,用户做那样的事情的可能性不大,但它可能会导致问题并导致错误报告。所以你需要决定你是否关心这个问题。谁知道,也许这是一项要求,所以没什么大不了的?
正如@kiheru 所建议的,您或许可以使用 JLayer class。当您创建代码时,我认为 JLayer 不是 API 的一部分。我不太熟悉它,但是 Swing 教程中有一段关于使用 JLayer class 时的 Responding to Events,这似乎表明你可以做你想做的事。
使用 JLayer 时,您无法将功能动态添加到任何组件(您的代码的方式)。您需要在设计时添加功能,但我认为这不是问题。