2

アプリケーションの描画を最適化しようとしています。現在、アニメーションといくつかの GUI コンポーネントを実装しています。正確に分離されているものもあれば、重なり合っているものもあります。現在、スイング コンポーネントのオーバーラップ内で問題に直面しています。私のアニメーションと重なる GUI の一部は、多くの文字列を描画する必要があります。

その結果、オーバーラップする GUI は、アニメーションが更新されるたびに再描画されます。お互いの前に描かれているものを確認するために、かなり多くの異なる方法を使用してみました。GlassPane、Jlayeredpane など。残念なことに、これらの試行のいずれにおいても、ユーザーが操作するときにのみ呼び出される必要がある重複する Menus paintcomponent メソッドが、アニメーションのために頻繁に呼び出され、CPU 使用率が非常に高くなります。

レイヤーペイン内でメニューを下位に配置しようとしました。つまり、次のようになります。

    getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
    getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
    getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);        
    getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);   

ペインターのペイントプロセス中に、領域を変更しようとしました-つまり:

@Override
protected void paintComponent(Graphics g) {

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

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

    ...}

さて - ペインター コンポーネントが !isOpague(); になるとすぐに その下にあるすべてのコンポーネントが再描画されます。残念ながら、メニューを上位に配置すると、アニメーションの更新で再描画する必要があります。

オーバーラップするコンポーネントとアニメーション化されたコンポーネントの永久的な再描画を回避する方法を知っている人はいますか?

私が見た唯一の解決策は、重量のあるコンテナーを使用することでした。残念ながら、相対的な位置付けは、目的を移動する際の動作も示しており、これは適切ではありません。

アドバイスありがとうございます!!

4

3 に答える 3

3

まあ、不透明でないコンポーネントが重なっている場合、特定の長方形へのアニメーション再描画呼び出しを最適化しない限り、それらのすべてがそれらのいずれかの変更で再描画されることは明らかなので、無駄な操作はありません。

Swing がどのように機能するかをもう少し説明しましょう。paint、paintComponent、およびその他のメソッド (コンポーネントの再描画ごとに呼び出される) で行うすべての描画は、全体の「キャッシュ」バージョンを保持する単一の画像のサブ画像に対して行われます。フレームインターフェース。

ここで、UI の何かを変更 (コンポーネントの追加/削除/再描画) したとします。その最終的な画像 (またはコンポーネントを含む少なくともその一部) を適切に更新する必要があります。コンポーネントが不透明でない場合にこれを行うには、コンポーネントの適切な背景を作成するために、すべてのサブコンポーネントが再描画矩形としてコンポーネントの境界で再描画されます。コンポーネントが不透明な場合 - 再描画されるのはそのコンポーネントだけですが、それ自体で境界四角形全体を塗りつぶす必要があります。そうしないと、再描画のたびにコンポーネントの背後にひどい描画アーティファクトが表示されます。

要約すると、重複するコンポーネントの無意味な再描画を避けるために、いくつかのアプローチがあります。

  1. 実際に再描画が必要な領域へのアニメーションの再描画呼び出しを最適化する
  2. コンポーネントに透明なものをペイントする場合を除き、不透明なコンポーネントを使用します
  3. 重複するすべてのコンポーネントの描画操作を最適化して、再描画に時間がかからないようにする

特定のケースによっては、さらに多くの最適化アプローチがあるかもしれませんが、全体像を見ないと不可能なので、自分で見つける必要があります。

この本には、Swing での最適化に関する多くの有用な情報も記載されています: Filthy Rich Clients

于 2012-10-01T13:54:50.293 に答える
0

では、行きましょう:

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 を保持するコンポーネントであるメニューを想像する必要があります。これは CPU 使用率に大きな影響を与えます。毎秒約25回再描画されるので、私は不思議に思っていません.

paintImmediately を使用することが適切である場合、現在のソリューションについては不明です。これとは別に、あなたまたは誰かが無駄な再描画を防ぐ代替のより良い方法を持っている場合(GlasspaneまたはJLayeredPaneまたはisOptimizedDrawingまたはisOpaqueが役立つと本当に思っていました)、本当に感謝しています。

よろしくお願いします。

于 2012-10-04T12:05:03.657 に答える
0

最適化に関しては、かなりの問題を引き起こしているコンポーネントがありますが、そのコンポーネントは書き直す予定です。したがって、ペイント領域が適切であることを確認したいだけです。その時点に続いて、必要なすべての領域を計算し、それらをリストに入力しました。これは、データが変更されたときに渡します。

長方形が1つしか適用されていない限り、正しく機能しています。2番目のものを渡すとすぐに、その y - 拡張子が無視されるようです。例えば:

[x=0,y=0,幅=322,高さ=20] [x=0,y=620,幅=322,高さ=20]

y=20 と y=620 の間のすべても再描画されます。

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

OK、EDT 内で paintImmediately を試してみましたが、これは今のところ機能しますが、これが適切な方法であるかどうか疑問に思います。

SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                for (Rectangle rec : clippingAreas) {
                    painter.paintImmediately(rec);                        
                }
            }
        });
于 2012-10-02T12:56:36.720 に答える