重新绘制JScrollPane中的JPanel

时间:2012-08-08 05:47:14

标签: java swing jpanel jscrollpane paintcomponent

我正在开发的项目是用于玩桌面游戏的计算机间实用程序,其中一个关键部分是交互式网格/地图。我已经内置了自定义地图创建功能,并希望屏幕能够显示非常大的地图,以及非常小的地图。为此,我创建了一系列类,我可以使用它来显示任何大小的网格。然而,我想要做的是将完成的JPanel放入JScrollPane,从而为地图创造无限空间。当我尝试这样做时,完全显示出JScrollPane的地图根本不会绘制。我怀疑它与Graphics上下文有关,但到目前为止还无法找到解决方案。

在下面的代码中,注释滚动行并在初始化时切换grid.draw方法调用,看看我希望它看起来像什么。

完整代码如下:

package pac;

import javax.swing.*;
import javax.swing.text.*;

import java.awt.*;

public class MainScreen extends JFrame
{
int width, height, x, y;
Grid grid;

public static void main(String[] args) 
{
    MainScreen mainScreen = new MainScreen();
    mainScreen.setVisible(true);        
}

public MainScreen()
{
    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
    float factor = 1.25f;
    width = (int)(dim.width / factor);
    height = (int)(dim.height / factor);
    x = (int)(dim.width / (factor * 8));
    y = (int)(dim.height / (factor * 8));

    setBounds(x,y,width,height);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setTitle("DnD Main Screen");
    setVisible(true);
    grid = new Grid();

    JScrollPane scrollPane = new JScrollPane(grid);
    scrollPane.setBounds(212,0,650,500);
    scrollPane.setViewportView(grid);
    add(scrollPane);
    this.setLayout(null);

    Thread draw = new Thread()
    {
        public void run()
        {
            while(true)
            {
                Graphics g = getGraphics();

                grid.repaint();
                //grid.paintComponents(getGraphics());

                g.setColor(Color.blue);
                g.fillRect(0 + 8, 0 + 30, 212, 200);
                g.fillRect(863 + 8, 0 + 30, 212, 200);

                g.setColor(Color.red);
                g.fillRect(0 + 8, 200 + 30, 212, 375);
                g.fillRect(863 + 8, 200 + 30, 212, 375);

                try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
                Thread.yield();
            }
        }
    };
    draw.start();
}

public class Grid extends JPanel
{
    Tile[][] tiles;
    public int x, y, across, down;
    public int topBar = 30;
    public int skinnyBar = 8;
    public Grid()
    {
        this.setPreferredSize(new Dimension(600,600));

        x=212 + skinnyBar;
        y=0+topBar;
        across = 13;
        down = 9;
        tiles = new Tile[across][down];
        for(int i = 0; i<across; i++)
            for(int j = 0; j<down; j++)
            {
                tiles[i][j] = new Tile(x+(i*50),y+(j*50),50);
            }
        this.setVisible(true);
    }
    public void paintComponents(Graphics g)
    {
        //super.paintComponents(g);
        draw(g);
    }
    public void draw(Graphics g)
    {
        for(int i = 0; i<across; i++)
            for(int j = 0; j<down; j++)
            {
                g.setColor(Color.black);
                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);
            }
    }
    private class Tile
    {
        int x, y, side;
        public Tile(int inX, int inY, int inSide)
        {
            x=inX;
            y=inY;
            side=inSide;
        }
    }
}

}

我一直在研究这个问题,但是找不到与我相似的问题和问题。有什么建议?拜托,谢谢你。

4 个答案:

答案 0 :(得分:7)

1.不要使用JScrollPan e的空布局,使用一些标准LayoutManager

2.不要使用getGraphics(),此方法用于打印到打印机或将GUI保存为文件图像的快照

3.没有理由使用draw(),准备所有Objects并在paintComponent()内绘制所有内容

4.大多数示例都过时了,基于Thread,请勿使用Thread,请使用Swing Timer

5.最简单的方法

JFrame - &gt; JScrollPane - &gt; JPanel - &gt;使用GridLayout将所需数量的JPanels放入所需的Color,将这些JPanels放入某种类型的数组,然后使用Swing Timer来获取来自数组的JPanel并更改其Backgroung

示例,必须添加Swing Timer以便在期间

上进行选择
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;

