【发布时间】:2012-12-20 00:25:55
【问题描述】:
我正在编写一个幻灯片程序,该程序将测量用户在每张幻灯片上花费的时间。幻灯片展示了几种不同的魔术技巧。每个技巧显示两次。中间图像显示在重复之间。每个技巧之间都会显示过渡图像。
在第一次重复一个技巧时,JPanel 颜色在单击后在屏幕上闪烁,然后显示下一个图像。在第二次重复相同技巧时不会发生这种情况。可能是图片加载时间过长。
有没有一种简单的方法来预加载图像,以便在第一次加载时不会出现延迟?
NOTE: Original code deleted.
编辑 2013 年 1 月 10 日:此代码现在适用于速度较慢的机器。垃圾神的第二个附录帮助最大。 mouseClick 控制结构定期要求 SwingWorker 类加载当前技巧的 40 个或更少的图像,同时还将使用的图像设置为空。我已经将我的代码简化为只有两个 Image[] 并添加了一个 main 方法,因此它是独立的。图像仍然需要运行。现在这是非常简单的代码,如果您尝试制作包含大量图像的幻灯片,我认为这是一个不错的起点。
注意:我想我知道如何在仍然使用多个 Image[] 的同时正确实现 SwingWorker。垃圾神和 kleopatra 这个实现是否符合您的建议?我最终没有使用发布和处理,因为我无法弄清楚如何让它与索引数组一起正常工作,但是因为 StringWorker 不会加载数组中的所有图像(只有 40 个),并且代码每 20 张图像调用一次 StringWorker,应该有一个相当不错的缓冲区。
编辑 1/10/2013 通过在我的 Mouse 类上扩展 MouseAdapter 来更改 MouseListener。还修复了我的paintComponent 方法以包含对super.paintComponent(g) 的调用。 向我的 SwingWorker 类 ImageWorker 添加了发布/处理方法。添加了一个包装类 ArrayWrapper 以允许将 imageArray[i] 及其对应的索引 int i 与发布传递给进程。
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<Image[], ArrayWrapper>
{
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<ArrayWrapper> 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
******************************************/
}
【问题讨论】:
-
当心:您的 SwingWorker 实现不正确 - doInBackground 不得 触摸视图领域中使用的字段/属性(正如@trashgod 已经指出的那样在他的一个 cmets 中 - 重复强调 :-),EDT 和后台线程的完全分离是 SwingWorker 的重点
-
@kleopatra 我仍在尝试学习这个概念。我正在阅读并发和 SwingWorker 文档。我还没有完成,但如果有任何其他材料我应该涵盖或一个好的教程,我将非常感激。我想我明白你在说什么,但为了清楚起见,HandCuffWorker 的哪些部分实施不正确?
-
@kleopatra 嘿,我想我修正了 SwingWorker 的分离,你介意就上面显示的新代码提供反馈吗?
-
@sage88:进度,但没有
publish()/process()并且取决于延迟,您的MouseListener(MouseAdapter?) 可以在done()之前访问图像数组。这可能是一个新问题;在您的sscce 中,使用单个图像并用createGraphics()和drawString(String.valueOf(i), 5, 20)标记它。感谢使用Action。 -
@trashgod 感谢您推荐我使用 MouseAdapter。我读过的文本谈到了 WindowListener 及其关联的 WindowAdapter,所以我想知道 MouseListener 是否存在类似的东西。我认为使用 createGraphics() 和 drawString() 您试图让我注意到我的 paintComponent 方法缺少 super.paintComponent(g)?我找不到在基于数组/列表的图像上使用发布/处理的代码示例。我能找到的只是在字符串或整数上使用发布的简单示例。你知道一个使用带有数组的发布/进程的 SwingWorker 的好例子吗?
标签: java image slideshow paintcomponent swingworker