将自定义 Swing 组件的绘制委托给 parent

Delegate painting of custom Swing component to parent

我的目标

我希望我的自定义 Swing 组件 CellLabel extends JLabel 始终绘制某些东西 (X),而某些其他元素 (Y) 仅当其 parent -(JPanel 保持 一个 CellLabels) 的网格 - 告诉它。 (Y) 的绘制取决于网格中相邻单元格的状态,只有 JPanel 可以获取此信息并决定是否 以及如何 (Y)为每个 child CellLabel.

绘制

我的问题

我如何将绘画行为外部化(到一个带有参数的方法中,该方法允许我描述 如何 准确地绘制 (Y))绘画行为,以便 parent 可以决定是否要 CellLabel child 绘制 (Y)?

我的问题

那里的每个 Swing 教程都告诉我在创建自定义组件时应该覆盖 paintComponent 方法;但是,在我的应用程序中,组件无法自行决定是否以及如何绘制 (Y),因为它缺少必要的信息。

我尝试编写一个 CellLabel::paintY(int offset) 方法,一旦它决定了如何以及是否绘制 (Y),我可以从 parent 调用它:

class CellLabel extends JLabel {
    void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Paint elements (X) which should always be painted, independent
        // of external state
    }        

    void paintY(int[] params){
        Graphics2D g2 = (Graphics2D) this.getGraphics();

        // Perform some calculation with params that decide how painting is done

        // (*) Actually paint elements (Y) by using the g2 graphics context
    }
}

但是,似乎我无法在约定告诉我覆盖的预定义 paint 方法之外获取 Graphics 上下文。我在 (*) 处遇到以下异常(实际尝试使用 g2 进行绘制时):

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at CellLabel.paintY(CellLabel.java:77)
at AutomatonUI.createAndShowGUI(AutomatonUI.java:50)
at AutomatonUI.access[=12=]0(AutomatonUI.java:22)
at AutomatonUI.run(AutomatonUI.java:29)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:744)
at java.awt.EventQueue.access0(EventQueue.java:97)
at java.awt.EventQueue.run(EventQueue.java:697)
at java.awt.EventQueue.run(EventQueue.java:691)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:714)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

我有丰富的业务逻辑编码经验和 number-crunching,但我对 Swing(和 UI 编程)总体来说还很陌生,所以请原谅(并教育我) ) 如果我做错了什么。

IIRC,您的 paintComponent 函数需要调用 super.paintComponent(g);

上次我用 swing 编写程序时,我在 class 中声明了一个静态图形对象,任何东西都可以访问(这可能是一件很糟糕的事情,但它是一个帮助我的解决方法来完成我的任务)。不是将信息存储在二维数组中,而是将 "information" 个对象存储在数组中。这些信息对象(例如对象 x)可以包含有关单元格本身的所有信息,以及一个 "draw(g)" 方法。该绘制方法可以指示如何绘制此单元格。在 paintComponent 函数中,您现在可以调用

//where g is the graphics object
for (array x : array of arrays) {
    for (informationObject y : x) {
        y.draw(g);
    }
}

现在您可以在对象内部进行所有计算,而不必处理获取外部信息和在外部进行计算的问题。 (那么,代码也会更容易调试)

更好的方法可能是将您的逻辑与 Swing GUI 库分开。例如,定义一个 CellContainer class 包含 Cell 对象,可以在每次渲染之前更新(在更新中你可以从 [=10= 更新 Cells ] 随心所欲)。然后你可以有 CellContainerPanelCellLabel class 扩展适当的 Swing class 引用 CellContainerCell 每个只是绘制他们的参考可用的状态。