java swing:在paintComponent方法中如何知道重绘什么?

时间:2012-09-29 15:17:42

标签: java swing

我的组件比屏幕大,部分未显示(我将使用滚动条) 当我在paintComponent(g)接到电话时,我怎么知道我应该画哪个区域?

3 个答案:

答案 0 :(得分:1)

我不确定这是不是您的意思,但问题是您每次拨打repaint()时都必须致电JScrollPane paintComponent(Graphics g) JPanel上的JPanelJScrollPane上的更新将无法在JScrollBar中显示。

另外我看到你想使用JScrollPane(或者你可能会混淆术语)?我建议使用JPanel

我做了一个小例子,它是一个JPanel,网格每2秒就会改变一次颜色(红色变为黑色,反之亦然)。 JScrollPane /网格比repaint()大;无论我们是否需要在JScrollPane实例上调用import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.Timer; public class Test { public static void main(String[] args) throws Exception { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new Test().createAndShowUI(); } }); } private void createAndShowUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); initComponents(frame); frame.setPreferredSize(new Dimension(400, 400)); frame.pack(); frame.setVisible(true); } private void initComponents(JFrame frame) { JScrollPane jsp = new JScrollPane(); jsp.setViewportView(new Panel(800, 800, jsp)); frame.getContentPane().add(jsp); } } class Panel extends JPanel { private int across, down; private Panel.Tile[][] tiles; private Color color = Color.black; private final JScrollPane jScrollPane; public Panel(int width, int height, JScrollPane jScrollPane) { this.setPreferredSize(new Dimension(width, height)); this.jScrollPane = jScrollPane; createTiles(); changePanelColorTimer();//just something to do to check if its repaints fine } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); for (int i = 0; i < across; i++) { for (int j = 0; j < down; j++) { g.setColor(color); for (int k = 0; k < 5; k++) { g.drawRect(tiles[i][j].x + k, tiles[i][j].y + k, tiles[i][j].side - k * 2, tiles[i][j].side - 2 * k); } } } updateScrollPane();//refresh the pane after every paint } //calls repaint on the scrollPane instance private void updateScrollPane() { jScrollPane.repaint(); } private void createTiles() { across = 13; down = 9; tiles = new Panel.Tile[across][down]; for (int i = 0; i < across; i++) { for (int j = 0; j < down; j++) { tiles[i][j] = new Panel.Tile((i * 50), (j * 50), 50); } } } //change the color of the grid lines from black to red and vice versa every 2s private void changePanelColorTimer() { Timer timer = new Timer(2000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (color == Color.black) { color = Color.red; } else { color = Color.black; } } }); timer.setInitialDelay(2000); timer.start(); } private class Tile { int x, y, side; public Tile(int inX, int inY, int inSide) { x = inX; y = inY; side = inSide; } } } ,否则网格不会改变颜色:

Panel

updateScrollPane();课程中,如果我们对paintComponent(Graphics g)中的行{{1}}发表评论,我们就不会看到网格更改颜色。

答案 1 :(得分:1)

您可以通过查询Graphics对象的剪辑边界来找出实际上必须绘制的区域。

对于这种方法,JavaDoc似乎有点过时了:它说,它可能返回null剪辑。但是,显然情况并非如此(其他Swing类也依赖于剪辑永远不会null!)。

以下MCVE说明了使用剪辑或绘制整个组件之间的区别:

ClipDemo

它在滚动窗格中包含大小为800x800的JPanel。面板绘制一组矩形,并打印已绘制的矩形数。

可以使用“使用剪辑边界”复选框启用和禁用剪辑。使用剪辑时,仅重新绘制面板的可见区域,并且矩形的数量要低得多。 (请注意,测试是否需要绘制矩形在这里相当简单:它只执行矩形与可见区域的交叉测试。对于实际应用程序,可以直接使用剪辑边界以找出必须绘制的矩形。)

此示例还显示了一些棘手的滚动窗格内部:当关闭闪烁并移动滚动条时,可以看到 - 尽管整个可见区域发生了变化 - 实际上只需要一个小区域重新绘制(即由于滚动而变得可见的区域)。通过blitting先前的内容,简单地移动另一部分。可以使用JViewport.html#setScrollMode修改此行为。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class PaintRegionTest
{
    public static void main(String[] args) throws Exception
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final PaintRegionPanel paintRegionPanel = new PaintRegionPanel();
        paintRegionPanel.setPreferredSize(new Dimension(800, 800));

        final Timer timer = new Timer(1000, new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                paintRegionPanel.changeColor();
            }
        });
        timer.setInitialDelay(1000);
        timer.start();

        JScrollPane scrollPane = new JScrollPane(paintRegionPanel);
        frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(scrollPane, BorderLayout.CENTER);

        JPanel controlPanel = new JPanel(new FlowLayout());

        final JCheckBox blinkCheckbox = new JCheckBox("Blink", true);
        blinkCheckbox.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if (blinkCheckbox.isSelected())
                {
                    timer.start();
                }
                else
                {
                    timer.stop();
                }
            }
        });
        controlPanel.add(blinkCheckbox);


        final JCheckBox useClipCheckbox = new JCheckBox("Use clip bounds");
        useClipCheckbox.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                paintRegionPanel.setUseClipBounds(
                    useClipCheckbox.isSelected());
            }
        });
        controlPanel.add(useClipCheckbox);

        frame.getContentPane().add(controlPanel, BorderLayout.SOUTH);

        frame.setPreferredSize(new Dimension(400, 400));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

