问题
I need to draw a ring, with given thickness, that looks something like this:
The center must be transparent, so that it doesn't cover previously drawn shapes. (or other rings) I've tried something like this:
//g is a Graphics2D object
g.setColor(Color.RED);
g.drawOval(x,y,width,height);
g.setColor(Color.WHITE);
g.drawOval(x+thickness,y+thickness,width-2*thickness,height-2*thickness);
which draws a satisfactory ring, but it covers other shapes; the interior is white, not transparent. How can I modify/rewrite my code so that it doesn't do that?
回答1:
You can create an Area from an Ellipse2D that describes the outer circle, and subtract the ellipse that describes the inner circle. This way, you will obtain an actual Shape that can either be drawn or filled (and this will only refer to the area that is actually covered by the ring!).
The advantage is that you really have the geometry of the ring available. This allows you, for example, to check whether the ring shape contains a certain point, or to fill it with a Paint that is more than a single color:
Here is an example, the relevant part is the createRingShape
method:
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RingPaintTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
RingPaintTestPanel p = new RingPaintTestPanel();
f.getContentPane().add(p);
f.setSize(800,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class RingPaintTestPanel extends JPanel
{
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.RED);
g.drawString("Text", 100, 100);
g.drawString("Text", 300, 100);
Shape ring = createRingShape(100, 100, 80, 20);
g.setColor(Color.CYAN);
g.fill(ring);
g.setColor(Color.BLACK);
g.draw(ring);
Shape otherRing = createRingShape(300, 100, 80, 20);
g.setPaint(new GradientPaint(
new Point(250, 40), Color.RED,
new Point(350, 200), Color.GREEN));
g.fill(otherRing);
g.setColor(Color.BLACK);
g.draw(otherRing);
}
private static Shape createRingShape(
double centerX, double centerY, double outerRadius, double thickness)
{
Ellipse2D outer = new Ellipse2D.Double(
centerX - outerRadius,
centerY - outerRadius,
outerRadius + outerRadius,
outerRadius + outerRadius);
Ellipse2D inner = new Ellipse2D.Double(
centerX - outerRadius + thickness,
centerY - outerRadius + thickness,
outerRadius + outerRadius - thickness - thickness,
outerRadius + outerRadius - thickness - thickness);
Area area = new Area(outer);
area.subtract(new Area(inner));
return area;
}
}
回答2:
You can use the Shape
and Area
classes to create interesting effects:
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
import java.net.*;
public class Subtract extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int size = 100;
int thickness = 10;
int innerSize = size - (2 * thickness);
Shape outer = new Ellipse2D.Double(0, 0, size, size);
Shape inner = new Ellipse2D.Double(thickness, thickness, innerSize, innerSize);
Area circle = new Area( outer );
circle.subtract( new Area(inner) );
int x = (getSize().width - size) / 2;
int y = (getSize().height - size) / 2;
g2d.translate(x, y);
g2d.setColor(Color.CYAN);
g2d.fill(circle);
g2d.setColor(Color.BLACK);
g2d.draw(circle);
g2d.dispose();
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Subtract");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Subtract());
frame.setLocationByPlatform( true );
frame.setSize(200, 200);
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Using an Area you can also add multiple Shapes together or get the intersection of multiple Shapes.
回答3:
You could use graphics.setStroke(...)
for this. This way the center will be fully transparent and therefore won't cover previously drawn shapes. In my example I had to do some additional calculations because of this method though, to make sure the displayed x
and y
coordinates are actually the same as the ones of the Ring
instance:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example {
public Example() {
ArrayList<Ring> rings = new ArrayList<Ring>();
rings.add(new Ring(10, 10, 100, 20, Color.CYAN));
JPanel panel = new JPanel() {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g.create();
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ring ring : rings) {
// Previously drawn
gg.setColor(Color.BLACK);
String str = "Hello!";
gg.drawString(str, ring.getX() + (ring.getWidth() - gg.getFontMetrics().stringWidth(str)) / 2,
ring.getY() + ring.getHeight() / 2 + gg.getFontMetrics().getAscent());
// The actual ring
ring.draw(gg);
}
gg.dispose();
}
};
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new Example();
}
});
}
}
class Ring {
private int x, y, width, height, thickness;
private Color color;
public Ring(int x, int y, int width, int height, int thickness, Color color) {
setX(x);
setY(y);
setWidth(width);
setHeight(height);
setThickness(thickness);
setColor(color);
}
public Ring(int x, int y, int radius, int thickness, Color color) {
this(x, y, radius * 2, radius * 2, thickness, color);
}
public void draw(Graphics2D gg) {
Stroke oldStroke = gg.getStroke();
Color oldColor = gg.getColor();
gg.setColor(Color.BLACK);
gg.setStroke(new BasicStroke(getThickness()));
gg.drawOval(getX() + getThickness() / 2, getY() + getThickness() / 2, getWidth() - getThickness(),
getHeight() - getThickness());
gg.setColor(getColor());
gg.setStroke(new BasicStroke(getThickness() - 2));
gg.drawOval(getX() + getThickness() / 2, getY() + getThickness() / 2, getWidth() - getThickness(),
getHeight() - getThickness());
gg.setStroke(oldStroke);
gg.setColor(oldColor);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getThickness() {
return thickness;
}
public void setThickness(int thickness) {
this.thickness = thickness;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
}
来源:https://stackoverflow.com/questions/35524394/draw-ring-with-given-thickness-position-and-radius-java2d