public class TilePainter extends JPanel implements Scrollable {

    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame("Tiles");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(new TilePainter()));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
    private final int TILE_SIZE = 50;
    private final int TILE_COUNT = 100;
    private final int visibleTiles = 10;
    private final boolean[][] loaded;
    private final boolean[][] loading;
    private final Random random;

    public TilePainter() {
        setPreferredSize(new Dimension(TILE_SIZE * TILE_COUNT, TILE_SIZE * TILE_COUNT));
        loaded = new boolean[TILE_COUNT][TILE_COUNT];
        loading = new boolean[TILE_COUNT][TILE_COUNT];
        random = new Random();
    }

    public boolean getTile(final int x, final int y) {
        boolean canPaint = loaded[x][y];
        if (!canPaint && !loading[x][y]) {
            loading[x][y] = true;
            Timer timer = new Timer(random.nextInt(500),
                    new ActionListener() {

                        @Override
                        public void actionPerformed(ActionEvent e) {
                            loaded[x][y] = true;
                            repaint(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
                        }
                    });
            timer.setRepeats(false);
            timer.start();
        }
        return canPaint;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Rectangle clip = g.getClipBounds();
        int startX = clip.x - (clip.x % TILE_SIZE);
        int startY = clip.y - (clip.y % TILE_SIZE);
        for (int x = startX; x < clip.x + clip.width; x += TILE_SIZE) {
            for (int y = startY; y < clip.y + clip.height; y += TILE_SIZE) {
                if (getTile(x / TILE_SIZE, y / TILE_SIZE)) {
                    g.setColor(Color.GREEN);
                } else {
                    g.setColor(Color.RED);
                }
                g.fillRect(x, y, TILE_SIZE - 1, TILE_SIZE - 1);
            }
        }
    }

    @Override
    public Dimension getPreferredScrollableViewportSize() {
        return new Dimension(visibleTiles * TILE_SIZE, visibleTiles * TILE_SIZE);
    }

    @Override
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        return TILE_SIZE * Math.max(1, visibleTiles - 1);
    }

    @Override
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return false;
    }

    @Override
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        return TILE_SIZE;
    }
}

答案 1 :(得分:5)

你确定吗?

public void paintComponents(Graphics g)

你的意思是

public void paintComponent(Graphics g)

答案 2 :(得分:4)

除了永远不会对Event Dispatching Thread之外的用户界面进行更新外,我还建议如下:

确保Grid窗格调用super.paintComponent(...)这很重要,你真的,真的,真的必须有充分的理由不这样做。

如果您希望面板透明,请改用setOpaque(false)

我还建议您熟悉Graphics.drawLine方法。我认为它会更有效率

你必须记住,你可以控制绘画过程。接受它。您可以向要更新的图形管道提供请求(invalidaterevalidaterepaint),但这是关于它的。剩下的由操作系统和重绘经理来决定。

this.setLayout(null);是一个非常非常糟糕的主意,它可能只是我的观点,但这确实是一个坏主意。如果没有其他任何事情了解GridBagLayout或使用compund面板和布局管理器,它将为您节省很多麻烦

答案 3 :(得分:4)

Hmm做了我自己的小例子,演示了在JPanel中重新绘制包裹<{em>的JScrollPane

该面板有一个自定义方法setScrollPane(JSCrollPane jsp),该面板将在面板和JScrollPane初始化后调用,这将允许JPanelrepaint() > scrollPanes 实例:

<强> PanelPaintTest.java:

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class PanelPaintTest extends JFrame {

    public PanelPaintTest() {
        createUI();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new PanelPaintTest().setVisible(true);
            }
        });
    }

    private void createUI() {
        //create frame
        setTitle("JPanel and JScrollPane Paint Test");
        setSize(500, 500);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //create custom panel
        Panel panel = new Panel(500, 500);    
        //create scrollpane to hold JPanel
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setViewportView(panel);    
        panel.setScrollPane(scrollPane);
        //custom method to set the panels JScrollPane which will be repainted when ever the panel is    
        getContentPane().add(scrollPane);//add scrollpane to the frame
    }
}

<强> Panel.java

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.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;

class Panel extends JPanel {

    private int width, height, across, down, x = 0, y = 0;
    ;
    private Panel.Tile[][] tiles;
    private Color color = Color.black;
    private JScrollPane scrollPane;

    public Panel(int width, int height) {
        this.setPreferredSize(new Dimension(width * 2, height * 2));//simple to make the scrollbars visible

        this.width = width * 2;
        this.height = height * 2;

        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() {
        scrollPane.repaint();
    }

    void setScrollPane(JScrollPane scrollPane) {
        this.scrollPane = scrollPane;
    }

    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(x + (i * 50), y + (j * 50), 50);//took out x and y values didnt know what 
            }
        }
    }

    //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;
        }
    }
}

面板将绘制您显示的图块,它们将每隔2秒闪烁红色或黑色。

HTH