【发布时间】:2018-08-27 10:01:56
【问题描述】:
我正在尝试寻找最有效的方法来创建动态 java 图形应用程序。我想构建一个带有许多不同部分的大屏幕,所有这些都使用不同的线程重新绘制或更新,以使屏幕看起来“活”。 然而,我最初的尝试是可怕的,屏幕变得非常慢,错误等等 - 所以我想我需要创建不同的模块(JPanels),每个模块都包含其他图形部分(线条、圆圈等),并且每个单独重绘不同的 JPanel(需要时),而不是整个主面板(或框架)。
所以我写了一个小演示程序——我的程序包含一个窗口,有多个面板,包裹在我的名为“MyPanel”的对象中——每个这样的 MyPanel 都包含几条画线(我有一个 Line 对象),所有线都以从左上角开始,有不同的长度和角度)。每个不同的 MyPanel 都有不同的线条颜色(参见此图)。
我实例化了几个工作线程,每个线程指定一个 MyPanel - 工作人员等待 5 秒,然后尝试以下列方式重新绘制所有线条:
- 从 JPanel (MyPanel) 中删除所有现有行。
- 创建具有不同角度和长度的新线条。
- 通过调用 super.repaint() 重绘 JPanel (MyPanel) 这是整个目的,只更新这个面板,让它重绘它的所有子部分,而不是整个程序强>
然而,发生了一些奇怪的事情:当重新绘制面板时,每个面板都以一种可能包含所有其他 MyPanel 的方式重新绘制,或者以某种方式镜像主屏幕 - 非常不清楚这里到底发生了什么。此外,面板的所有“背景不透明度”都消失了(参见这张图片)。
在我附加我的代码之前,让我说它使用了一个空的 LayoutManager。我知道就效率、模块化等方面而言,这是一个很大的“不可以”。但是我别无选择,因为我需要快速创建一个非常复杂且精确的演示,这仅作为概念验证,所以现在,所有这些缺陷都可以忽略不计。我知道这在设计方面很糟糕,也让我很痛苦,但这是我能按时完成的唯一方法。
这是代码 - 会发生什么? 如果不使用这种方式,如何有效地重新绘制程序的不同部分?请注意,我不能“用背景颜色重新绘制现有线条”,因为我的主程序中有一个背景图像.
任何帮助将不胜感激!
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* Displays the main windows (this is the "JFrame" object).
*/
public class GUI extends JFrame
{
/**
* A customized panel which contains several lines with different coordinates, all starting from
* the top left corner of the panel with coordinates (1,1). The object contains a method which
* removes all drawn lines from the panel, then redraws lines with different vectors.
*/
public static class MyPanel extends JPanel
{
private List<Line> _lines;
private Color _color;
private int _facet;
private int _numLines;
public MyPanel(int facet, int numLines, Color color)
{
_facet = facet;
_color = color;
_numLines = numLines;
_lines = new ArrayList<>();
super.setLayout(null);
createLines();
}
public void createLines()
{
for(Line line : _lines)
{
remove(line);
}
_lines.clear();
Random r = new Random();
for(int i = 0; i < _numLines; i++)
{
int lengthX = r.nextInt(_facet) + 1;
int lengthY = r.nextInt(_facet) + 1;
Line line = new Line(1, 1, 1 + lengthX, 1 + lengthY, 1, _color);
line.setBounds(1, 1, 1 + lengthX, 1 + lengthY);
super.add(line);
_lines.add(line);
}
super.repaint();
}
}
/**
* Represents a line, drawn with antialiasing at a given start and end coordinates
* and a given thickness.
*/
public static class Line extends JPanel
{
private int _startX;
private int _startY;
private int _endX;
private int _endY;
private float _thickness;
private Color _color;
public Line(int startX, int startY, int endX, int endY, float thickness, Color color)
{
_startX = startX;
_startY = startY;
_endX = endX;
_endY = endY;
_thickness = thickness;
_color = color;
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(_color);
g2d.setStroke(new BasicStroke(_thickness));
g2d.drawLine(_startX, _startY, _endX, _endY);
}
}
/**
* Stores all "MyPanel" panels of the GUI.
* The "MyPanels" are rectangular panels containing lines of the same color
* (different color across different panels).
*/
public List<MyPanel> panels;
public GUI()
{
setSize(800, 800);
setLayout(null);
setTitle("Y U no work??");
panels = new ArrayList<>();
// The starting positions (x,y) of the "MyPanel"s. All panels are squares of
// height = 300 and width = 300.
int[][] coords = {{1, 1}, {100, 100}, {200, 100}, {50, 300}, {300, 300},
{0, 400}, {300, 400}, {350, 250}, {370, 390}};
// The colors of the lines, drawn in the panels.
Color[] colors = {Color.RED, Color.GREEN, Color.BLUE, Color.ORANGE, Color.CYAN,
Color.MAGENTA, Color.YELLOW, Color.PINK, Color.darkGray};
for(int i = 0; i < colors.length; i++)
{
MyPanel panel = new MyPanel(300, 50, colors[i]);
panel.setBackground(new Color(0, 0, 0, 0));
// Set the *exact* start coordinates and width/height (null layout manager).
panel.setBounds(coords[i][0], coords[i][1], 300, 300);
add(panel);
panels.add(panel);
}
}
/**
* A runnable used to instantiate a thread which waits for 5 seconds then redraws
* the lines of a given "MyPanel".
*/
public static class Actioner implements Runnable
{
private MyPanel _panel;
public Actioner(MyPanel panel)
{
_panel = panel;
}
public void run()
{
while(true)
{
try
{
Thread.sleep(5000);
}
catch(Exception e) {}
_panel.createLines();
}
}
}
public static void main(String[] args)
{
GUI GUI = new GUI();
EventQueue.invokeLater(() ->
{
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.setVisible(true);
});
// Create all operating threads (one per "MyPanel").
for(MyPanel panel : GUI.panels)
{
new Thread(new Actioner(panel)).start();
}
}
}
【问题讨论】:
-
1) 自定义绘画是通过覆盖paintComponent(...) 而不是paint() 来完成的。 2) 第一条语句应该是
super.paintComponent(g)以清除背景。 3) 行不应扩展 JPanel。它应该只是一个包含有关要绘制的线的信息的类。然后,您保留您绘制的 Line 对象的 ArrayList。有关此方法的示例,请参阅 Custom Painting Approaches。它展示了如何从 ArrayList 绘制Colored Rectangles。 -
使用上述方法也可以解决空布局问题。
-
感谢您的评论!我会调查你写的。我相信它会有所帮助:)
-
@camickr 再次感谢您的回复,它帮助我编写了更好的 JPanel 组件,将几张图纸编译成一个 paintComponent 方法真的很有帮助。