我正在开发的项目是用于玩桌面游戏的计算机间实用程序,其中一个关键部分是交互式网格/地图。我已经内置了自定义地图创建功能,并希望屏幕能够显示非常大的地图,以及非常小的地图。为此,我创建了一系列类,我可以使用它来显示任何大小的网格。然而,我想要做的是将完成的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;
}
}
}
}
我一直在研究这个问题,但是找不到与我相似的问题和问题。有什么建议?拜托,谢谢你。
答案 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
方法。我认为它会更有效率
你必须记住,你不可以控制绘画过程。接受它。您可以向要更新的图形管道提供请求(invalidate
,revalidate
,repaint
),但这是关于它的。剩下的由操作系统和重绘经理来决定。
this.setLayout(null);
是一个非常非常糟糕的主意,它可能只是我的观点,但这确实是一个坏主意。如果没有其他任何事情了解GridBagLayout
或使用compund面板和布局管理器,它将为您节省很多麻烦
答案 3 :(得分:4)
Hmm做了我自己的小例子,演示了在JPanel
中重新绘制包裹<{em>的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