为什么.paintComponent()是在JComponent上定义的?

很抱歉,如果我的问题没有适应Stackoverflow的要求,因为它是理论上但我不知道还能在哪里问。

在过去的几周里,我一直在努力更好地了解Swing API的工作原理以及它的组件,以便创建我自己的自定义组件。 我已经阅读了很多教程,在这里搜索,我在Java的摇摆源代码中非常深入,坦率地说……我的思绪很混乱。

据我所知,摆动组件由3部分组成:

  • 模型:存储组件状态和数据的位置
  • UI委托:绘制组件和
  • JComponent:它将所有内容联系在一起。

在本教程https://docs.oracle.com/javase/tutorial/uiswing/painting/step2.html中有一段说明:

paintComponent方法是进行所有自定义绘制的地方。 >此方法由javax.swing.JComponent定义,然后由>子类重写以提供其自定义行为

为什么JComponent上有paintComponent方法? 它不应该是UI委托的独家方法吗?

Swing基于AWT。 因此,最初的限制是基于AWT如何绘制组件。 因为Swing重量很轻,所以当绘制组件根组件时,需要通知子组件Swing组件他们还需要更新。 这是通过调用受更新影响的所有子组件的paint方法(然后调用paintComponent方法)来完成的。

关于Swing API的涂料链的许多决定都是基于定制的概念。 它降低了所涉及的复杂性(通过阻止paint的覆盖并将function集中在paintComponent方法中)。

外观和感觉API基于“委托”模型。 这意味着用于执行所述动作的function被“委托”给某个其他对象。 这意味着UI委托UI实际上并不知道组件“需要”何时被绘制,而是由组件告知它需要被绘制。 这使它更灵活,在许多情况下,更容易定制。

根据您之前的问题 ,自定义ButtonUI可能是更好的选择之一,这样您就可以更好地控制按钮的绘制方式。

试图让按钮跟随当前的外观和感觉颜色方案将是非常困难的,但你可以尝试看看与JDK一起安装的src.jar ,其中包括许多外观和感觉的实现(如果你的在Windows上你应该得到Windows的外观和感觉,如果你在Mac上,那么你没有得到Mac或Windows🙄)

我首先看一下BasicButtonUIButtonUI ,以便更好地理解这些属性。 我将一些更有趣的方法引入自定义的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); } } 

大多数这些你可能不需要担心,但你应该至少知道它们存在,因为你可能想要稍后自定义一些其他属性/function。

此委托旨在根据需要安装在按钮上,作为将其安装为按钮的默认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); 

最终产生了类似……

走那条路

现在,这是一个非常基本的例子,实际上,按钮应该在文本周围resize(带有一些额外的填充/边距信息),但这需要多部分形状,因此我们知道哪些部分可以resize并且在什么方向,所以,复杂。