关于Java:使用Swing的动画管道

animation pipeline using swing

我尝试优化应用程序的图形。目前,我已经实现了动画以及一些GUI组件。有些是完全分开的,有些是彼此重叠的。目前,我在挥杆组件的重叠部分面临一个问题。 GUI的一部分与我的动画重叠,需要绘制许多字符串,并在放置的Jlist中绘制常见的挥杆组件。

结果是,随着动画的更新,重叠的GUI会被重绘。我尝试使用许多不同的方法来确保彼此之间得出的结论。诸如GlassPane,Jlayeredpane之类的东西。不幸的是,在任何这些尝试中,仅当用户与之交互时才需要调用重叠的Menus paintcomponent方法,由于动画其频繁调用该方法,并且会导致相当高的CPU使用率。

我试图将菜单在Layeredpane中的位置较低,即:

1
2
3
4
    getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
    getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
    getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);        
    getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);

在画家的绘画过程中,我试图修改区域-即:

1
2
3
4
5
6
7
8
9
@Override
protected void paintComponent(Graphics g) {

    g2 = (Graphics2D)g;
    g2.setRenderingHints(DefaultResources.getRenderQuality());

    g2.clip(JMain.getInstance().getMapBounds());

    ...}

好-画家组件!isOpague();下方的所有组件都会重绘。不幸的是,如果我确实将菜单置于较高的顺序,那么它们也需要在进行任何动画更新时重新绘制。

有人有什么想法,如何避免使用动画组件永久重绘重叠的组件?

我看到的唯一解决方案是使用重量级的容器。不幸的是,相对定位还显示了在移动目的期间的行为,这是不合适的。

感谢任何建议!


好吧,很明显,如果重叠的不透明组件全部都将在其中之一的任何更改上重新绘制,除非您优化了对某些特定矩形的动画重新绘制调用,所以不会有任何重叠无用的操作。

让我描述一下Swing的工作原理-您在paint,paintComponent和其他方法中所做的所有绘画(在每个组件重绘时都会调用)在包含" cached "的单个图像的子图像上完成。整个框架界面的版本。

现在,假设您在UI(添加/删除/重绘组件)中进行了某些更改-必须正确更新最终图像(或至少包含组件的一小部分)。如果您的组件不是不透明的,请执行此操作-所有子组件都将使用您的组件边界进行重新绘制,作为重新绘制矩形以为您的组件创建适当的背景。如果您的组件是不透明的-它将是唯一一个重新绘制的对象,但是它也必须自己填充整个边界,否则每次重新绘制时,您都将在您的组件后面看到可怕的绘制工件。

总结一下-为避免重叠组件的无意义重绘,有几种方法:

  • 优化对您实际需要重画的区域的动画重画调用
  • 使用不透明的组件,除非您要在组件中绘制透明的东西
  • 优化所有重叠的组件绘制操作,因此重新绘制不会花费太多时间
  • 根据您的具体情况,可能还会有更多的优化方法,但是您将不得不自己找到它们,因为如果不了解整个情况,这是不可能的。

    您还可以在本书中找到有关Swing优化的许多有用信息:肮脏的富客户端


    好的,我们去了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    package animationissue;

    import java.awt.Color;
    import java.awt.Graphics;
    import javax.swing.JFrame;
    import javax.swing.JLayeredPane;
    import javax.swing.JPanel;

    public class AnimationIssue extends JFrame {

    JPanel a = new JPanel() {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("map has become repainted");
        }
    };
    JPanel b = new JPanel() {

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("menu as well");
        }
    };

    public AnimationIssue() {
        this.setSize(500, 500);
        this.setLayout(null);

        a.setSize(400, 400);
        b.setSize(400, 200);

        this.getLayeredPane().add(a, JLayeredPane.DEFAULT_LAYER); // Map
        this.getLayeredPane().add(b, JLayeredPane.PALETTE_LAYER); // Menu

        a.setLocation(0, 0);
        b.setLocation(0, 100);

        a.setBackground(Color.red);
        b.setBackground(Color.blue);



        Thread t = new Thread(new Runnable() {

            @Override
            public void run() {
                // doin some computations for animation
                // cast a repaint after having finished new
                //animation information i.e. interpolation
                while (true) {
                    try {
                        Thread.sleep(2000);
                    } catch (Exception e) {
                    }

                    // case 1 - just a repaint of the whole component - triggering map redraw results in menu redraw
                    // a.repaint();

                    // case 2 - repaint of specified rectangle
                    // Either passing one - the menu does not get repainted, or passing       both - menu also gets repainted      

                    //a.repaint(0, 0, 400, 100);
                    //a.repaint(0, 300, 400, 100);

                    // paintimmediately works for now

                    //a.paintImmediately(0, 0, 400, 100);
                    //a.paintImmediately(0, 300, 400, 100);

                    // Just repainting Menu does not trigger map to become repainted, except if its opague, but then it should become repainted
                    b.repaint();


                }

            }
        });

        t.start();
    }

    /**
     * @param args the command line arguments
     */

    public static void main(String[] args) {
        // TODO code application logic here
        AnimationIssue f = new AnimationIssue();
        f.setVisible(true);
    }

    } ??

    我真的很想优化行为,如果没有必要的话,不会重绘菜单。您必须想象菜单是一个包含多个JList的组件,这些JList具有许多String绘制任务,这实际上对cpu的使用有很大的影响。我一直在想,因为它每秒被重绘大约25次。

    对于当前的解决方案,我只是不确定,如果立即使用paint是合适的。除此之外-如果您或其他人有另一种-更好的方法-防止无用的重绘(我真的认为Glasspane或JLayeredPane或isOptimizedDrawing或isOpaque可能会有所帮助),我真的很感谢。

    最诚挚的问候。


    在优化方面,我确实有一个组件,这引起了很多麻烦,但是我计划重写它。因此,我只想确保绘画区域正确。在那之后,我已经计算了所有必要的区域并将它们填充到一个列表中,当数据更改时我将其通过。

    只要我仅应用一个矩形,它就可以正常工作。一旦我通过了第二个,似乎它的y-扩展名就被忽略了。例如:

    [x = 0,y = 0,width = 322,height = 20]
    [x = 0,y = 620,width = 322,height = 20]

    在y = 20到y = 620之间的所有内容也会重新绘制。

    1
    2
    3
    for (Rectangle rec : clippingAreas) {                    
                     painter.repaint(rec);
                }

    好吧,我已经在EDT中立即进行了paint操作,目前该方法确实有效,但是我想知道这是否是一种正确的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    for (Rectangle rec : clippingAreas) {
                        painter.paintImmediately(rec);                        
                    }
                }
            });