我有一个任务执行器,它有5个外部面板,每个外部面板都是JFrame
,其innerPanel
。 innerpanel
是负责绘制图像的JPanel
。我一遍又一遍地显示和删除许多图像,而且我的表现非常糟糕。
有人可以建议在不使用JFrame
的情况下加载和删除许多图片吗?
我在屏幕的不同部分显示图像,有时持续时间非常短50-1500ms
。有时图像只是部分加载而其他图像根本不加载。
Main
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleWithFixedDelay(new ImageTask(new OuterPanel(ScreenPosition.CENTER)), 0, 3, TimeUnit.SECONDS);
scheduledExecutorService.scheduleWithFixedDelay(new ImageTask(new OuterPanel(ScreenPosition.CENTER)), 0, 3, TimeUnit.SECONDS);
scheduledExecutorService.scheduleWithFixedDelay(new ImageTask(new OuterPanel(ScreenPosition.CENTER)), 0, 3, TimeUnit.SECONDS);
scheduledExecutorService.scheduleWithFixedDelay(new ImageTask(new OuterPanel(ScreenPosition.CENTER)), 0, 3, TimeUnit.SECONDS);
scheduledExecutorService.scheduleWithFixedDelay(new ImageTask(new OuterPanel(ScreenPosition.CENTER)), 0, 3, TimeUnit.SECONDS);
}
}
ImageTask
public class ImageTask implements Runnable {
private OuterPanel outerPanel;
private int messageIndex;
public ImageTask(OuterPanel outerPanel) {
this.outerPanel = outerPanel;
}
@Override
public void run() {
outerPanel.setMessage(imagePath);
outerPanel.setVisible(true);
try {
TimeUnit.MILLISECONDS.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
outerPanel.setVisible(false);
}
}
OuterPanel
public class OuterPanel extends JFrame {
private InnerPanel innerPanel;
public static int height = 200;
public static int width = 200;
public OuterPanel(ScreenPosition screenPosition) {
initComponents();
setFocusable(false);
setUndecorated(true);
setBackground(new Color(0, 0, 0, 0));
setAlwaysOnTop(true);
setSize(SetScreenLocation.screenSize.width, SetScreenLocation.screenSize.height);
setFocusableWindowState(false);
setEnabled(false);
pack();
setLocation(0, 0);
}
private void initComponents() {
innerPanel = new InnerPanel();
add(innerPanel);
}
public void setMessage(Message message) {
ImageIcon icon = IconFetch.getInstance().getIcon(message.getImagePath());
if (icon != null) {
Image img = IconFetch.getInstance().getScaledImage(icon.getImage(), width, height);
innerPanel.setImage(img);
}
}
}
InnerPanel
public class InnerPanel extends JPanel {
private String message;
private Image img;
public InnerPanel() {
setOpaque(false);
setPreferredSize(new Dimension(900, 450));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (img != null) {
x = (this.getWidth() - img.getWidth(null)) / 2;
y = (this.getHeight() - img.getHeight(null)) / 2;
g2d.drawImage(img, x, y, this);
}
}
public void setImage(Image image) {
this.img = image;
repaint();
}
}
答案 0 :(得分:1)
所以,有三件事是时间成本的:
如果可以减少这些开销,可以提高系统的性能
我看不出IconFetch
是如何工作的,但我强烈建议:
ImageIO.read
加载图片,因为在图片完全加载之前它不会返回这是一个非常简单的例子。你传递一个文件路径,它会将图像加载到缓存中,如果它尚未缓存,否则它将返回缓存值
public enum ImagePool {
INSTANCE;
private Map<String, Image> images;
private ImagePool() {
images = new HashMap<>(25);
}
public synchronized Image grab(String name) throws IOException {
Image image = images.get(name);
if (image == null) {
image = ImageIO.read(new File(name));
images.put(name, image);
}
return image;
}
}
第一次在屏幕上显示一个窗口是一个代价高昂的过程,并且由于系统开销,您无法始终知道窗口何时在屏幕上实际“可见”。
为此,您应该专注于将窗口缓存到某种池中并尽可能地重新使用它们,例如:
public enum FramePool {
INSTANCE;
private int minSize = 15;
private int maxSize = 25;
private List<ImageFrame> avaliable;
private FramePool() {
avaliable = new ArrayList<>(25);
for (int index = 0; index < minSize; index++) {
avaliable.add(new ImageFrame());
}
}
public synchronized ImageFrame grab() {
ImageFrame frame;
System.out.println(avaliable.size());
if (avaliable.isEmpty()) {
System.out.println("Make new");
frame = new ImageFrame();
} else {
frame = avaliable.remove(0);
}
return frame;
}
public synchronized void release(ImageFrame frame) {
if (avaliable.size() < maxSize) {
avaliable.add(frame);
} else {
System.out.println("Destory");
frame.dispose();
}
}
public class ImageFrame extends JFrame {
private JLabel label;
private int timeout = 150;
public ImageFrame() throws HeadlessException {
label = new JLabel();
add(label);
setUndecorated(true);
setBackground(new Color(0, 0, 0, 0));
setAlwaysOnTop(true);
setFocusableWindowState(false);
setFocusable(false);
pack();
addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
System.out.println("Show me");
Timer timer = new Timer(timeout, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Hide me");
setVisible(false);
System.out.println("Return");
FramePool.INSTANCE.release(ImageFrame.this);
}
});
timer.start();
}
});
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public void setImage(Image image) {
label.setIcon(new ImageIcon(image));
pack();
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
bounds = gc.getBounds();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
bounds.x += insets.left;
bounds.y += insets.top;
bounds.width -= (insets.left + insets.right);
bounds.height -= (insets.top + insets.bottom);
int x = (int) (Math.random() * bounds.width);
int y = (int) (Math.random() * bounds.height);
if (x + getWidth() > bounds.width) {
x = bounds.width - getWidth();
if (x < 0) {
x = 0;
}
}
if (y + getHeight() > bounds.height) {
y = bounds.height - getHeight();
if (y < 0) {
y = 0;
}
}
setLocation(bounds.x + x, bounds.y + y);
}
}
}
最后,你设置了任务......
public class ImageTask implements Runnable {
private String name;
private int timeout;
public ImageTask(String name, int timeout) {
this.name = name;
this.timeout = timeout;
}
@Override
public void run() {
try {
System.out.println(this);
FramePool.ImageFrame frame = FramePool.INSTANCE.grab();
frame.setImage(ImagePool.INSTANCE.grab(name));
frame.setTimeout(timeout);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
现在,我采取了不同的方法。基本上,我只是将图像的名称传递给任务,然后使用框架和图像池来构建结果并将其显示在屏幕上。
对我来说,好处是孤立的控制。要在屏幕上获取图像,我只需要知道ImageTask
和图像文件名,而不需要其他内容。
对于我的测试,我从目录中加载了一堆图像并显示它们。
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
File[] files = path.listFiles((File pathname) -> pathname.getName().toLowerCase().endsWith(".png"));
for (File file : files) {
scheduledExecutorService.scheduleWithFixedDelay(new ImageTask(file.getPath(), 250 + (int) (Math.random() * 1000)), 0, 3, TimeUnit.SECONDS);
}
一旦创建了所有对象,它就会顺利运行
这个想法有不同之处,例如,您可能不需要FramePool
并且可以为每个ImageTask
创建一个实例