在JScrollPane重绘一个JPanel(Repainting a JPanel in a JS

2019-09-20 23:56发布

我的工作在该项目是玩桌面游戏的计算机间实用,而且其中的关键部分是一个互动的网格/地图。 嗯,我已经内置了对自定义地图创建功能,并希望屏幕能够显示非常大的地图,以及非常小的地图。 为此我已经创建了一系列与我可以显示任意大小的网格类。 不过我会想要做的是把此与实现成一个JScrollPane,从而对地图的无限空间的JPanel。 当我尝试这样做,显示出完美的一个JScrollPane的完全不画地图。 我怀疑它与图形方面的事,但迄今未能找到解决方案。

在下面的代码,备注滚动线和开关在初始化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;
        }
    }
}

}

我一直在这一段时间,一直没能找到和问题相似,足以雷找到修复。 有什么建议? 谢谢,麻烦您了。

Answer 1:

1.don't使用空布局JScrollPan E,使用一些标准的LayoutManager

2.不要使用getGraphics()这种方法是用于打印的打印机或用于保存GUI作为快照文件的图像

人们进去理由使用draw()准备所有Objects前,内画一切paintComponent()

的例子4.most太旧,基于Thread ,不使用Thread ,使用Swing Timer来代替

5.easiest可能方式

JFrame - > JScrollPane - > JPanel - >通过使用GridLayout放有所需的量JPanels具有期望Color ,把这些JPanels到一些类型的数组,并通过使用Swing Timer拾起JPanel从阵列和改变其Backgroung

例如,必须添加Swing Timer用于paiting上期

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


Answer 2:

你确定吗?

public void paintComponents(Graphics g)

你的意思,而不是

public void paintComponent(Graphics g)


Answer 3:

除了外界从未进行更新到UI Event Dispatching Thread ,我也建议如下:

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

如果您希望面板是透明的,使用setOpaque(false)来代替。

我也建议你熟悉Graphics.drawLine方法。 我认为这将是更有效

你要记住,你不必在涂料工艺控制。 只要接受它。 您可以提供请求,要更新(图形流水线invalidaterevalidaterepaint ),但仅此而已。 剩下的就是对操作系统和重绘管理器来决定。

this.setLayout(null); 是一个非常,非常糟糕的主意,它可能只是我的意见,但是这确实是一个坏主意。 如果不出意外结识GridBagLayout或使用复方面板和布局管理器,它只会为你节省很多麻烦



Answer 4:

嗯,的确这表明重画一个我自己的小例子JPanel它被包裹JScrollPane

面板上有一个自定义的方法setScrollPane(JSCrollPane jsp)后面板将被称为JScrollPane已被初始化,这将允许JPanel调用repaint()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



文章来源: Repainting a JPanel in a JScrollPane