如何摆脱 paint 和 paintImmediately 的不同位置
how to get rid of different locations from paint and paintImmediately
我正在研究任意形状的按钮。第一阶段成功,因为按钮按预期显示。
第二阶段失败,因为从 doClick 调用的 paintImmediately 绘制到一个完全不同的位置。
我尝试了几种使用 AffineTransforms 的方法,但无法计算出正确的位置。
如有任何提示,我们将不胜感激。
我试过的是这个 - 按钮class:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
public class MyCircleButton extends JButton implements MouseListener {
public MyCircleButton(String text, double xCenter, double yCenter, double rOuter, double rInner,
double start, double extend) {
super(text);
this.text = text;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.xOffset = xCenter - rOuter;
this.yOffset = yCenter - rOuter;
this.rOuter = rOuter;
this.rInner = rInner;
this.angStart = start;
this.angSize = extend;
int fontSize = (int) (rOuter * 0.15);
this.font = new Font("Arial", Font.BOLD, fontSize);
this.addMouseListener(this);
calcShape();
calcText();
}
@Override
public boolean contains(int x, int y) {
return shape.contains(x, y);
}
@Override
public void doClick(int pressTime) {
model.setPressed(true);
Component c = SwingUtilities.getRoot(this);
Graphics g = c.getGraphics();
Rectangle r = shape.getBounds();
if (g != null) {
Graphics2D g2 = (Graphics2D) g;
Point p = SwingUtilities.convertPoint(this, new Point(0, 0), getParent());
AffineTransform t = new AffineTransform();
t.setToTranslation(p.getX(), p.getY());
// t.setToTranslation(xText, yText);
// t.setToTranslation(xCenter, yCenter);
// g2.getTransform().concatenate(t);
g2.setTransform(t);
paint(g);
}
try {
Thread.currentThread().sleep(pressTime);
} catch (InterruptedException ie) {
}
model.setPressed(false);
}
@Override
public int getHeight() {
return shape.getBounds().height;
}
@Override
public int getWidth() {
return shape.getBounds().width;
}
@Override
public int getX() {
return shape.getBounds().x;
}
@Override
public int getY() {
return shape.getBounds().y;
}
@Override
public void mouseClicked(MouseEvent me) {
System.out.println("mouse clicked CIRCLE-BUTTON<" + text + "> at " + me.getX() + "/" + me.getY());
doClick(50);
}
@Override
public void mouseEntered(MouseEvent me) {
}
@Override
public void mouseExited(MouseEvent me) {
}
@Override
public void mousePressed(MouseEvent me) {
}
@Override
public void mouseReleased(MouseEvent me) {
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Shape oldClip = g2.getClip();
Paint oldPaint = g2.getPaint();
Stroke oldStroke = g2.getStroke();
Font oldFont = g2.getFont();
FontMetrics fm = g2.getFontMetrics(font);
Rectangle2D tb = fm.getStringBounds(text, g2);
g2.setClip(this.shape);
if (getModel().isArmed() || getModel().isPressed())
g2.setPaint(Color.RED);
else
g2.setPaint(Color.GREEN);
g2.fill(shape);
g2.setStroke(new BasicStroke(4));
g2.setPaint(Color.WHITE);
g2.draw(shape);
g2.setPaint(Color.BLACK);
g2.setFont(font);
g2.drawString(text, (int) (xText + 2 - tb.getWidth() / 2), (int) (yText + 2 + fm.getAscent() * 0.35));
g2.setPaint(Color.WHITE);
g2.drawString(text, (int) (xText - tb.getWidth() / 2), (int) (yText + fm.getAscent() * 0.35));
g2.setFont(oldFont);
g2.setStroke(oldStroke);
g2.setPaint(oldPaint);
g2.setClip(oldClip);
}
protected void calcShape() {
Arc2D a = new Arc2D.Double(xOffset, yOffset, 2 * rOuter, 2 * rOuter, angStart, angSize,
Arc2D.PIE);
Ellipse2D e = new Ellipse2D.Double(xOffset + rOuter - rInner, yOffset + rOuter - rInner,
2 * rInner, 2 * rInner);
Area resShape = new Area(a);
resShape.subtract(new Area(e));
this.shape = resShape;
}
protected void calcText() {
double angle = angStart + angSize / 2;
double r = rInner + (rOuter - rInner) / 2;
double xOff = r * Math.sin(Math.toRadians(90 + angle));
double yOff = r * Math.cos(Math.toRadians(90 + angle));
xText = xCenter + (Math.abs(xOff) > 0.1 ? xOff : 0);
yText = yCenter + (Math.abs(yOff) > 0.1 ? yOff : 0);
}
private double xCenter;
private double yCenter;
private double xOffset;
private double yOffset;
private double rOuter;
private double rInner;
private double angStart;
private double angSize;
private double xText;
private double yText;
private String text;
private Shape shape;
private Font font;
}
和窗格 class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestCirclePane extends JPanel {
public TestCirclePane(int w, int h) {
diameter = (int) (0.9 * (w < h ? w : h));
Dimension s = new Dimension(w, h);
xCenter = w / 2;
yCenter = h / 2;
setMinimumSize(s);
setPreferredSize(s);
setLayout(null);
createComponents(this);
}
@Override
public void paintChildren(Graphics g) {
for (Component c : getComponents()) {
if (c instanceof MyCircleButton)
((MyCircleButton) c).paint(g);
}
}
protected void createComponents(JPanel p) {
double r = diameter / 2;
double w = r * 0.66;
double r3o = r;
double r3i = r - w * 0.5;
add(new MyCircleButton("B0", xCenter, yCenter, r3o, r3i, 135, 90));
add(new MyCircleButton("B1", xCenter, yCenter, r3o, r3i, 45, 90));
add(new MyCircleButton("B2", xCenter, yCenter, r3o, r3i, -45, 90));
add(new MyCircleButton("B3", xCenter, yCenter, r3o, r3i, 225, 90));
}
@Override
protected void paintComponent(Graphics arg0) {
super.paintComponent(arg0);
Graphics2D g2 = (Graphics2D) arg0;
Dimension d = getSize();
Paint oldPaint = g2.getPaint();
g2.setPaint(Color.WHITE);
g2.fill3DRect(0, 0, d.width, d.height, true);
g2.setPaint(Color.LIGHT_GRAY);
g2.drawLine(0, yCenter, d.width, yCenter);
g2.drawLine(xCenter, 0, xCenter, d.height);
g2.setPaint(oldPaint);
}
public static void createWindow() {
JFrame frame = new JFrame("Test - CircleButton");
JPanel client = new JPanel();
client.setLayout(new BorderLayout());
client.add(placeHolder("top"), BorderLayout.NORTH);
client.add(placeHolder("bottom"), BorderLayout.SOUTH);
client.add(placeHolder("left"), BorderLayout.WEST);
client.add(placeHolder("right"), BorderLayout.EAST);
client.add(new TestCirclePane(800, 600), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(client);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createWindow();
}
});
}
public static JComponent placeHolder(String s) {
JLabel l = new JLabel(s);
l.setPreferredSize(new Dimension(300, 300));
return l;
}
private int xCenter;
private int yCenter;
private int diameter;
private static final long serialVersionUID = 1L;
}
不确定这是否有帮助,但这是我的两分钱:
不要覆盖 paintChildren(…)
。 JPanel 将自动重新绘制添加到面板的任何组件。
不要覆盖 paint(…)。自定义绘画是通过覆盖 paintComponent(…)
完成的,你需要调用 super.paintComponent(…)
否则你可能会有绘画工件。
不要覆盖getX(
和getY()
这些方法用于控制组件在面板上的位置。
我猜你也不应该覆盖 getWidth() 和 getHeight()。我猜你应该覆盖 getPreferredSize()
方法来控制组件的大小。
如果您打算使用空布局,那么您的代码负责设置面板上每个组件的size/location。
不要在你的画法上翻译。组件的绘制应始终相对于 (0, 0) 完成。位置决定组件在面板上的位置。
不要在 doClick() 方法中调用 Thread.sleep()。 Thread.sleep() 将导致 EDT 休眠,这意味着 GUI 无法自行重绘。不确定为什么你甚至需要覆盖该方法。
所以这是有效的变体。再次首先是 Button-class:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
public class MyCircleButton extends JButton implements MouseListener {
public MyCircleButton(String text, double xCenter, double yCenter, double rOuter, double rInner,
double start, double extend) {
super(text);
this.xCenter = xCenter;
this.yCenter = yCenter;
this.xOffset = xCenter - rOuter;
this.yOffset = yCenter - rOuter;
this.rOuter = rOuter;
this.rInner = rInner;
this.angStart = start;
this.angSize = extend;
int fontSize = (int) ((rOuter - rInner) * 0.5);
setFont(new Font("Arial", Font.BOLD, fontSize));
this.addMouseListener(this);
setBorderPainted(false);
setContentAreaFilled(false);
calcShape();
calcText();
}
@Override
public boolean contains(int x, int y) {
return shape.contains(x, y);
}
@Override
public int getHeight() {
return shape.getBounds().height;
}
@Override
public int getWidth() {
return shape.getBounds().width;
}
@Override
public int getX() {
return shape.getBounds().x;
}
@Override
public int getY() {
return shape.getBounds().y;
}
@Override
public void mouseClicked(MouseEvent me) {
}
@Override
public void mouseEntered(MouseEvent me) {
}
@Override
public void mouseExited(MouseEvent me) {
}
@Override
public void mousePressed(MouseEvent me) {
model.setArmed(true);
model.setPressed(true);
paintImmediately();
}
@Override
public void mouseReleased(MouseEvent me) {
model.setArmed(false);
model.setPressed(false);
paintImmediately();
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Paint oldPaint = g2.getPaint();
Stroke oldStroke = g2.getStroke();
Font oldFont = g2.getFont();
AffineTransform oldTrans = g2.getTransform();
FontMetrics fm = g2.getFontMetrics(getFont());
Rectangle2D tb = fm.getStringBounds(getText(), g2);
AffineTransform t = new AffineTransform();
t.setToTranslation(-1 * shape.getBounds().getX(), -1 * shape.getBounds().getY());
t.concatenate(oldTrans);
g2.setTransform(t);
if (getModel().isArmed() || getModel().isPressed())
g2.setPaint(Color.RED);
else
g2.setPaint(Color.GREEN);
g2.fill(shape);
g2.setStroke(new BasicStroke(4));
g2.setPaint(Color.WHITE);
g2.draw(shape);
g2.setPaint(Color.BLACK);
g2.setFont(getFont());
g2.drawString(getText(), (int) (xText + 2 - tb.getWidth() / 2),
(int) (yText + 2 + fm.getAscent() * 0.35));
g2.setPaint(Color.WHITE);
g2.drawString(getText(), (int) (xText - tb.getWidth() / 2), (int) (yText + fm.getAscent() * 0.35));
g2.setFont(oldFont);
g2.setStroke(oldStroke);
g2.setPaint(oldPaint);
}
protected void calcShape() {
Arc2D a = new Arc2D.Double(xOffset, yOffset, 2 * rOuter, 2 * rOuter, angStart, angSize,
Arc2D.PIE);
Ellipse2D e = new Ellipse2D.Double(xOffset + rOuter - rInner, yOffset + rOuter - rInner,
2 * rInner, 2 * rInner);
Area resShape = new Area(a);
resShape.subtract(new Area(e));
this.shape = resShape;
}
protected void calcText() {
double angle = angStart + angSize / 2;
double r = rInner + (rOuter - rInner) / 2;
double xOff = r * Math.sin(Math.toRadians(90 + angle));
double yOff = r * Math.cos(Math.toRadians(90 + angle));
xText = xCenter + (Math.abs(xOff) > 0.1 ? xOff : 0);
yText = yCenter + (Math.abs(yOff) > 0.1 ? yOff : 0);
}
protected void paintImmediately() {
Component c = SwingUtilities.getRoot(this);
Graphics g = c.getGraphics();
Graphics2D g2 = (Graphics2D) g;
Point p = SwingUtilities.convertPoint(this, new Point(0, 0), c);
AffineTransform oT = g2.getTransform();
AffineTransform t = new AffineTransform();
t.setToTranslation(p.getX(), p.getY());
g2.setTransform(t);
paintComponent(g);
g2.setTransform(oT);
}
private double xCenter;
private double yCenter;
private double xOffset;
private double yOffset;
private double rOuter;
private double rInner;
private double angStart;
private double angSize;
private double xText;
private double yText;
private Shape shape;
}
现在是窗格 class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestCirclePane extends JPanel {
public TestCirclePane(int w, int h) {
diameter = (int) (0.9 * (w < h ? w : h));
Dimension s = new Dimension(w, h);
xCenter = w / 2;
yCenter = h / 2;
setMinimumSize(s);
setPreferredSize(s);
setLayout(null);
createComponents(this);
}
protected void createComponents(JPanel p) {
double r = diameter / 2;
double w = r * 0.66;
double r3o = r;
double r3i = r - w * 0.5;
add(new MyCircleButton("B0", xCenter, yCenter, r3o, r3i, 135, 90));
add(new MyCircleButton("B1", xCenter, yCenter, r3o, r3i, 45, 90));
add(new MyCircleButton("B2", xCenter, yCenter, r3o, r3i, -45, 90));
add(new MyCircleButton("B3", xCenter, yCenter, r3o, r3i, 225, 90));
}
@Override
protected void paintComponent(Graphics arg0) {
super.paintComponent(arg0);
Graphics2D g2 = (Graphics2D) arg0;
Dimension d = getSize();
Paint oldPaint = g2.getPaint();
g2.setPaint(Color.WHITE);
g2.fill3DRect(0, 0, d.width, d.height, true);
g2.setPaint(Color.LIGHT_GRAY);
g2.drawLine(0, yCenter, d.width, yCenter);
g2.drawLine(xCenter, 0, xCenter, d.height);
g2.setPaint(oldPaint);
}
public static void createWindow() {
JFrame frame = new JFrame("Test - CircleButton");
JPanel client = new JPanel();
client.setLayout(new BorderLayout());
client.add(placeHolder("top"), BorderLayout.NORTH);
client.add(placeHolder("bottom"), BorderLayout.SOUTH);
client.add(placeHolder("left"), BorderLayout.WEST);
client.add(placeHolder("right"), BorderLayout.EAST);
client.add(new TestCirclePane(800, 600), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(client);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createWindow();
}
});
}
public static JComponent placeHolder(String s) {
JLabel l = new JLabel(s);
l.setPreferredSize(new Dimension(300, 300));
return l;
}
private int xCenter;
private int yCenter;
private int diameter;
private static final long serialVersionUID = 1L;
}
由于具有该形状的按钮的行为不像 "normal" 按钮,我需要添加一个鼠标侦听器。为了给点击提供视觉反馈,我使用了 mousePressed()
和 mouseReleased()
。
但是当我的 paintComponent()
从绘画链外部调用时,我意识到它不知道它的(按钮)来源。
所以我想,我的 paintImmediately 是在按钮 paint 概念之外的。因此我准备了环境,就像 superclass 会在正常的绘画操作上做的那样。好吧,我认为 superclass 会做的。不知道 - 只是猜测。
因此,如果您认为我的编码有误,请向我解释,如何做得更好。
我终于学会了=:O
我将形状创建从面板移动到 Layoutmanager 并设置按钮的大小和位置,现在可以按预期工作。
没有重载位置或大小吸气剂,并且没有 paintImmediately :)
感谢您的帮助。
我正在研究任意形状的按钮。第一阶段成功,因为按钮按预期显示。 第二阶段失败,因为从 doClick 调用的 paintImmediately 绘制到一个完全不同的位置。 我尝试了几种使用 AffineTransforms 的方法,但无法计算出正确的位置。
如有任何提示,我们将不胜感激。
我试过的是这个 - 按钮class:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
public class MyCircleButton extends JButton implements MouseListener {
public MyCircleButton(String text, double xCenter, double yCenter, double rOuter, double rInner,
double start, double extend) {
super(text);
this.text = text;
this.xCenter = xCenter;
this.yCenter = yCenter;
this.xOffset = xCenter - rOuter;
this.yOffset = yCenter - rOuter;
this.rOuter = rOuter;
this.rInner = rInner;
this.angStart = start;
this.angSize = extend;
int fontSize = (int) (rOuter * 0.15);
this.font = new Font("Arial", Font.BOLD, fontSize);
this.addMouseListener(this);
calcShape();
calcText();
}
@Override
public boolean contains(int x, int y) {
return shape.contains(x, y);
}
@Override
public void doClick(int pressTime) {
model.setPressed(true);
Component c = SwingUtilities.getRoot(this);
Graphics g = c.getGraphics();
Rectangle r = shape.getBounds();
if (g != null) {
Graphics2D g2 = (Graphics2D) g;
Point p = SwingUtilities.convertPoint(this, new Point(0, 0), getParent());
AffineTransform t = new AffineTransform();
t.setToTranslation(p.getX(), p.getY());
// t.setToTranslation(xText, yText);
// t.setToTranslation(xCenter, yCenter);
// g2.getTransform().concatenate(t);
g2.setTransform(t);
paint(g);
}
try {
Thread.currentThread().sleep(pressTime);
} catch (InterruptedException ie) {
}
model.setPressed(false);
}
@Override
public int getHeight() {
return shape.getBounds().height;
}
@Override
public int getWidth() {
return shape.getBounds().width;
}
@Override
public int getX() {
return shape.getBounds().x;
}
@Override
public int getY() {
return shape.getBounds().y;
}
@Override
public void mouseClicked(MouseEvent me) {
System.out.println("mouse clicked CIRCLE-BUTTON<" + text + "> at " + me.getX() + "/" + me.getY());
doClick(50);
}
@Override
public void mouseEntered(MouseEvent me) {
}
@Override
public void mouseExited(MouseEvent me) {
}
@Override
public void mousePressed(MouseEvent me) {
}
@Override
public void mouseReleased(MouseEvent me) {
}
@Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Shape oldClip = g2.getClip();
Paint oldPaint = g2.getPaint();
Stroke oldStroke = g2.getStroke();
Font oldFont = g2.getFont();
FontMetrics fm = g2.getFontMetrics(font);
Rectangle2D tb = fm.getStringBounds(text, g2);
g2.setClip(this.shape);
if (getModel().isArmed() || getModel().isPressed())
g2.setPaint(Color.RED);
else
g2.setPaint(Color.GREEN);
g2.fill(shape);
g2.setStroke(new BasicStroke(4));
g2.setPaint(Color.WHITE);
g2.draw(shape);
g2.setPaint(Color.BLACK);
g2.setFont(font);
g2.drawString(text, (int) (xText + 2 - tb.getWidth() / 2), (int) (yText + 2 + fm.getAscent() * 0.35));
g2.setPaint(Color.WHITE);
g2.drawString(text, (int) (xText - tb.getWidth() / 2), (int) (yText + fm.getAscent() * 0.35));
g2.setFont(oldFont);
g2.setStroke(oldStroke);
g2.setPaint(oldPaint);
g2.setClip(oldClip);
}
protected void calcShape() {
Arc2D a = new Arc2D.Double(xOffset, yOffset, 2 * rOuter, 2 * rOuter, angStart, angSize,
Arc2D.PIE);
Ellipse2D e = new Ellipse2D.Double(xOffset + rOuter - rInner, yOffset + rOuter - rInner,
2 * rInner, 2 * rInner);
Area resShape = new Area(a);
resShape.subtract(new Area(e));
this.shape = resShape;
}
protected void calcText() {
double angle = angStart + angSize / 2;
double r = rInner + (rOuter - rInner) / 2;
double xOff = r * Math.sin(Math.toRadians(90 + angle));
double yOff = r * Math.cos(Math.toRadians(90 + angle));
xText = xCenter + (Math.abs(xOff) > 0.1 ? xOff : 0);
yText = yCenter + (Math.abs(yOff) > 0.1 ? yOff : 0);
}
private double xCenter;
private double yCenter;
private double xOffset;
private double yOffset;
private double rOuter;
private double rInner;
private double angStart;
private double angSize;
private double xText;
private double yText;
private String text;
private Shape shape;
private Font font;
}
和窗格 class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestCirclePane extends JPanel {
public TestCirclePane(int w, int h) {
diameter = (int) (0.9 * (w < h ? w : h));
Dimension s = new Dimension(w, h);
xCenter = w / 2;
yCenter = h / 2;
setMinimumSize(s);
setPreferredSize(s);
setLayout(null);
createComponents(this);
}
@Override
public void paintChildren(Graphics g) {
for (Component c : getComponents()) {
if (c instanceof MyCircleButton)
((MyCircleButton) c).paint(g);
}
}
protected void createComponents(JPanel p) {
double r = diameter / 2;
double w = r * 0.66;
double r3o = r;
double r3i = r - w * 0.5;
add(new MyCircleButton("B0", xCenter, yCenter, r3o, r3i, 135, 90));
add(new MyCircleButton("B1", xCenter, yCenter, r3o, r3i, 45, 90));
add(new MyCircleButton("B2", xCenter, yCenter, r3o, r3i, -45, 90));
add(new MyCircleButton("B3", xCenter, yCenter, r3o, r3i, 225, 90));
}
@Override
protected void paintComponent(Graphics arg0) {
super.paintComponent(arg0);
Graphics2D g2 = (Graphics2D) arg0;
Dimension d = getSize();
Paint oldPaint = g2.getPaint();
g2.setPaint(Color.WHITE);
g2.fill3DRect(0, 0, d.width, d.height, true);
g2.setPaint(Color.LIGHT_GRAY);
g2.drawLine(0, yCenter, d.width, yCenter);
g2.drawLine(xCenter, 0, xCenter, d.height);
g2.setPaint(oldPaint);
}
public static void createWindow() {
JFrame frame = new JFrame("Test - CircleButton");
JPanel client = new JPanel();
client.setLayout(new BorderLayout());
client.add(placeHolder("top"), BorderLayout.NORTH);
client.add(placeHolder("bottom"), BorderLayout.SOUTH);
client.add(placeHolder("left"), BorderLayout.WEST);
client.add(placeHolder("right"), BorderLayout.EAST);
client.add(new TestCirclePane(800, 600), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(client);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createWindow();
}
});
}
public static JComponent placeHolder(String s) {
JLabel l = new JLabel(s);
l.setPreferredSize(new Dimension(300, 300));
return l;
}
private int xCenter;
private int yCenter;
private int diameter;
private static final long serialVersionUID = 1L;
}
不确定这是否有帮助,但这是我的两分钱:
不要覆盖
paintChildren(…)
。 JPanel 将自动重新绘制添加到面板的任何组件。不要覆盖 paint(…)。自定义绘画是通过覆盖
paintComponent(…)
完成的,你需要调用super.paintComponent(…)
否则你可能会有绘画工件。不要覆盖
getX(
和getY()
这些方法用于控制组件在面板上的位置。我猜你也不应该覆盖 getWidth() 和 getHeight()。我猜你应该覆盖
getPreferredSize()
方法来控制组件的大小。如果您打算使用空布局,那么您的代码负责设置面板上每个组件的size/location。
不要在你的画法上翻译。组件的绘制应始终相对于 (0, 0) 完成。位置决定组件在面板上的位置。
不要在 doClick() 方法中调用 Thread.sleep()。 Thread.sleep() 将导致 EDT 休眠,这意味着 GUI 无法自行重绘。不确定为什么你甚至需要覆盖该方法。
所以这是有效的变体。再次首先是 Button-class:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
public class MyCircleButton extends JButton implements MouseListener {
public MyCircleButton(String text, double xCenter, double yCenter, double rOuter, double rInner,
double start, double extend) {
super(text);
this.xCenter = xCenter;
this.yCenter = yCenter;
this.xOffset = xCenter - rOuter;
this.yOffset = yCenter - rOuter;
this.rOuter = rOuter;
this.rInner = rInner;
this.angStart = start;
this.angSize = extend;
int fontSize = (int) ((rOuter - rInner) * 0.5);
setFont(new Font("Arial", Font.BOLD, fontSize));
this.addMouseListener(this);
setBorderPainted(false);
setContentAreaFilled(false);
calcShape();
calcText();
}
@Override
public boolean contains(int x, int y) {
return shape.contains(x, y);
}
@Override
public int getHeight() {
return shape.getBounds().height;
}
@Override
public int getWidth() {
return shape.getBounds().width;
}
@Override
public int getX() {
return shape.getBounds().x;
}
@Override
public int getY() {
return shape.getBounds().y;
}
@Override
public void mouseClicked(MouseEvent me) {
}
@Override
public void mouseEntered(MouseEvent me) {
}
@Override
public void mouseExited(MouseEvent me) {
}
@Override
public void mousePressed(MouseEvent me) {
model.setArmed(true);
model.setPressed(true);
paintImmediately();
}
@Override
public void mouseReleased(MouseEvent me) {
model.setArmed(false);
model.setPressed(false);
paintImmediately();
}
@Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Paint oldPaint = g2.getPaint();
Stroke oldStroke = g2.getStroke();
Font oldFont = g2.getFont();
AffineTransform oldTrans = g2.getTransform();
FontMetrics fm = g2.getFontMetrics(getFont());
Rectangle2D tb = fm.getStringBounds(getText(), g2);
AffineTransform t = new AffineTransform();
t.setToTranslation(-1 * shape.getBounds().getX(), -1 * shape.getBounds().getY());
t.concatenate(oldTrans);
g2.setTransform(t);
if (getModel().isArmed() || getModel().isPressed())
g2.setPaint(Color.RED);
else
g2.setPaint(Color.GREEN);
g2.fill(shape);
g2.setStroke(new BasicStroke(4));
g2.setPaint(Color.WHITE);
g2.draw(shape);
g2.setPaint(Color.BLACK);
g2.setFont(getFont());
g2.drawString(getText(), (int) (xText + 2 - tb.getWidth() / 2),
(int) (yText + 2 + fm.getAscent() * 0.35));
g2.setPaint(Color.WHITE);
g2.drawString(getText(), (int) (xText - tb.getWidth() / 2), (int) (yText + fm.getAscent() * 0.35));
g2.setFont(oldFont);
g2.setStroke(oldStroke);
g2.setPaint(oldPaint);
}
protected void calcShape() {
Arc2D a = new Arc2D.Double(xOffset, yOffset, 2 * rOuter, 2 * rOuter, angStart, angSize,
Arc2D.PIE);
Ellipse2D e = new Ellipse2D.Double(xOffset + rOuter - rInner, yOffset + rOuter - rInner,
2 * rInner, 2 * rInner);
Area resShape = new Area(a);
resShape.subtract(new Area(e));
this.shape = resShape;
}
protected void calcText() {
double angle = angStart + angSize / 2;
double r = rInner + (rOuter - rInner) / 2;
double xOff = r * Math.sin(Math.toRadians(90 + angle));
double yOff = r * Math.cos(Math.toRadians(90 + angle));
xText = xCenter + (Math.abs(xOff) > 0.1 ? xOff : 0);
yText = yCenter + (Math.abs(yOff) > 0.1 ? yOff : 0);
}
protected void paintImmediately() {
Component c = SwingUtilities.getRoot(this);
Graphics g = c.getGraphics();
Graphics2D g2 = (Graphics2D) g;
Point p = SwingUtilities.convertPoint(this, new Point(0, 0), c);
AffineTransform oT = g2.getTransform();
AffineTransform t = new AffineTransform();
t.setToTranslation(p.getX(), p.getY());
g2.setTransform(t);
paintComponent(g);
g2.setTransform(oT);
}
private double xCenter;
private double yCenter;
private double xOffset;
private double yOffset;
private double rOuter;
private double rInner;
private double angStart;
private double angSize;
private double xText;
private double yText;
private Shape shape;
}
现在是窗格 class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestCirclePane extends JPanel {
public TestCirclePane(int w, int h) {
diameter = (int) (0.9 * (w < h ? w : h));
Dimension s = new Dimension(w, h);
xCenter = w / 2;
yCenter = h / 2;
setMinimumSize(s);
setPreferredSize(s);
setLayout(null);
createComponents(this);
}
protected void createComponents(JPanel p) {
double r = diameter / 2;
double w = r * 0.66;
double r3o = r;
double r3i = r - w * 0.5;
add(new MyCircleButton("B0", xCenter, yCenter, r3o, r3i, 135, 90));
add(new MyCircleButton("B1", xCenter, yCenter, r3o, r3i, 45, 90));
add(new MyCircleButton("B2", xCenter, yCenter, r3o, r3i, -45, 90));
add(new MyCircleButton("B3", xCenter, yCenter, r3o, r3i, 225, 90));
}
@Override
protected void paintComponent(Graphics arg0) {
super.paintComponent(arg0);
Graphics2D g2 = (Graphics2D) arg0;
Dimension d = getSize();
Paint oldPaint = g2.getPaint();
g2.setPaint(Color.WHITE);
g2.fill3DRect(0, 0, d.width, d.height, true);
g2.setPaint(Color.LIGHT_GRAY);
g2.drawLine(0, yCenter, d.width, yCenter);
g2.drawLine(xCenter, 0, xCenter, d.height);
g2.setPaint(oldPaint);
}
public static void createWindow() {
JFrame frame = new JFrame("Test - CircleButton");
JPanel client = new JPanel();
client.setLayout(new BorderLayout());
client.add(placeHolder("top"), BorderLayout.NORTH);
client.add(placeHolder("bottom"), BorderLayout.SOUTH);
client.add(placeHolder("left"), BorderLayout.WEST);
client.add(placeHolder("right"), BorderLayout.EAST);
client.add(new TestCirclePane(800, 600), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(client);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createWindow();
}
});
}
public static JComponent placeHolder(String s) {
JLabel l = new JLabel(s);
l.setPreferredSize(new Dimension(300, 300));
return l;
}
private int xCenter;
private int yCenter;
private int diameter;
private static final long serialVersionUID = 1L;
}
由于具有该形状的按钮的行为不像 "normal" 按钮,我需要添加一个鼠标侦听器。为了给点击提供视觉反馈,我使用了 mousePressed()
和 mouseReleased()
。
但是当我的 paintComponent()
从绘画链外部调用时,我意识到它不知道它的(按钮)来源。
所以我想,我的 paintImmediately 是在按钮 paint 概念之外的。因此我准备了环境,就像 superclass 会在正常的绘画操作上做的那样。好吧,我认为 superclass 会做的。不知道 - 只是猜测。
因此,如果您认为我的编码有误,请向我解释,如何做得更好。
我终于学会了=:O
我将形状创建从面板移动到 Layoutmanager 并设置按钮的大小和位置,现在可以按预期工作。 没有重载位置或大小吸气剂,并且没有 paintImmediately :)
感谢您的帮助。