7

に埋め込んだ大きな「キャンバスのような」CAD コンポーネントにナビゲーション ウィンドウを提供するために、ここJScrollNavigatorで説明するコンポーネントを使用しています。JScrollPane

JScrollNavigatorユーザーに追加のコンテキストを提供するために、キャンバスのサムネイル画像を描画するように を適応させようとしました。ただし、これを実行すると、アプリケーションのメイン フレームのレンダリングが破損します。具体的にはpaint(Graphics)、ビューポート コンポーネント (つまり、私のメイン キャンバス)を呼び出し、Graphicsによって作成されたオブジェクトを渡すアクションがBufferedImage、その後の表示の破損を引き起こします。この行をコメントアウトすると、すべて正常に動作します。

以下は、JScrollNavigatorオーバーライドされたpaintComponentメソッドです。

@Override
protected void paintComponent(Graphics g) {
    Component view = jScrollPane.getViewport().getView();
    BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = img.createGraphics();

    // Paint JScrollPane view to off-screen image and then scale.
    // It is this action that causes the display corruption!
    view.paint(g2d);
    g2d.drawImage(img, 0, 0, null);
    Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);

    super.paintComponent(g);
    g.drawImage(scaled, 0, 0, null);
}

破損の原因について何か提案はありますか? オフスクリーン イメージへのペイントは、既存のペイント操作に影響を与えるべきではないと考えていたでしょう。

編集

追加の詳細を提供するには:JScrollNavigatorは、 の左側にサブパネルを形成しJSplitPaneます。ナビゲーターにJScrollPane関連付けられている は右側にあります。「破損」により、スプリッターがレンダリングされなくなり、スクロールバーが表示されなくなります (白く表示されます)。のサイズを変更するJFrameと、JMenuセクションも白くなります。ナビゲーターを使用したり、スクロールバーを操作しようとすると、それらは表示されますが、スプリッターは白のままです。さまざまなコンポーネントの不透明な設定が、ビューポート ビューをオフスクリーン イメージにレンダリングすることによって影響を受けているかのようです。

また、 をJScrollNavigator完全に別の に表示させるとJDialog、すべてが正しく機能します。

編集2

以下を実行することで、一貫して問題を再現できます。

JMenuBarに aを追加mFrame:

JMenuBar bar = new JMenuBar();
bar.add(new JMenu("File"));
mFrame.setJMenuBar(bar);

置換のmain()方法では:JScrollNavigator

jsp.setViewportView(textArea);

... と:

jsp.setViewportView(new JPanel() {
  {
    setBackground(Color.GREEN);
    setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
  }
});

が別個の として表示されるのではなく、JScrollNavigator内にパネルとして埋め込まれていることを確認します。mFrameJDialog

mFrame.add(jsp, BorderLayout.CENTER);
mFrame.add(nav, BorderLayout.NORTH);

アプリケーションを実行すると、JMenuBar は表示されなくなります。によって返されるにビュー (つまり、JPanel太い黒の境界線を持つ緑) をペイントする行為は、実際には、おそらく JFrame の左上隅から、画面上にレンダリングしているように見えます。そのため、他のコンポーネントが見えなくなります。これは、 がビューポート ビューとして使用され、、 などの別のコンポーネントが使用されていない場合にのみ発生するようです。Graphics2DBufferedImage.createGraphics()JPanelJTextAreaJTable

編集3

この人が同じ問題を抱えていたようです (解決策は投稿されていません): http://www.javaworld.com/community/node/2894/

編集4

編集 2 で説明されている再現可能なエラーが発生するメソッドmainとメソッドは次のとおりです。paintComponent

public static void main(String[] args) {
    JScrollPane jsp = new JScrollPane();
    jsp.setViewportView(new JPanel() {
        {
            setBackground(Color.GREEN);
            setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
        }
    });

    JScrollNavigator nav = new JScrollNavigator();
    nav.setJScrollPane(jsp);

    JFrame mFrame = new JFrame();

    JMenuBar bar = new JMenuBar();
    bar.add(new JMenu("File"));
    mFrame.setJMenuBar(bar);

    mFrame.setTitle("JScrollNavigator Test");

    mFrame.setSize(800, 600);

    mFrame.setLayout(new GridLayout(1, 2));

    mFrame.add(jsp);
    mFrame.add(nav);
    Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
    mFrame.setLocation((screenDim.width - mFrame.getSize().width) / 2, (screenDim.height - mFrame.getSize().height) / 2);

    mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mFrame.setVisible(true);
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Component view = jScrollPane.getViewport().getView();

    if (img == null) {
        GraphicsConfiguration gfConf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    }

    Graphics2D g2d = img.createGraphics();
    view.paint(g2d);

    Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);

    g.drawImage(scaled, 0, 0, null);
}

編集5

他の人が正確な問題を再現するのに苦労しているようです。ここに貼り付けたコードを実行してもらいたいと思います。この例を最初に実行すると、次のように表示されます。

