使用paintComponent进行Java幻灯片图像延迟

我正在整理一个幻灯片程序,用于衡量用户在每张幻灯片上花费的时间。 幻灯片演示了几个不同的魔术技巧。 每个技巧都会显示两次。 在重复之间显示临时图像。 在每个技巧之间显示过渡图像。

在第一次重复播放时,在显示下一个图像之前,点击后,JPanel颜色在屏幕上闪烁。 在第二次重复相同的技巧期间不会发生这种情况。 图像可能需要很长时间才能加载。

是否有一种简单的方法来预加载图像,以便第一次没有延迟?

NOTE: Original code deleted. 

编辑2013年1月10日:此代码现在适用于较慢的计算机。 trashgod的第二个附录帮助最多。 mouseClick控件结构定期要求SwingWorker类加载40个或更少的当前技巧,同时还将使用过的图像设置为null。 我已经将我的代码简化为两个Image []并添加了一个main方法,因此它是独立的。 仍然需要运行图像。 现在这是非常简单的代码,如果你想用大量图片制作幻灯片,我认为这将是一个很好的起点。

注意:我想我在使用多个Image []时想出了如何正确实现SwingWorker。 trashgod和kleopatra这个实现符合你的建议吗? 我最终没有使用发布和进程,因为我无法弄清楚如何使用索引数组正常工作,但因为StringWorker没有加载数组中的所有图像(只有40)和代码调用StringWorker每20个图像,应该有一个相当不错的缓冲区。