class PaintRegionPanel extends JPanel
{
    private Color color = Color.BLACK;
    private boolean useClipBounds = false;

    void setUseClipBounds(boolean useClipBounds)
    {
        this.useClipBounds = useClipBounds; 
    }

    void changeColor()
    {
        if (color == Color.BLACK)
        {
            color = Color.RED;
        }
        else
        {
            color = Color.BLACK;
        }
        repaint();
    }

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

        g.setColor(color);

        Rectangle clipBounds = g.getClipBounds();
        Rectangle ownBounds = new Rectangle(0,0,getWidth(),getHeight());

        System.out.println("clipBounds: " + clipBounds);
        System.out.println(" ownBounds: " + ownBounds);

        Rectangle paintedRegion = null;
        if (useClipBounds)
        {
            System.out.println("Using clipBounds");
            paintedRegion = clipBounds;
        }
        else
        {
            System.out.println("Using ownBounds");
            paintedRegion = ownBounds;
        }

        int counter = 0;

        // This loop performs a a simple test see whether the objects 
        // have to be painted. In a real application, one would 
        // probably use the clip information to ONLY create the
        // rectangles that actually have to be painted:
        for (int x = 0; x < getWidth(); x += 20)
        {
            for (int y = 0; y < getHeight(); y += 20)
            {
                Rectangle r = new Rectangle(x + 5, y + 5, 10, 10);
                if (r.intersects(paintedRegion))
                {
                    g.fill(r);
                    counter++;
                }
            }
        }
        System.out.println("Painted "+counter+" rectangles ");
    }

}

抛开:对于许多应用案例,几乎不需要这样的“优化”。无论如何,绘制的元素与剪辑相交,因此可能无法获得太多的性能。当“准备”要绘制的元素在计算上是昂贵的时,可以将其视为一种选择。 (在示例中,“准备”是指创建Rectangle实例,但可能存在更复杂的模式)。但在这些情况下,除了手动检查剪辑边界外,还可能有更优雅,更简单的解决方案。

答案 2 :(得分:0)

所有答案都错了。所以我决定回答这个问题,这个问题是两年之久。

我认为正确的答案是在g.getClipBounds()方法中调用paintComponent(Graphics g)。它将返回该区域的控件坐标系中的矩形,该区域无效并且必须重新绘制。