破損した画像 1

JScrollNavigator も JMenuBar も描画されていません。これらのフレーム領域は透明です。

サイズ変更後、次のように表示されます。

破損した画像 2

JMenuBarまだペイントされておらずJPanel、ある時点で (0,0) (あるJMenuBarべき場所) でレンダリングされたようです。内部のview.paint呼び出しpaintComponentは、これの直接の原因です。

4

3 に答える 3

9

概要: オリジナルJScrollNavigatorでは、Swingプロパティを使用して、隣接するコンポーネントのスケーリングされたサムネイルの上にopacity便利な緑色をレンダリングします。を拡張するため、(共有) UI デリゲートの使用は、スクロール可能なコンポーネントの使用と競合します。上記の編集 5 で見られる画像は、関連するレンダリング アーティファクトを代表するもので、こちらにも示されています。解決策は、以下の 2 番目の補遺で提案されているように、スクロール可能なコンポーネントを拡張することです。その後、各コンポーネントは独自のプロパティを個別に管理できます。NavBoxJScrollPaneJPanelopacityNavBoxJScrollNavigatorJComponent

ここに画像の説明を入力

私のプラットフォーム、Mac OS X、Java 1.6 に投稿されたコードには、異常なレンダリング アーティファクトは見られません。申し訳ありませんが、明らかな移植性違反は見当たりません。

画像1

おそらく関係ないかもしれないが、役に立つかもしれないいくつかの観察。

  • setSize()この場合、適切にを使用してもpack()、囲んでいる必要がありますWindow

    f.pack();
    f.setSize(300, 200);
    
  • 便宜上、add()コンポーネントをコンテンツ ペインに転送します。

    f.add(nav, BorderLayout.WEST);
    
  • StringBuilderを好むStringBuffer

  • ComponentAdapterの代わりに検討してくださいComponentListener

補遺:ここで提案されているように、以下のようRenderingHintsに代わりに使用すると、より柔軟な結果が得られました。getScaledInstance()いくつかのアイコンを追加すると、画像とテキストの異なる効果を簡単に確認できます。

画像2

editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon"));
editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
...
@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Component view = jScrollPane.getViewport().getView();
    BufferedImage img = new BufferedImage(view.getWidth(),
        view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D off = img.createGraphics();
    off.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    off.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    view.paint(off);
    Graphics2D on = (Graphics2D)g;
    on.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    on.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    on.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}

補遺 2: JPanelUI デリゲートが協力していないようです。回避策の 1 つは、 opacityJComponentを制御できるように拡張することです。を管理するのは、ほんの少し手間がかかります。また、同様の治療法の候補でもあります。backgroundColorNavBoxJScrollNavigator

ここに画像の説明を入力

jsp.setViewportView(new JComponent() {

    {
        setBackground(Color.red);
        setBorder(BorderFactory.createLineBorder(Color.BLACK, 16));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 300);
    }
});
于 2012-07-31T16:39:33.823 に答える
2

また、破損の意味がわかりませんが、再スケーリングのヒントとして Image.SCALE_SMOOTH を指定すると、再サ​​ンプリングされた画像がはるかに優れていることに気付きました。

Image scaled = img.getScaledInstance(getWidth(), getHeight(), Image.SCALE_SMOOTH);

多分これはあなたが探しているものです...

于 2012-07-31T17:15:59.030 に答える
2

私はあなたの問題を再現し、あなたが探している結果を得ることができました. 問題は、再度再描画するまでに画像の描画が完了していなかったため、画像の一部しか描画されていなかったことです。これを修正するには、このフィールドをJScrollNavigatorクラスに (ロックとして)追加します。

/** Lock to prevent trying to repaint too many times */
private boolean blockRepaint = false;

コンポーネントを再描画すると、このロックが有効になります。パネルを正常にペイントできるようになるまでリリースされません。その後、別のペイントを実行できます。

ロックを遵守し、ナビゲーション パネルをペイントするときにを使用するには、 を変更するpaintComponent必要があります。ImageObserver

@Override
protected void paintComponent(final Graphics g) {
    super.paintComponent(g);
    if(!blockRepaint){
        final Component view = (Component)jScrollPane.getViewport().getView();
        BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Paint JScrollPane view to off-screen image and then scale.
        // It is this action that causes the display corruption!
        view.paint(g2d);
        ImageObserver io = new ImageObserver() {

            @Override
            public boolean imageUpdate(Image img, int infoflags, int x, int y,int width, int height) {
                boolean result = true;
                g.drawImage(img, 0, 0, null);
                if((infoflags & ImageObserver.FRAMEBITS) == ImageObserver.FRAMEBITS){
                    blockRepaint = false;
                    result = false;
                }

                return result;
            }
        };

        Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);
        blockRepaint = g.drawImage(scaled, 0, 0, io);
    }
}
于 2012-08-03T20:38:05.577 に答える