i am trying to load image from specified path, and list of files are stored inside List<>.
at first time when i initialize image it display but when i am trying to display next image from List instance which contain list of files, it doesn't repaint the image.
what's wrong going is i am initializing image in constructor first time that overwrite
the new image, now where to initialize image first time outside constructor i don't know.
my code :
public void nextImage(int cnt)
{
System.out.println(cnt);
if (cnt < imageFiles.size())
{
System.out.println(imageFiles.size());
try
{
bg = ImageIO.read(new File((imageFiles.get(cnt)).toString()));
scaled = getScaledInstanceToFit(bg, new Dimension(600, 600));
setBackground(Color.BLACK);
}
catch(Exception e)
{
e.printStackTrace();
}
}
MouseHandler handler = new MouseHandler();
addMouseListener(handler);
addMouseMotionListener(handler);
System.out.println(cnt);
System.out.println(imageFiles.get(cnt).toString());
}
menu item click code :
JMenuItem mntmRestoreImage = new JMenuItem("Next Image");
final ImagePane st = new ImagePane();
mntmRestoreImage.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
st.nextImage(k);
k++;
}
});
mnEdit.add(mntmRestoreImage);
class code & constructor :
private BufferedImage bg;
private BufferedImage scaled;
java.util.List<Path> imageFiles= getFilesFromDirectory(FileSystems.getDefault().getPath("D:\\New folder"));
public ImagePane()
{
try
{
bg = ImageIO.read(getClass().getResource("/images/src11.jpg"));
scaled = getScaledInstanceToFit(bg, new Dimension(600, 600));
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
counter also increments, now the code inside ImagePane() constructor
overwrites the image of nextImage() function, so idea what happen out
in this code ??
any suggestion ?
I think I have the perfect solution for you! I wrote a little program for you so it is easier to understand.
First I have a method for you to check if the file is a picture:
public Stack<File> getFilesInFolder(String startPath) {
File startFolder = new File(startPath);
Stack<File> picturestack = new Stack<File>();
String extension;
int dotindex;
// Go through the folder
for (File file : startFolder.listFiles()) {
extension = "";
dotindex = file.getName().lastIndexOf('.'); // Get the index of the dot in the filename
if (dotindex > 0) {
extension = file.getName().substring(dotindex + 1);
// Iterate all valid file types and check it
for (String filetype : validpicturetypes) {
if (extension.equals(filetype)) {
picturestack.add(file);
}
}
}
}
return picturestack;
}
Very easy! Take the folder and iterate his files. Take the extension of the file and check if it is a valid file type. Define the file types in a array at the begining of your code.
String[] validpicturetypes = {"png", "jpg", "jpeg", "gif"};
At the end I push every file into a stack. Remember to fill the stack into a variable, do not read the files more than once because than you get the same problem as before:
Stack<File> pictures = getFilesInFolder("C:\\Users\\Admin\\Desktop");
After that use a Action for your JMenuItem! In my example I do not have much, you have to put your methods in!
Action nextpictureaction = new AbstractAction("Next Picture") {
private static final long serialVersionUID = 2421742449531785343L;
#Override
public void actionPerformed(ActionEvent e) {
if (!pictures.isEmpty()) {
System.out.println(pictures.pop().getName());
}
}
};
Add the Action at your JMenu and set the properties of your Frame.
/*
* Add Components to Frame
*/
setJMenuBar(menubar);
menubar.add(toolsmenu);
toolsmenu.add(nextpictureaction);
/*
* Frame Properties
*/
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationByPlatform(true);
setSize(1000, 700);
setTitle("PictureEditor");
setVisible(true);
At the end execute your program with the invokeLater method!
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new PictureEditor();
}
});
}
Summary
Basically you need a somthing to iterate through because values like integer are not saved the way you like. In my example I used a Stack and save at the beginning all pictures in it. Important is that, if you used or finished with the picture, you have to remove it (use stack.pop() for a Stack). I do not found a method where you check if the file is a picture (if it is the ImageIO catch it is bad). I wrote a method for that if you want you could use it.
This is not an answer, but I cannot paste that much code into a comment.
I would change your code to something along the lines of this piece of code. This seperates the image loading from the gui updating logic (like adding mousehandlers and the like, I pasted only image loading code).
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import java.util.Iterator;
import javax.imageio.ImageIO;
public class ImageLoader
{
public static class ImageContainer
{
BufferedImage bg = null;
BufferedImage scaled;
}
Iterator<File> imageFiles = Arrays.asList(
new File("D:\\New folder").listFiles()).iterator();
public ImageContainer nextImage(Dimension dimensionToFit) throws Exception
{
ImageContainer c = new ImageContainer();
if (imageFiles.hasNext())
{
File file = imageFiles.next();
//you might not need this, if only images are in this directory
if(file.isDirectory())
return null;
c.bg = ImageIO.read(file);
c.scaled = getScaledInstanceToFit(c.bg, dimensionToFit);
return c;
}
else
return null;
}
private BufferedImage getScaledInstanceToFit(BufferedImage bg,
Dimension dimensionToFit)
{
//do the risizing
}
}
This is not yet optimized though.
Related
I am designing a matching memory game, I am almost done with it and everything is working as it should be, however, when the user has opened two different cards the program won't pause(wait) few seconds so the user can see what the second card was.
I have tried using a long for loop operation but encountered the same problem. I have tried Thread.sleep, TimeUnit.SECONDS.sleep, Task and Platform.runLater.
The program opens the card and closes it instantly THEN it waits for the specified duration, keeping in mind that I am calling pauseThread after open and before close functions.
I have tried the above suggestions but they are leading me no where and I can't seem to find where the problem is with my code or where should I place the pauseThread. Thanks in advance.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class MemoryMatchingGame extends Application{
private static Card selectedCard=null; // This is to save a reference for the first card to use in comparison
private static int numOfCorrectPairs = 0; // Keeping track of how many cards the user got correct
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
String[] images = {"C:\\Users\\userName\\Desktop\\Project#4\\1.png", // This is a string array to store images locations
"C:\\Users\\userName\\Desktop\\Project#4\\2.png",
"C:\\Users\\userName\\Desktop\\Project#4\\3.jpg",
"C:\\Users\\userName\\Desktop\\Project#4\\4.jpg",
"C:\\Users\\userName\\Desktop\\Project#4\\5.jpg",
"C:\\Users\\userName\\Desktop\\Project#4\\6.png",
"C:\\Users\\userName\\Desktop\\Project#4\\7.jpg",
"C:\\Users\\userName\\Desktop\\Project#4\\8.jpg"};
ArrayList<Card> listOfCards = new ArrayList<Card>();
for(int i=0; i<images.length; i++) { // This for loop will add each image twice to the array list
listOfCards.add(new Card(images[i]));
listOfCards.add(new Card(images[i]));
}
Collections.shuffle(listOfCards); // Shuffling the deck of cards
primaryStage.setTitle("Memory Matching Game");
HBox hb = new HBox();
VBox firstColoumn = new VBox();
for(int i=0; i<4; i++)
firstColoumn.getChildren().add(listOfCards.get(i));
VBox secondColoumn = new VBox();
for(int i=4; i<8; i++)
secondColoumn.getChildren().add(listOfCards.get(i));
VBox thirdColoumn = new VBox();
for(int i=8; i<12; i++)
thirdColoumn.getChildren().add(listOfCards.get(i));
VBox fourthColoumn = new VBox();
for(int i=12; i<16; i++)
fourthColoumn.getChildren().add(listOfCards.get(i));
hb.getChildren().addAll(firstColoumn, secondColoumn, thirdColoumn, fourthColoumn);
Scene scene = new Scene(hb, 460, 450);
primaryStage.setScene(scene);
primaryStage.show();
}
private class Card extends Button {
private String imageLocation; // To store the destination of the image
private Image img; // To store a reference of the image to be used when setting graphic on a button
public Card(String imageLocation) throws FileNotFoundException {
this.imageLocation = imageLocation;
FileInputStream fis = new FileInputStream(imageLocation);
img = new Image(fis);
setPrefSize(150, 150);
setOnMouseClicked(e -> {
if(isCardOpen()==true)
return; // To ensure no action is made once an image is already opened and the user clicked on it again
if(selectedCard==null) {// This will test if the user has a card open already for comparison or not, if not it will store a reference to the card to use to compare once another card is opened
selectedCard = this;
open();
}
else { // If we enter this statement, this means the user has a card open already and we are ready to perform comparison
open(); // First action taken is to reveal the second card then perform comparison
if(this.isEqual(selectedCard)) {
numOfCorrectPairs++;
System.out.println("Got one");
}
else {
//Get program to pause here
Hold pauseThread = new Hold();
pauseThread.run();
System.out.println("After pausing");
this.close();
selectedCard.close();
}
selectedCard=null; // This will nullify the variable so that we are able to perform comparison again for two other cards
} // End of else statement
}); // End of actionHandler
close(); // This will ensure whenever a card is created it is set face-down
}
private void close() {
setGraphic(null);
}
public void open() {
setGraphic(new ImageView(img));
System.out.println("Open");
}
private boolean isCardOpen() {
return this.getGraphic()!=null;
}
private boolean isEqual(Card selectedCard) {
return this.imageLocation.equals(selectedCard.imageLocation);
}
}
private class Hold extends Thread{
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Everything in your code is running in the JavaFX Application Thread. You don't want to pause this thread because it will lock your GUI. As has already been mentioned, you are starting another thread and putting it to sleep, but this doesn't add delay to your GUI that is running in the JavaFX Thread.
An alternative approach would be to use Platform.runLater(). The Hold thread can invoke a method in the JavaFX thread that implements a Platform.runLater() runnable. The runnable is a short lambda that holds the code to close the selected card. The timing may vary slightly from 3000 ms, but you don't have much going on in the JavaFX thread and it doesn't seem critical for this application.
Here are the modifications to try.
First modify the Hold class to include a constructor to pass in the Card object. Then call the closeAfterPause() method on card.
private class Hold extends Thread {
private Card card;
public Hold(Card card) {
this.card = card;
}
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
card.closeAfterPause();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Then create the closeAfterPause() method in the MemoryMatchingGame class.
private void closeAfterPause() {
Platform.runLater(() -> {
System.out.println("After Pausing");
close();
selectedCard.close();
});
}
Then modify the else part of your if-else statement as follows
else {
//Get program to pause here
Hold pauseThread = new Hold(this);
new Thread(pauseThread).start();
}
FX comes with a rich set of Animation/Timeline support - there's no need for falling back onto bare Threads. The most simple form of getting wait-for-xx is to use a Timeline configured with xx and an actionHandler that does something when ready:
Timeline holdTimer = new Timeline(new KeyFrame(
Duration.seconds(2), e -> closeCards()));
Also it's a good idea to centralize all logic outside of a Control. Actually, you should never-ever extend a view for the purpose to include view-unrelated logic. So your long-time goal should be to
extract all single card logic from Card into a CardModel which exposes properties like f.i. image, id, open, disposed
use a plain Button, configure and bind its properties to model properties as appropriate
centralize all game logic like timing, selecting, when opening is allowed, when succeed into a game controller
As I don't want to spoil your fun in doing that - I'll just post a little outline in the direction of the last bullet. Its responsibilities so far
provide api to open a single card
provide api to end a turn: either match or close cards
internals to keep track of opened cards and timing
The snippets just re-mixes your code a bit, moving game logic from the button into the controller (aka: here simply the outer class) and setting the button's action handler to access the controller.
private Card firstCard;
private Card secondCard;
private Timeline holdTimer = new Timeline(new KeyFrame(
Duration.millis(2000), e -> closeCards()));
public void closeCards() {
if (firstCard == null || secondCard == null) {
System.out.println("error!!");
return;
}
if (firstCard.isEqual(secondCard)) {
System.out.println("success");
firstCard.setDisable(true);
secondCard.setDisable(true);
firstCard = null;
secondCard = null;
} else {
firstCard.close();
secondCard.close();
firstCard = null;
secondCard = null;
}
}
public void openCard(Card card) {
if (card.isCardOpen()) return;
if (holdTimer.getStatus() == Status.RUNNING) return;
if (firstCard == null) {
firstCard = card;
firstCard.open();
} else if (secondCard == null) {
secondCard = card;
secondCard.open();
holdTimer.playFromStart();
}
}
// Dont! dont, dont!!! ever extend a Control
//**TBD**: Move open/close state logic into a CardModel
// then configure a plain Button with the properies of that model
private class Card extends Button {
private String imageLocation; // To store the destination of the image
// private Image img; // To store a reference of the image to be used when setting graphic on a button
public Card(String imageLocation) throws FileNotFoundException {
this.imageLocation = imageLocation;
setPrefSize(150, 150);
setOnAction(e -> openCard(this));
}
public void close() {
setText("");
}
public void open() {
setText(imageLocation);
System.out.println("Open");
}
public boolean isCardOpen() {
return getText() != null && getText().length() > 0;//this.getGraphic()!=null;
}
private boolean isEqual(Card selectedCard) {
if (selectedCard == null) return false;
return this.imageLocation.equals(selectedCard.imageLocation);
}
}
I'm using WorldWind and trying to "pick" multiple surface images in the same layer and not understanding why it isn't working.
I was under the impression that calling this:
this.getWwd().getSceneController().setDeepPickEnabled(true);
Would enable me to pick multiple renderables in the same layer. This seems to work for all other cases other than SurfaceImage. I also noticed if I force the loaded SurfaceImage into different layers it works as expected.
This is the code I'm using to test this out:
public class SurfaceImageViewer extends ApplicationTemplate
{
public static class AppFrame extends ApplicationTemplate.AppFrame
{
private JFileChooser fileChooser = new JFileChooser();
private JSlider opacitySlider;
private SurfaceImageLayer layer;
private JLabel statusLabel = new JLabel("status: ready");
public AppFrame()
{
super(true, true, false);
this.getWwd().getSceneController().setDeepPickEnabled(true);
try
{
this.layer = new SurfaceImageLayer();
this.layer.setOpacity(1);
this.layer.setPickEnabled(true);
this.layer.setName("Surface Images");
insertBeforeCompass(this.getWwd(), layer);
this.getControlPanel().add(makeControlPanel(), BorderLayout.SOUTH);
}
catch (Exception e)
{
e.printStackTrace();
}
this.getWwd().addSelectListener(new SelectListener() {
#Override
public void selected(SelectEvent event) {
PickedObjectList pol = AppFrame.this.getWwd().getObjectsAtCurrentPosition();
if(event.isLeftClick()){
System.out.println("POL SIZE "+pol.size());
}
}
});
}
Action openElevationsAction = new AbstractAction("Open Elevation File...")
{
public void actionPerformed(ActionEvent e)
{
int status = fileChooser.showOpenDialog(AppFrame.this);
if (status != JFileChooser.APPROVE_OPTION)
return;
final File imageFile = fileChooser.getSelectedFile();
if (imageFile == null)
return;
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
CompoundElevationModel cem
= (CompoundElevationModel) getWwd().getModel().getGlobe().getElevationModel();
LocalElevationModel em = new LocalElevationModel();
em.addElevations(imageFile.getPath());
cem.addElevationModel(em);
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
};
Action openImageAction = new AbstractAction("Open Image File...")
{
public void actionPerformed(ActionEvent actionEvent)
{
int status = fileChooser.showOpenDialog(AppFrame.this);
if (status != JFileChooser.APPROVE_OPTION)
return;
final File imageFile = fileChooser.getSelectedFile();
if (imageFile == null)
return;
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
statusLabel.setText("status: Loading image");
// TODO: proper threading
layer.addImage(imageFile.getAbsolutePath());
getWwd().redraw();
statusLabel.setText("status: ready");
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
};
private JPanel makeControlPanel()
{
JPanel controlPanel = new JPanel(new GridLayout(0, 1, 5, 5));
JButton openImageButton = new JButton(openImageAction);
controlPanel.add(openImageButton);
this.opacitySlider = new JSlider();
this.opacitySlider.setMaximum(100);
this.opacitySlider.setValue((int) (layer.getOpacity() * 100));
this.opacitySlider.setEnabled(true);
this.opacitySlider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = opacitySlider.getValue();
layer.setOpacity(value / 100d);
getWwd().redraw();
}
});
JPanel opacityPanel = new JPanel(new BorderLayout(5, 5));
opacityPanel.setBorder(new EmptyBorder(0, 10, 0, 0));
opacityPanel.add(new JLabel("Opacity"), BorderLayout.WEST);
opacityPanel.add(this.opacitySlider, BorderLayout.CENTER);
controlPanel.add(opacityPanel);
JButton openElevationsButton = new JButton(openElevationsAction);
controlPanel.add(openElevationsButton);
controlPanel.add(statusLabel);
controlPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
return controlPanel;
}
}
public static void main(String[] args)
{
ApplicationTemplate.start("World Wind Surface Images", SurfaceImageViewer.AppFrame.class);
}
}
These are 2 geotiffs that are layered on top of each other that I've been using to test this out. I would expect my println on the SelectListener to print out "3" when I single left click on both geotiffs. (I've uploaded the geotiffs into a zip available here)
The area where you will see these is in San Francisco, see screenshot:
Update:
It was discovered that the examples for Batch Picking were oriented around AbstractSurfaceObject instances, which did not apply in this case. For the handling of SurfaceImage instances the property for setAlwaysOnTop should be configured to false which appears to let the selection event process all elements under the cursor.
Reading through the examples for DeepPicking, there are actually 2 things that need to be done.
setDeepPickEnabled(true); //This is done.
Disable Batch picking on the desired elements
https://github.com/nasa/World-Wind-Java/blob/master/WorldWind/src/gov/nasa/worldwindx/examples/DeepPicking.java
In order to enable deep picking, any batch picking for the desired elements must be disabled and the
SceneController's deep picking property must be enabled. See {#link gov.nasa.worldwind.SceneController#setDeepPickEnabled(boolean)
Took me a little while to understand the second one, but it appears to be tied to the AbstractSurfaceObject class.
I am assuming that the things that you're drawing on the layer are a subclass of AbstractSurfaceObject
I believe that in this situation, I would subclass the SurfaceImageLayer, and override the addRenderable methods. I would check the renderable if it was an instance of an AbstractSurfaceObject, and disable batch picking on it before forwarding it to the super class.
This code may not be the best long-term solution, but it may provide quick results to determine if this is the underlying issue.
import gov.nasa.worldwind.layers.SurfaceImageLayer;
import gov.nasa.worldwind.render.AbstractSurfaceObject;
import gov.nasa.worldwind.render.Renderable;
/**
* Very Rough extension of SurfaceImageLayer which disables batch picking on all AbstractSurfaceobjects.
* #author http://stackoverflow.com/users/5407189/jeremiah
* #since Nov 26, 2016
*
*/
public class MySurfaceImageLayer extends SurfaceImageLayer {
#Override
public void addRenderable(Renderable renderable) {
if (renderable instanceof AbstractSurfaceObject) {
((AbstractSurfaceObject)renderable).setEnableBatchPicking(false);
}
super.addRenderable(renderable);
}
#Override
public void addRenderables(Iterable<? extends Renderable> renderables) {
for (Renderable r : renderables) {
addRenderable(r);
}
}
}
IF the thing you want to have picked is the image directly, that appears to not be supported out-of-the-box. You would need to do something to get the SurfaceImage references from the SurfaceImageLayer to be visible to the RenderableLayer on doPick. That may come with a new set of problems to watch out for.
As a side-note, if you're rendering Icons then all you need to do is set the IconRenderer.setAllowBatchPicking(false)
I hope that's at least somewhat helpful.
Best of Luck.
I want to display different images in a same frame within a loop. String pathName[] contains the different paths of the images. When running this code, only last image i.e image at path pathname[last] is getting displayed on frame instead I want all images to be displayed in a continuous way (have given delay of 1sec ). Help is appreciated.
public void actionPerformed(ActionEvent event) {
int i=0;
while(i<5){
if(i>0){
Container labelParent = receiverImageLabel.getParent();
labelParent.remove(receiverImageLabel);
labelParent.validate();
labelParent.repaint();
}
try {
imageR = ImageIO.read(new File(pathName[i++])).getScaledInstance(512,512 , BufferedImage.SCALE_SMOOTH);
receivedImage = new ImageIcon(imageR);
}catch (IOException e) {
e.printStackTrace();
}
receiverImageLabel = new JLabel(receivedImage);
receiverFrame.getContentPane().add(BorderLayout.EAST,receiverImageLabel);
receiverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
receiverFrame.setSize(800,700);
receiverFrame.setVisible(true);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Your problem is a common one: you're calling Thread.sleep(...) in a Swing GUI on the event thread and are essentially thus putting the entire GUI to sleep.
Solution: Google the Swing Timer and use this in place of your while loop/Thread.sleep(...)
Also, if the images aren't too big, then consider reading them all in at once (in a background thread), putting them into ImageIcons, and then swapping out a JLabel's ImageIconsand in your Swing Timer.
For example, you could do something like:
ImageIcon[] icons = new ImageIcon[IMAGE_COUNT];
for (int i = 0; i < IMAGE_COUNT; i++) {
BufferedImage img = ImageIO.read(...); // read in the appropriate image
// ...... here manipulate the image if desired such as re-size it
icons[i] = new ImageIcon(img); // put it into an icon
}
elsewhere:
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener(){
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (count < IMAGE_COUNT) {
someLabel.setIcon(icons[count]);
count++;
} else {
// stop the timer
((Timer)e.getSource()).stop();
}
}
}).start();
Note: code not compiled nor tested and is posted only as a general example of steps to consider.
I am putting together a slideshow program that will measure a user's time spent on each slide. The slideshow goes through several different magic tricks. Each trick is shown twice. Interim images are shown between the repetition. Transition images are shown between each trick.
On the first repetition of a trick the JPanel color flashes on the screen after a click before the next image is shown. This doesn't happen during the second repetition of the same trick. It's possible that the image is taking too long to load.
Is there an easy way to pre-load the images so that there isn't a delay the first time through?
NOTE: Original code deleted.
EDIT 1/10/2013: This code now works on slower machines. trashgod's second addendum helped the most. The mouseClick control structure periodically asks SwingWorker classes to load 40 images or less of the current trick while also setting the used images to null. I have simplified my code down for this to just two Image[]s and added a main method so it stands alone. Images are still required to run though. This is now pretty bare bones code, and if you're trying to make a slideshow with a lot of images I think it would be a good place to start.
NOTE: I think I figured out how to properly implement SwingWorker while still using multiple Image[]s. trashgod and kleopatra is this implementation in-line with what you were suggesting? I didn't end up using publish and process since I couldn't figure out how to get that to work appropriately with an indexed array, but because the StringWorker doesn't load all images in the array (only 40), and the code calls StringWorker every 20 images, there should be a pretty good buffer.
EDIT 1/10/2013 Changed out MouseListener by instead extending MouseAdapter on my Mouse class. Also fixed my paintComponent method to include a call to super.paintComponent(g).
Added publish/process methods to my SwingWorker class ImageWorker. Added a wrapper class, ArrayWrapper to allow passing imageArray[i] and its corresponding index int i with publish to process.
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
******************************************/
}
This example uses a List<ImageIcon> as a cache of images returned by getImage(). Using getResource(), the delay is imperceptible. The next and previous buttons are bound to the Space key by default.
Addendum: You can control navigation by conditioning a button's setEnabled() state using an instance of javax.swing.Timer, for example.
Addendum: Your second example waits until the mouse is clicked to begin reading an image, an indeterminate process that may return a copy immediately or may not complete until after repaint(). Instead, begin reading the images in the background using ImageIO.read(), as shown here. You can process() your List<Image> and show progress, as seen here. The SwingWorker can be launched from the initial thread, running while you subsequently build your GUI on the EDT. You can display the first image as soon as it is processed.
I am making an applet that displays a random collection of 10 cards based upon 10 random integers.
My idea was to make an array of the 52 displayable cards (not including jokers) and display each cards from the array based upon the random integer like this (sorry i don't know how to use code blocks):
for (int i = 0; i<cards.length; i++) { //cards being my image array
//code that displays each image
}
But I ran into trouble trying to add images into the array and I don't know how to display the images from the array either.
Should I be adding them like this:
Image[] cards = new Image[52];
cards[0] = c1; //name of the Ace of Clubs, I had used getImage() to already get it
The preceding statements throw up errors saying it's an illegal start.
I also need help with displaying the images once I incorporate the images since I don't think:
System.out.println(cards[x]);
Will work with images.
Thanks in advance and sorry for seeming so complicated, I've tried to water it down as much as possible!
So, here's my silly take on it...
public class RandomCards {
public static void main(String[] args) {
new RandomCards();
}
public RandomCards() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new RandomCardsPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
}
});
}
public class RandomCardsPane extends JPanel {
// A list is a collection of Image objects...
private List<Image> cardList;
private Image card = null;
public RandomCardsPane() throws IOException {
// My cards are stored in the default execution location of the program
// and are named "Card_1.png" through "Card_51.png"...
// You image loading process will be different, replace it here..
// ArrayList is a dynamic list (meaning it can grow and shrink
// over the life time of the list) and is backed by an array
// which shouldn't concern you, the only thing you really need to
// know is that it has excellent random access...
cardList = new ArrayList<Image>(51);
for (int index = 0; index < 51; index++) {
cardList.add(ImageIO.read(new File("Card_" + index + ".png")));
}
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
card = cardList.get(Math.min((int)Math.round(Math.random() * cardList.size()), 51));
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (card != null) {
int x = (getWidth() - card.getWidth(this)) / 2;
int y = (getHeight() - card.getHeight(this)) / 2;
g.drawImage(card, x, y, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(225, 315);
}
}
}
I would also prefer ImageIO over Toolkit.getImage or even ImageIcon, apart from the fact that it guarantees the image data is loaded BEFORE the method returns, it also supports a greater number of image formats and is extendable via plugins...