如何有效地将自定义JPanel绘制为JScrollPane的ViewportView?

时间:2013-01-24 00:26:07

标签: java swing jscrollpane viewport custom-painting

我有JScrollPane显示(作为其视口视图)MyPanelJPanel的子类。

MyPanel通过重载paintComponent来实现自定义绘制。 MyPanel的可显示内容的总大小通常很宽(意味着比JScrollPane视口大50倍到200倍)并使用Timer,我水平滚动查看不同基础MyPanel的部分。我还允许使用滚动条滑块手动搜索MyPanel的特定区域。

在我的paintComponent实现中,我目前正在使用MyPanel查找当前在视图端口中可见的JViewport#getVisibleRect部分,并且每次查看端口位置时只绘制该部分改变。

这样可以正常工作 - 但我最终反复重新绘制MyPanel的可见部分的一小部分,因为定时滚动一次只能移动视口大小的1/50视口。另外,我通常最终会滚动MyPanel的整个水平范围,因此无论如何我必须至少绘制一次。

这让我想到只绘制一次MyPanel的全部内容(到BufferedImage?),然后让JScrollPane(或JViewport)处理裁剪blitting只有BufferedImage所需的区域。

直觉上,在我看来,这是处理此问题的最有效方式,也是一种比较常见的方法。

当我研究Swing教程和其他资源时,我了解到Swing已经是双缓冲的。如果我试图强迫我自己的暴力,独立于Swing功能,听起来我最终会得到三重缓冲。

我没有找到利用JScrollPane为我做这个的配方(如果存在)。

是否有可用的示例或指示如何执行此操作(如果可能)?

1 个答案:

答案 0 :(得分:0)

Swing会自动为您绘制最小的必要组件区域。 repaint(4 args)仅在组件部分更改且您不希望重新绘制整个可见区域时才有用。在实际操作中,它与repaint(no-args)具有相同的效果。

如您在问题中所述,自动裁剪区域已经足够小,可以解决问题。您可以在程序中对其进行配置。

此外,您无需担心滚动 - JScrollPane会自动调用其子项的重绘。

您可以轻松地尝试这些:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Test extends JFrame {

    private Random rnd = new Random();
    private Color c = Color.WHITE;

    public Test () {
        final JPanel pnl = new JPanel() {

            @Override
            public void paintComponent (Graphics g) {
                super.paintComponent(g);
                g.setColor(c);
                g.fillRect(0, 0, getWidth(), getHeight());
                Rectangle r = g.getClipBounds();
                System.out.println(r.width + ", " + r.height);
            }
        };
        pnl.setPreferredSize(new Dimension(10000, 10000));

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 400);
        setLocationRelativeTo(null);

        add(new JScrollPane(pnl));

        setVisible(true);

        new Thread() {

            @Override
            public void run () {
                while (true) {
                    c = new Color(rnd.nextInt(0xffffff));
                    pnl.repaint();
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {}
                }
            }
        }.start();
    }

    public static void main (String args[]) {
        new Test();
    }
}