Swing是基于AWT的。因此最初的限制基于 AWT 如何绘制它的组件。由于 Swing 是轻量级的,因此当绘制组件根组件时,需要通知子组件 Swing 组件它们也需要更新。这是通过调用来完成的paint
受更新影响的所有子组件的方法(依次调用paintComponent
方法)。
关于 Swing API 绘制链的许多决策都是基于定制的概念。它降低了所涉及的复杂性(通过阻止覆盖paint
并将功能集中到paintComponent
方法)。
Look and Feel API 基于“委托”模型。这意味着用于执行所述操作的功能被“委托”给某个其他对象。这意味着 UI 委托 UI 实际上并不知道组件何时“需要”绘制,而是由组件告诉它需要绘制。这使得它更加灵活,并且在许多情况下更容易定制。
根据您的上一个问题 https://stackoverflow.com/questions/47145979/custom-jtooglebutton-shape-but-with-default-colors一个习俗ButtonUI
可能是更好的选择之一,这样您就可以更好地控制按钮的绘制方式。
尝试让按钮遵循当前的外观和感觉配色方案将非常困难,但您可以尝试看看src.jar
它与 JDK 一起安装,其中包括许多外观和感觉的实现(如果您在 Windows 上,您也应该获得 Windows 外观和感觉,如果您在 Mac 上,那么您不会获得 Mac 或 Windows ????)
我首先看了一下BasicButtonUI
and ButtonUI
以便更好地了解这些属性。我把一些更有趣的方法拉到了自定义中ShapeButtonUI
...
public class ShapeButtonUI extends BasicButtonUI {
private Shape shape;
public ShapeButtonUI(Shape shape) {
this.shape = shape;
}
protected Color getSelectColor() {
return UIManager.getColor(getPropertyPrefix() + "select");
}
protected Color getDisabledTextColor() {
return UIManager.getColor(getPropertyPrefix()
+ "disabledText");
}
protected Color getFocusColor() {
return UIManager.getColor(getPropertyPrefix() + "focus");
}
@Override
protected void installDefaults(AbstractButton b) {
super.installDefaults(b);
}
@Override
protected void uninstallDefaults(AbstractButton b) {
super.uninstallDefaults(b);
}
@Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setClip(shape);
Rectangle bounds = shape.getBounds();
LinearGradientPaint lgp = new LinearGradientPaint(
new Point(bounds.x, bounds.y),
new Point(bounds.x, bounds.y + bounds.height),
new float[]{0, 1},
new Color[]{c.getBackground().brighter(), c.getBackground().darker()});
g2d.setPaint(lgp);
g2d.fill(shape);
g2d.dispose();
g2d = (Graphics2D) g.create();
g2d.setColor(c.getForeground());
g2d.draw(shape);
g2d.dispose();
super.paint(g, c);
}
@Override
protected void paintButtonPressed(Graphics g, AbstractButton b) {
super.paintButtonPressed(g, b);
}
@Override
protected void paintFocus(Graphics g, AbstractButton b,
Rectangle viewRect, Rectangle textRect, Rectangle iconRect) {
super.paintFocus(g, b, viewRect, textRect, iconRect);
}
@Override
protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
super.paintText(g, b, textRect, text);
// ButtonModel model = b.getModel();
// FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
// int mnemIndex = b.getDisplayedMnemonicIndex();
//
// /* Draw the Text */
// if (model.isEnabled()) {
// /**
// * * paint the text normally
// */
// g.setColor(b.getForeground());
// } else {
// /**
// * * paint the text disabled **
// */
// g.setColor(getDisabledTextColor());
// }
// SwingUtilities2.drawStringUnderlineCharAt(c, g, text, mnemIndex,
// textRect.x, textRect.y + fm.getAscent());
}
@Override
public Dimension getMinimumSize(JComponent c) {
Rectangle bounds = shape.getBounds();
return new Dimension(bounds.x + bounds.width + 1, bounds.y + bounds.height + 1);
}
@Override
public Dimension getPreferredSize(JComponent c) {
Rectangle bounds = shape.getBounds();
return new Dimension(bounds.x + bounds.width + 1, bounds.y + bounds.height + 1);
}
@Override
public Dimension getMaximumSize(JComponent c) {
Rectangle bounds = shape.getBounds();
return new Dimension(bounds.x + bounds.width + 1, bounds.y + bounds.height + 1);
}
}
其中大多数您可能不需要担心,但您至少应该知道它们存在,因为您稍后可能想要自定义一些其他属性/功能。
该委托旨在根据需要安装在按钮上,而不是将其安装为按钮的默认 UI 委托。这样做的原因是需要形状对象。如果您愿意,这允许每个按钮都有自己的形状。
您可以将单个形状植入UIManager
的属性,并使用它来代替,但我并没有为这个例子而烦恼。
然后我创建了自己的形状/路径......
public class PointerPath extends Path2D.Double {
public PointerPath() {
moveTo(1, 1);
lineTo(150, 1);
lineTo(198, 100);
lineTo(150, 198);
lineTo(1, 198);
lineTo(50, 100);
closePath();
}
}
并将其应用到按钮上......
ShapeButtonUI shapeUI = new ShapeButtonUI(new PointerPath());
JButton btn = new JButton("That way");
btn.setUI(shapeUI);
最终产生了类似的东西......
现在,这是一个非常基本的示例,实际上,按钮应该围绕文本调整自身大小(带有一些附加的填充/边距信息),但这需要多部分形状,因此我们知道哪些部分可以调整大小,哪些部分可以调整大小什么方向,所以,复杂。