我正在从事的项目是一个用于玩桌面游戏的计算机间实用程序,其中的一个关键部分是交互式网格/地图。好吧,我已经内置了自定义地图创建功能,并希望屏幕能够显示非常大的地图,以及非常小的地图。为此,我创建了一系列类,使用这些类可以显示任何大小的网格。然而,我想做的是将完成此操作的JPanel放入JScrollPane中,从而为地图腾出无限的空间。当我尝试这样做时,从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;
}
}
}
}
我已经为此工作了一段时间,但无法找到与我类似的问题来找到修复程序。有什么建议吗?请,谢谢。
1.不要对 JScrollPan
e 使用空布局,使用一些标准LayoutManager
,
2.不要使用getGraphics()
,此方法用于打印到打印机或将GUI作为快照保存到文件图像
3.没有理由使用draw()
,准备所有Objects
之前,并在里面涂漆paintComponent()
4.大部分例子太老了,基于Thread
,不要用Thread
,改用Swing Timer
5.最简单的方法可以
JFrame
-> JScrollPane
-> JPanel
-> 通过使用 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;
}
}
你确定吗?
public void paintComponents(Graphics g)
你的意思是相反
public void paintComponent(Graphics g)
除了从不对Event Dispatching Thread
之外的UI执行更新外,我还建议如下:
确保Grid
窗格调用super.paintComponent(...)
这很重要,并且您真的,真的,真的必须有一个很好的理由不这样做。
如果希望面板透明,请改用setOpaque(false)
。
我还建议您熟悉Graphics.drawLine
方法。 我认为它会更有效率
您必须记住,您无法控制绘画过程。 只要接受它。 您可以向要更新的图形管道提供请求(invalidate
、revalidate
、repaint
),但仅此而已。 其余的由操作系统和重绘管理器决定。
this.setLayout(null);
是一个非常非常糟糕的主意,这可能只是我的意见,但这确实是一个坏主意。 如果没有其他东西可以了解GridBagLayout
或使用 compund 面板和布局管理器,那只会为您省去很多麻烦
我做了我自己的小例子,演示了重新绘制包裹在JScrollPane
中的JPanel
:
面板有一个自定义方法setScrollPane(JSCrollPane jsp)
该方法将在面板之后调用,并且JScrollPane
已初始化,这将允许JPanel
在scrollPanes实例上调用repaint()
:
面板油漆测试.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
}
}
面板.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 秒闪烁一次红色或黑色。
呵呵