编辑1/10/2013通过在Mouse类上扩展MouseAdapter来改变MouseListener。 还修复了我的paintComponent方法,以包含对super.paintComponent(g)的调用。 向我的SwingWorker类ImageWorker添加了发布/处理方法。 添加了一个包装类ArrayWrapper,允许传递imageArray [i]及其相应的索引int i并使用publish进行处理。

 package slideshow3; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseAdapter; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import java.util.List; public class SlideShow3 extends JFrame { //screenImage will be replaced with each new slide private Image screenImage; private int width; private int height; //Create panel for displaying images using paintComponent() private SlideShow3.PaintPanel mainImagePanel; //Used for keybinding private Action escapeAction; //Image array variables for each trick private Image[] handCuffs; //h private Image[] cups; //c //Used to step through the trick arrays one image at a time private int h = 0; private int c = 0; //Used by timeStamp() for documenting time per slide private long time0 = 0; private long time1; public SlideShow3() { super(); //Create instance of each Image array handCuffs = new Image[50]; cups = new Image[176]; //start(handCuffsString); start("handCuffs"); try { screenImage = ImageIO.read(new File("images/begin1.jpg")); } catch (IOException nm) { System.out.println("begin"); System.out.println(nm.getMessage()); System.exit(0); } /****************************************** * Removes window framing. The next line sets fullscreen mode. * Once fullscreen is set width and height are determined for the window ******************************************/ this.setUndecorated(true); GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this); width = this.getWidth(); height = this.getHeight(); //Mouse click binding to slide advance control structure addMouseListener(new Mouse()); //Create panel so that I can use key binding which requires JComponent mainImagePanel = new PaintPanel(); add(mainImagePanel); /****************************************** * Key Binding * ESC will exit the slideshow ******************************************/ // Key bound AbstractAction items escapeAction = new EscapeAction(); // Gets the mainImagePanel InputMap and pairs the key to the action mainImagePanel.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "doEscapeAction"); // This line pairs the AbstractAction enterAction to the action "doEnterAction" mainImagePanel.getActionMap().put("doEscapeAction", escapeAction); /****************************************** * End Key Binding ******************************************/ } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SlideShow3 show = new SlideShow3(); show.setVisible(true); } }); } //This method executes a specific SwingWorker class to preload images public void start(String e) { if(e.equals("handCuffs")) { new ImageWorker(handCuffs.length, h, e).execute(); } else if(e.equals("cups")) { new ImageWorker(cups.length, c, e).execute(); } } //Stretches and displays images in fullscreen window private class PaintPanel extends JPanel { @Override public void paintComponent(Graphics g) { if(screenImage != null) { super.paintComponent(g); g.drawImage(screenImage, 0, 0, width, height, this); } } } /****************************************** * The following SwingWorker class Pre-loads all necessary images. ******************************************/ private class ArrayWrapper { private int i; private Image image; public ArrayWrapper(Image image, int i) { this.i = i; this.image = image; } public int getIndex() { return i; } public Image getImage() { return image; } } private class ImageWorker extends SwingWorker { private int currentPosition; private int arraySize; private String trickName; private Image[] imageArray; public ImageWorker(int arraySize, int currentPosition, String trick) { super(); this.currentPosition = currentPosition; this.arraySize = arraySize; this.trickName = trick; } @Override public Image[] doInBackground() { imageArray = new Image[arraySize]; for(int i = currentPosition; i < currentPosition+40 && i < arraySize; i++) { try { imageArray[i] = ImageIO.read(new File("images/" + trickName + (i+1) + ".jpg")); ArrayWrapper wrapArray = new ArrayWrapper(imageArray[i], i); publish(wrapArray); } catch (IOException e) { System.out.println(trickName); System.out.println(e.getMessage()); System.exit(0); } } return imageArray; } @Override public void process(List chunks) { for(ArrayWrapper element: chunks) { if(trickName.equals("handCuffs")) { handCuffs[element.getIndex()] = element.getImage(); } else if(trickName.equals("cups")) { cups[element.getIndex()] = element.getImage(); } } } @Override public void done() { try { if(trickName.equals("handCuffs")) { handCuffs = get(); } else if(trickName.equals("cups")) { cups = get(); } } catch(InterruptedException ignore){} catch(java.util.concurrent.ExecutionException e) { String why = null; Throwable cause = e.getCause(); if(cause != null) { why = cause.getMessage(); } else { why = e.getMessage(); } System.err.println("Error retrieving file: " + why); } } } /****************************************** * End SwingWorker Pre-Loading Classes ******************************************/ //Prints out time spent on each slide public void timeStamp() { time1 = System.currentTimeMillis(); if(time0 != 0) { System.out.println(time1 - time0); } time0 = System.currentTimeMillis(); } /****************************************** * User Input Classes for Key Binding Actions and Mouse Click Actions ******************************************/ private class EscapeAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } } public class Mouse extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { if(!(h<handCuffs.length) && !(c<cups.length)) { timeStamp(); System.exit(0); } else if(h<handCuffs.length) { timeStamp(); screenImage = handCuffs[h]; repaint(); System.out.print("handCuffs[" + (h+1) + "]\t"); h++; //purge used slides and refresh slide buffer if(h == 20 || h == 40) { for(int i = 0; i < h; i++) { handCuffs[i] = null; } start("handCuffs"); } if(h == 45) { start("cups"); } } else if(c<cups.length) { timeStamp(); screenImage = cups[c]; repaint(); System.out.print("cups[" + (c+1) + "]\t"); c++; //purge used slides and refresh slide buffer if(c == 20 || c == 40 || c == 60 || c == 80 || c == 100 || c == 120 || c == 140 || c == 160) { for(int i = 0; i < c; i++) { cups[i] = null; } start("cups"); } } } } /****************************************** * End User Input Classes for Key Binding Actions and Mouse Click Actions ******************************************/ } 

此示例使用List作为getImage()返回的图像的cache 。 使用getResource() ,延迟是难以察觉的。 默认情况下,下一个和上一个按钮绑定到Space键。

附录:例如,您可以通过使用javax.swing.Timer实例调整按钮的setEnabled()状态来控制导航。

附录:您的第二个示例等待,直到单击鼠标开始读取图像,这是一个可能立即返回副本的不确定进程,或者 repaint() 之后可能无法完成。 相反,使用ImageIO.read()开始在后台阅读图像,如下所示。 您可以process() List并显示进度,如此处所示。 SwingWorker可以从初始线程启动,在您随后在EDT上构建GUI时运行。 您可以在处理完第一张图像后立即显示它。