As a java non-expert, I would like to know how to change the code below to get it to work. Here is what I want to do
When the java code is called args contains several image filenames
I want to see the first image in this list
Then when I press a key, an index is changed, and the next image is displayed.
Using the suggestions made below, here is a compilable, running piece of code:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class LoadImageApp extends Component {
BufferedImage img;
private static int index = 0;
public void paint(Graphics g) {
g.drawImage(img, 0, 0, null);
}
public LoadImageApp(String filename) {
try {
img = ImageIO.read(new File(filename));
} catch (IOException e) {
System.out.println(e.getMessage());
System.exit(0);
}
}
public Dimension getPreferredSize() {
if (img == null) {
return new Dimension(100,100);
} else {
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
static public void changeImage(JFrame frame, String filename) {
System.out.println("Using file "+filename);
frame.add(new LoadImageApp(filename));
frame.pack();
frame.setVisible(true);
frame.repaint();
}
public static void main(final String[] args) {
char c=0;
String filename = args[0];
int numberImages = args.length;
final JFrame f = new JFrame("Load Image Sample");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
f.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent event) {
int key = event.getKeyCode();
if (key==81) {
System.exit(0);
} else if (key==89) {
System.out.println("yes");
} else if (key==78) {
System.out.println("no");
}
index += 1;
f.removeAll();
changeImage(f, args[index]);
}
});
// show first image here
changeImage(f, args[index]);
}
}
If I use use the code like
java LoadImageApp *.jpg
it only displays the first image. I can press keys, but the image shown does not change. I want to have the image changed.
I have found revalidate() and repaint() as possible solutions. Despite frame.revalidate() does not exist at all, frame.repaint() (inside changeImage) does still not change anything. I still see the first image displayed.
Is this the right approach anyway? Is there a more elegant way?
I wrote a program to demonstrate what you asked for, here is the code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageShow {
/** Inner class: JPanel that displays images. **/
static class JImagePanel extends JPanel {
protected final LinkedList<BufferedImage> images;
protected BufferedImage currentImage;
protected int currentIndex;
public JImagePanel(final LinkedList<BufferedImage> images) {
super(true);
this.setFocusable(false);
this.images = images;
this.setIndex(0);
}
/** Has to be private to not cause issues when used in the constructor. **/
private void setIndex(final int index) {
if (index >= this.images.size()) {
this.currentIndex = 0;
} else if (index < 0) {
this.currentIndex = this.images.size() - 1;
} else {
this.currentIndex = index;
}
this.currentImage = this.images.get(this.currentIndex);
this.setPreferredSize(new Dimension(this.currentImage.getWidth(), this.currentImage.getHeight()));
this.repaint();
}
public void shiftIndex(final int amount) {
this.setIndex(this.currentIndex + amount);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(this.currentImage, 0, 0, null);
}
}
public static void main(final String[] args) {
final LinkedList<BufferedImage> images = loadImages(args);
if (images.size() > 0) {
final JFrame window = new JFrame("Image Show");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JImagePanel imagePanel = new JImagePanel(images);
window.add(imagePanel);
window.addKeyListener(new KeyAdapter() {
private void shiftIndex(final int amount) {
imagePanel.shiftIndex(amount);
window.pack();
window.setLocationRelativeTo(null);
}
#Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ESCAPE:
window.dispose();
e.consume();
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_NUMPAD4:
shiftIndex(-1);
e.consume();
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_NUMPAD6:
shiftIndex(+1);
e.consume();
break;
}
}
});
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
} else {
System.err.println("No image could be loaded.\nPlease provide a list of image files as parameters.");
}
}
private static LinkedList<BufferedImage> loadImages(final String[] filenames) {
final LinkedList<BufferedImage> result = new LinkedList<>();
for (String filename : filenames) {
try {
final File file = new File(filename);
final BufferedImage image = ImageIO.read(file);
if (image == null) {
throw new IOException("Unknown image format");
}
result.add(image);
} catch (IOException e) {
System.err.println("Unable to load image \"" + filename + "\": " + e.getMessage());
}
}
return result;
}
}
Please note that this is not the most beautiful way of writing this tool, however it works.
What you should usually do:
Each class should be in its own .java file. The idea is to have a structure, that is easy to read even if you re-visit this code 3 years later.
You should not use variables from another scope like I did here with the window and imagePanel in the main function. Instead use constructors that store local variables with either the value given or a copy of the value (depending on your needs), like I did in the JImagePanel constructor.
Whether or not you need a copy of the value depends on what you do and how much risk you are willing to take. In this example changing the image list after JImagePanel is created would potentially mess things up.
You should never use numbers like you did in your version of the key listener. You never know which key-code corresponds to which key! Whenever available use the provided constants or functions to get such a 'magic' number.
Always expect the worst when it comes to error handling. For once try to catch and handle all possible errors. For second always try to avoid potential issues. A bug you cannot make, is a bug you won't have to bother about.
In your version the image file is loaded from disk every time a button is pressed. What happens if the image is no longer present at that moment? In my version everything is checked before-hand, and once that is done, the program cannot fail anymore (at least not when trying to switch images). ;)
In general: try to find a good book or online tutorial on Java for beginners. If you just hack away, you will miss all those nice things Java has already prepared for you, that will not only speed up developing a lot, it will as well prevent all bugs that you otherwise might code in.
The keyword final indicates that the variable will not change after it has been initialized. In order to use a variable within an anonymous inner class i.e. KeyAdapter it has to be declared final.
So you need:
public static void main(final String[] args) {
and
final JFrame f = new JFrame("Load Image Sample");
But this will not work for index as you are planning on changing it. So I recommed declaring it as a static variable on class level i.e. outside of the function.
private static int index = 0;
This is more or less what you want. I recommend you to study Java from scratch and fill the gap you have.
public class LoadImageApp extends JPanel {
//Current image the Canvas shows
private Image currentImage;
//change the current image and repaint
public void setCurrentImage(Image currentImage) {
this.currentImage = currentImage;
repaint();
}
#Override
public void paintComponent(Graphics g) {
g.clearRect(0, 0, getWidth(), getHeight());
if (currentImage != null)
g.drawImage(currentImage, 0, 0, null);
}
#Override
public Dimension getPreferredSize() {
if (currentImage == null) {
return new Dimension(100, 100);
} else {
return new Dimension(currentImage.getWidth(null), currentImage.getHeight(null));
}
}
public static void main(final String[] args) throws IOException {
if (args.length > 0){
final Image [] images = new Image[args.length];
for (int i = 0; i < images.length; i++){
images[i] = ImageIO.read(new File(args[i]));
}
//It is a goog practice to attach your code to AWT thread.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
LoadImageApp app = new LoadImageApp();
JFrame f = new JFrame("Load Image Sample");
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(app, BorderLayout.CENTER);
f.addKeyListener(new MyKeyAdapter(app, images));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
}
// We create another class for the KeyAdapter
class MyKeyAdapter extends KeyAdapter{
//The canvas so we can tell it that the current image has change
private LoadImageApp canvas;
//index of the current image
private int index;
//All the images
private Image [] images;
public MyKeyAdapter(LoadImageApp canvas, Image[] images) {
super();
this.canvas = canvas;
this.images = images;
rotateRight();
}
#Override
public void keyPressed(KeyEvent event) {
int key = event.getKeyCode();
if (key == 81) {
System.exit(0);
} else if (key == KeyEvent.VK_LEFT) {
rotateLeft();
} else if (key == KeyEvent.VK_RIGHT) {
rotateRight();
}
}
private void rotateRight() {
//change the image in the canvas
canvas.setCurrentImage(images[index]);
//increment index
index++;
//last element + 1, set it to 0
if (index >= images.length) index = 0;
}
private void rotateLeft() {
//change the image in the canvas
canvas.setCurrentImage(images[index]);
//decrement index
index--;
//< 0, set it to last image
if (index < 0) index = images.length - 1;
}
}
Related
I've got a JList whose elements consist of image files for which I'm creating thumbnails (in a background Thread). When these thumbnails become available, I'd like to force a repaint of just that item. However, I find that when I use the listModel's fireDataChanged method (see below), all the visible items in the list are repainted (using my custom ListCellRenderer).
public void updateElement(int index) {
frame.listModel.fireContentsChanged(frame.listModel, index, index);
}
Is there any way to cause ONLY the indexed item to be repainted?
Without some kind of runnable example which demonstrates your issue, it's impossible to make any concrete recommendations.
The following simple example makes use of a SwingWorker to change the value of the elements within the ListModel. To make it look more realistic, I've shuffled the List of indices and applied a short delay between each.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultListModel<String> model = new DefaultListModel<>();
public TestPane() {
setLayout(new BorderLayout());
add(new JScrollPane(new JList(model)));
JButton load = new JButton("Load");
add(load, BorderLayout.SOUTH);
load.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
load.setEnabled(false);
model.removeAllElements();
for (int index = 0; index < 100; index++) {
model.addElement("[" + index + "] Loading...");
}
LoadWorker worker = new LoadWorker(model);
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
System.out.println(evt.getPropertyName() + " == " + evt.getNewValue());
if ("state".equals(evt.getPropertyName())) {
Object value = evt.getNewValue();
if (value instanceof SwingWorker.StateValue) {
SwingWorker.StateValue stateValue = (SwingWorker.StateValue) value;
if (stateValue == SwingWorker.StateValue.DONE) {
load.setEnabled(true);
}
}
}
}
});
worker.execute();
}
});
}
}
public class LoadResult {
private int index;
private String value;
public LoadResult(int index, String value) {
this.index = index;
this.value = value;
}
public int getIndex() {
return index;
}
public String getValue() {
return value;
}
}
public class LoadWorker extends SwingWorker<Void, LoadResult> {
private DefaultListModel model;
public LoadWorker(DefaultListModel model) {
this.model = model;
}
public DefaultListModel getModel() {
return model;
}
#Override
protected void process(List<LoadResult> chunks) {
for (LoadResult loadResult : chunks) {
model.set(loadResult.index, loadResult.value);
}
}
#Override
protected Void doInBackground() throws Exception {
int count = model.getSize();
List<Integer> indicies = new ArrayList<>(count);
for (int index = 0; index < count; index++) {
indicies.add(index);
}
Collections.shuffle(indicies);
for (int index : indicies) {
Thread.sleep(15);
publish(new LoadResult(index, "[" + index + "] Has been loaded"));
}
return null;
}
}
}
The above is a linear progression, meaning it's processing each item in sequence, one at a time.
Because image loading can take time and is CPU intensive process, you could make use of a ExecutorService and use a pool of threads to help spread the load.
For example:
Java - Multithreading with ImageIO
Make images fetched from server display in real time
Extracting images from a text file of 100 image urls using java
I find that when I use the listModel's fireDataChanged method (see below), all the visible items in the list are repainted
You should NOT invoke that method manually. The fireXXX(...) methods should only be invoked by the model itself.
You should be updating the model by using the:
model.set(...);
The set(...) method will then invoke the appropriate method to notify the JList to repaint the cell.
Here is my attempt at a simple Thumbnail app. It attempts to add performance improvements by:
loading the model with a default Icon so the list doesn't continually need to resize itself
Use a ExecutorService to take advantage of multiple processors
Using an ImageReader to read the file. The sub sampling property allows you to use fewer pixels when scaling the image.
Just change the class to point to a directory containing some .jpg files and give it a go:
ThumbnailApp:
import java.io.*;
import java.util.concurrent.*;
import java.awt.*;
//import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;
class ThumbnailApp
{
private DefaultListModel<Thumbnail> model = new DefaultListModel<Thumbnail>();
private JList<Thumbnail> list = new JList<Thumbnail>(model);
public ThumbnailApp()
{
}
public JPanel createContentPane()
{
JPanel cp = new JPanel( new BorderLayout() );
list.setCellRenderer( new ThumbnailRenderer<Thumbnail>() );
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
Icon empty = new EmptyIcon(160, 160);
Thumbnail prototype = new Thumbnail(new File("PortugalSpain-000.JPG"), empty);
list.setPrototypeCellValue( prototype );
cp.add(new JScrollPane( list ), BorderLayout.CENTER);
return cp;
}
public void loadImages(File directory)
{
new Thread( () -> createThumbnails(directory) ).start();
}
private void createThumbnails(File directory)
{
try
{
File[] files = directory.listFiles((d, f) -> {return f.endsWith(".JPG");});
int processors = Runtime.getRuntime().availableProcessors();
ExecutorService service = Executors.newFixedThreadPool( processors - 2 );
long start = System.currentTimeMillis();
for (File file: files)
{
Thumbnail thumbnail = new Thumbnail(file, null);
model.addElement( thumbnail );
// new ThumbnailWorker(file, model, model.size() - 1).execute();
service.submit( new ThumbnailWorker(file, model, model.size() - 1) );
}
long duration = System.currentTimeMillis() - start;
System.out.println(duration);
service.shutdown();
}
catch(Exception e) { e.printStackTrace(); }
}
private static void createAndShowGUI()
{
ThumbnailApp app = new ThumbnailApp();
JFrame frame = new JFrame("ListDrop");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane( app.createContentPane() );
frame.setSize(1600, 900);
frame.setVisible(true);
// File directory = new File("C:/Users/netro/Pictures/TravelSun/2019_01_Cuba");
File directory = new File("C:/Users/netro/Pictures/TravelAdventures/2018_PortugalSpain");
app.loadImages( directory );
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
ThumbnailWorker:
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
//import java.util.concurrent.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;
class ThumbnailWorker extends SwingWorker<Image, Void>
{
private File file;
private DefaultListModel<Thumbnail> model;
private int index;
public ThumbnailWorker(File file, DefaultListModel<Thumbnail> model, int index)
{
this.file = file;
this.model = model;
this.index = index;
}
#Override
protected Image doInBackground() throws IOException
{
// Image image = ImageIO.read( file );
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = readers.next();
ImageReadParam irp = reader.getDefaultReadParam();
// irp.setSourceSubsampling(10, 10, 0, 0);
irp.setSourceSubsampling(5, 5, 0, 0);
ImageInputStream stream = new FileImageInputStream( file );
reader.setInput(stream);
Image image = reader.read(0, irp);
int width = 160;
int height = 90;
if (image.getHeight(null) > image.getWidth(null))
{
width = 90;
height = 160;
}
BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = scaled.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
image = null;
return scaled;
}
#Override
protected void done()
{
try
{
ImageIcon icon = new ImageIcon( get() );
Thumbnail thumbnail = model.get( index );
thumbnail.setIcon( icon );
model.set(index, thumbnail);
System.out.println("finished: " + file);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
ThumbnailRenderer
import java.awt.Component;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class ThumbnailRenderer<E> extends JLabel implements ListCellRenderer<E>
{
public ThumbnailRenderer()
{
setOpaque(true);
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
setHorizontalTextPosition( JLabel.CENTER );
setVerticalTextPosition( JLabel.BOTTOM );
setBorder( new EmptyBorder(4, 4, 4, 4) );
}
/*
* Display the Thumbnail Icon and file name.
*/
public Component getListCellRendererComponent(JList<? extends E> list, E value, int index, boolean isSelected, boolean cellHasFocus)
{
if (isSelected)
{
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
}
else
{
setBackground(list.getBackground());
setForeground(list.getForeground());
}
//Set the icon and filename
Thumbnail thumbnail = (Thumbnail)value;
setIcon( thumbnail.getIcon() );
setText( thumbnail.getFileName() );
return this;
}
}
Thumbnail:
import java.io.File;
import javax.swing.Icon;
public class Thumbnail
{
private File file;
private Icon icon;
public Thumbnail(File file, Icon icon)
{
this.file = file;
this.icon = icon;
}
public Icon getIcon()
{
return icon;
}
public void setIcon(Icon icon)
{
this.icon = icon;
}
public String getFileName()
{
return file.getName();
}
}
I tested on a directory with 302 images. Using the ExecutorService got the load time down from 2:31 to 0:35.
I've tried looking at the Oracle advice on Writing/Saving an image but nothing I do works. I have a start button that starts the blend of 2 images from one to another. Then a stop button to stop the blend midway (or wherever you want) then I have a saveImage button and would like it capture the image in it's current state and save it to file. But how ? here's the code from the Frame.java
import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.Timer;
class Frame extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private Image MeSmaller1;
private Image MeSmaller2;
protected Timer timer;
private float alpha;
JPanel pnlButton = new JPanel();
static JButton btnStartBlend = new JButton("Start Blend");
static JButton btnStopBlend = new JButton("Stop Blend");
static JButton saveImage = new JButton("Save Image To File");
public Frame() {
loadImages();
initTimer();
pnlButton.add(btnStartBlend);
this.add(pnlButton);
pnlButton.add(btnStopBlend);
this.add(pnlButton);
pnlButton.add(saveImage);
this.add(pnlButton);
addListeners();
}
// start button actionlistener
public void addListeners() {
btnStartBlend.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
initTimer();
timer.start();
System.out.println("Timer started");
}
});
// stop button actionlistener
btnStopBlend.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// initTimer();
timer.stop();
System.out.println("Timer stopped");
}
});
// Save button actionlistener
saveImage.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Save image clicked");
}
});
}
private void loadImages() {
MeSmaller1 = new ImageIcon("MeSmaller1.jpg").getImage();
MeSmaller2 = new ImageIcon("MeSmaller2.jpg").getImage();
}
public void initTimer() {
timer = new Timer(1000, this);
timer.start();
timer.stop();
alpha = 1f;
}
private void doDrawing(Graphics g) {
Graphics2D g2Dim = (Graphics2D) g;
// below sets the size of picture
BufferedImage buffImage = new BufferedImage(400, 600,
BufferedImage.TYPE_INT_ARGB);
Graphics2D gBuffI = buffImage.createGraphics();
AlphaComposite aComp = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha);
// decides where images are drawn in JFrame
gBuffI.drawImage(MeSmaller1, 28, 55, null);
gBuffI.setComposite(aComp);
gBuffI.drawImage(MeSmaller2, 30, 48, null);
g2Dim.drawImage(buffImage, 10, 10, null);
}
public static void saveToFile(BufferedImage img) {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
#Override
public void actionPerformed(ActionEvent e) {
alpha -= 0.1;
if (alpha <= 0) {
alpha = 0;
timer.stop();
System.out.println("Morph Finished please restart.");
}
repaint();
}
}
Can anyone help here it's just not working. There is also another class PictureMorph.java
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PictureMorph extends JFrame {
private static final long serialVersionUID = 1L;
public PictureMorph() {
initUI();
}
private void initUI() {
JFrame frame = new JFrame ("Image Morph");
setTitle("Facial Image Manipulation");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new Frame());
// below set Frame Size around image
setSize(380, 470);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
PictureMorph picMorph = new PictureMorph();
picMorph.setVisible(true);
}
});
}
}
And 3 classes (that I think could be redundant now actually?) called BtnStartBlendListener, BtnStopBlendListener, SaveImageListener that don't contain much code at all. Can anyone help ?
To use a JFileChooser and end up with a user friendly file selector, you may do this:
private static Frame workFrame;
public static Frame getWorkFrame() {
return workFrame;
}
public static void setWorkFrame(Frame frame) {
workFrame = frame;
}
Then modify the save method the following way (the signature changes and also I have commented the code that uses the Scanner and replace it by the JFileChooser)
public static void save(BufferedImage img, Frame frame) {
// Scanner scan = new Scanner(System.in);
// System.out.println("Enter the file name: ");
// String fileFullPath = scan.next();
String fileFullPath = getFileToSaveTo(frame);
file = new File(fileFullPath);
saveToFile(img, file);
}
Add the following method:
public static String getFileToSaveTo(Frame frame) {
JFileChooser fc=new JFileChooser();
int returnVal=fc.showOpenDialog(frame);
if (returnVal == JFileChooser.APPROVE_OPTION) {
return fc.getSelectedFile().getAbsolutePath();
}
else {
return null;
}
}
In your main method have a call like Frame.setWorkFrame(fr);where fr is defined as Frame fr = new Frame();
This should make it work with the JFileChooser. For example here is how I call the program in my main method
public static void main(String[] args) {
JFrame theFrame = new JFrame("Testing catess...");
theFrame.setSize(400, 400);
Frame fr = new Frame();
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.add(fr, BorderLayout.CENTER);
theFrame.setVisible(true);
Frame.setWorkFrame(fr);
}
It is working fine in my tests. Test and let me know if it works
Since you have left the saveToFile method empty, I assume that it is the major issue that you have.
For the saveToFile method you can use javax.imageio.ImageIO. I would suggest that you pass a File object parameter to your method so it knows where to save the image.
public static void saveToFile(BufferedImage img, File file) {
String filename = file.getName();
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
suffix = suffix.toLowerCase();
if (suffix.equals("jpg") || suffix.equals("png")) {
try { ImageIO.write(img, suffix, file); }
catch (IOException e) { e.printStackTrace(); }
}
else {
System.out.println("Error: filename must end in .jpg or .png");
}
}
Hope this helps
[EDIT]
The File constructor allows you to instanciate the File object with a string representing the path to the file.
Example: assuming I want to save it in, let's say C:\Tests\image1.jpg, I would do this
File myFile = new File("C:\\Tests\\image1.jpg")
,where Tests represents an existing folder and image1.jpg is of course the name of the new file in which you will save your image.
So assume that img is a variable containing the reference to your BufferedImage object you would call the above method with
Frame.saveToFile(img, myFile);
where myFile is the above File object.
You can also add a static method such as the following one to get the user to specify the path to the file. I used Scanner to get it quick, but for a more user - friendly dialog, use a JFileChooser (examples are here)
public static void save(BufferedImage img) {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the file name: ");
String fileFullPath = scan.next();
File file = new File(fileFullPath);
saveToFile(img, file);
}
The issues you experienced are mostly related to the fact that you declared final some of the variables, notably the BufferedImage and the File object (which were null and since you make them final their values can't be changed)
I corrected these issues. Here is the whole code, except the imports statements which are the same as those you had in the link to your source code.
After you click on the save button, enter a full path to the file to save: example C:\test1.jpg in the console.
class Frame extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
static BufferedImage bufferedImage = null; // Don't need to make this final
static File file = null; // Don't need to make this final
private Image MeSmaller1;
private Image MeSmaller2;
protected Timer timer;
private float alpha;
JPanel pnlButton = new JPanel();
static JButton btnStartBlend = new JButton("Start Blend");
static JButton btnStopBlend = new JButton("Stop Blend");
static JButton saveImage = new JButton("Save Image To File");
public Frame() {
loadImages();
initTimer();
pnlButton.add(btnStartBlend);
this.add(pnlButton);
pnlButton.add(btnStopBlend);
this.add(pnlButton);
pnlButton.add(saveImage);
this.add(pnlButton);
addListeners();
}
// start button action listener
public void addListeners() {
btnStartBlend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
initTimer();
timer.start();
System.out.println("Timer started");
}
});
// stop button actionlistener
btnStopBlend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// initTimer();
timer.stop();
System.out.println("Timer stopped");
}
});
// Save button actionlistener
saveImage.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent saveImage) {
save(bufferedImage);
System.out.println("Save image clicked");
}
});
}
private void loadImages() {
// I did my local test using
//MeSmaller1 = new ImageIcon("C:\\Tests\\Alain_Lompo.jpg").getImage();
//MeSmaller2 = new ImageIcon("C:\\Tests\\Alain_Lompo.jpg").getImage();
MeSmaller1 = new ImageIcon("MeSmaller1.jpg").getImage();
MeSmaller2 = new ImageIcon("MeSmaller2.jpg").getImage();
}
public void initTimer() {
timer = new Timer(1000, this);
timer.start();
timer.stop();
alpha = 1f;
}
private void doDrawing(Graphics g) {
Graphics2D g2Dim = (Graphics2D) g;
// below sets the size of picture
bufferedImage = new BufferedImage(400, 600,
BufferedImage.TYPE_INT_ARGB);
Graphics2D gBuffI = bufferedImage.createGraphics();
AlphaComposite aComp = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha);
// decides where images are drawn in JFrame
gBuffI.drawImage(MeSmaller1, 28, 55, null);
gBuffI.setComposite(aComp);
gBuffI.drawImage(MeSmaller2, 30, 48, null);
g2Dim.drawImage(bufferedImage, 10, 10, null);
}
public static void save(BufferedImage img) {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the file name: ");
String fileFullPath = scan.next();
file = new File(fileFullPath);
saveToFile(img, file);
}
public static void saveToFile(BufferedImage img, File file) {
String filename = file.getName();
String suffix = filename.substring(filename.lastIndexOf('.') + 1);
suffix = suffix.toLowerCase();
if (suffix.equals("jpg") || suffix.equals("png")) {
try { ImageIO.write(img, suffix, file); }
catch (IOException e) { e.printStackTrace(); }
}
else {
System.out.println("Error: filename must end in .jpg or .png");
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
public void actionPerformed(ActionEvent e) {
alpha -= 0.1;
if (alpha <= 0) {
alpha = 0;
timer.stop();
System.out.println("Morph Finished please restart.");
}
repaint();
}
public static void main(String[] args) {
JFrame theFrame = new JFrame("Testing catess...");
theFrame.setSize(400, 400);
Frame fr = new Frame();
theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theFrame.add(fr, BorderLayout.CENTER);
theFrame.setVisible(true);
}
}
How do i get an java applet to search an image from the specified path. I want to make a simple imageviewer applet which has 2 buttons NEXT,PREV . By clicking Next it should show next image and by clicking prev it should show the previous image.
i have written the following code .Please help me in writing the code for the updatenext() and updateprev() function.
public class pic extends Applet implements ActionListener
{
Button prev,next;
private static final String PREV="Previous";
private static final String NEXT="Next";
Image img;
public void init()
{
img=getImage(getCodeBase(),"image1.jpg");
prev = new Button();
prev.setLabel(PREV);
prev.setActionCommand(PREV);
prev.addActionListener(this);
add(prev);
next = new Button();
next.setLabel(NEXT);
next.setActionCommand(NEXT);
next.addActionListener(this);
add(next);
}
public void paint(Graphics g)
{
g.drawImage(img,0,0,600,700,this);
}
public void actionPerformed(ActionEvent e)
{
if(e.getActionCommand().equals(NEXT))
updatenext();
else if(e.getActionCommand().equals(PREV))
updateprev();
}
void updatenext()
{
//updateImagehere
}
void updateprev()
{
//updateimage here
repaint();
}
}
A very simple example is this.
import java.applet.*;
import java.awt.*;
public class AppletImage extends Applet{
Image img;
MediaTracker tr;
public void paint(Graphics g) {
tr = new MediaTracker(this);
img = getImage(getCodeBase(), "demoimg.gif");
tr.addImage(img,0);
g.drawImage(img, 0, 0, this);
}
public static void main(String args[]){
AppletImage appletImage = new AppletImage();
appletImage.setVisible(true);
}
}
try this and ya u can take image according to your self.
If you want to get a list of the files in the location of your applet you can create a File object of the current directory with:
File currentDir = new File(getCodeBase().getPath());
Then to iterate over the files call:
for (File file : currentDir.listFiles()) {
// do something with the file
}
You will need to filter out directories and non image files, I would suggest you use a FileNameFilter as an argument when calling listFiles().
EDIT:
Here's an example of the solution you could use with this method:
public class pic extends Applet implements ActionListener
{
Button prev, next;
private static final String PREV = "Previous";
private static final String NEXT = "Next";
Image img;
private File[] imageFiles;
private int currentIndex = 0;
public void init()
{
File baseDir = new File(getCodeBase().getPath());
imageFiles = baseDir.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
String image = ".*\\.(png|jpg|jpeg|gif)";
Pattern pattern = Pattern.compile(image);
Matcher matcher = pattern.matcher(name.toLowerCase());
return matcher.matches();
}
});
setImage();
prev = new Button();
prev.setLabel(PREV);
prev.setActionCommand(PREV);
prev.addActionListener(this);
add(prev);
next = new Button();
next.setLabel(NEXT);
next.setActionCommand(NEXT);
next.addActionListener(this);
add(next);
}
public void paint(Graphics g)
{
g.drawImage(img, 0, 0, 600, 700, this);
}
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand().equals(NEXT))
updatenext();
else if (e.getActionCommand().equals(PREV))
updateprev();
}
private void setImage() {
img = getImage(getCodeBase(), imageFiles[currentIndex].getName());
}
void updatenext()
{
// needs circular increment
if (currentIndex == imageFiles.length - 1) {
currentIndex = 0;
} else {
currentIndex++;
}
setImage();
repaint();
}
void updateprev()
{
// needs circular decrement
if (currentIndex == 0) {
currentIndex = imageFiles.length-1;
} else {
currentIndex--;
}
setImage();
repaint();
}
}
I am trying to create an function with an animation in java(swing). I want to create a "stack" of cards and then be able to draw one card from the stack using a method. The card i draw should then be "fliped" to reveal the front side.
This is working fine unless when I am trying to draw a new card using listeners.
I start the program by building the stack then drawing one card. Then I have a button that draws a new card(actionlistener). The animation does not show however i can see that the stack decreases by one card. I have tried to create a gif to show the problem.
Gif file showing the problem http://people.dsv.su.se/~chra4852/gif.gif
My best guess would be that it has something to do with threads or the EDT?
Here is the code(I have removed the use of the Scalr class since its not important) :
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
// import org.imgscalr.Scalr; Removed for Stackoverflow
public class Test extends JFrame{
public static JPanel cardPanel = new JPanel();
public static JLayeredPane layerPane = new JLayeredPane();
private final CardFlipp cardFlipp;
Test() {
cardFlipp = new CardFlipp();
this.setLayout(new BorderLayout());
layerPane.setPreferredSize(new Dimension(700, 300));
add(cardPanel, BorderLayout.CENTER);
JButton newJButton = new JButton("New card");
newJButton.addActionListener(new drawNewCardListener());
add(newJButton, BorderLayout.SOUTH);
setSize(800,400);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
Point pos = new Point(10,30);
cardFlipp.createCardFlipp(30, pos, layerPane, cardPanel);
System.out.println("Now running on thread " + Thread.currentThread());
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
cardFlipp.displayCard("http://imgi.se/image.php?di=IYW9");
}
public static void main(String[] args){
new Test();
}
class drawNewCardListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("Now running on thread " + Thread.currentThread());
cardFlipp.displayCard("http://imgi.se/image.php?di=IYW9");
}
}
}
class CardFlipp extends JPanel{
private Point origin;
private BufferedImage icon;
private JLayeredPane lPane;
private JPanel jPanel;
private int lastCardDisplayedId = 0;
public void createCardFlipp(int amount, Point pos, JLayeredPane lPaneRef, JPanel jPanelRef) {
origin = pos;
setIcon("http://imgi.se/image.php?di=HPCJ");
lPane = lPaneRef;
jPanel = jPanelRef;
for(int i = amount; i > 0; i--) {
origin.x += 3;
lPane.add(new Card(origin, icon, i), new Integer(amount-i));
jPanel.add(lPane);
lPane.revalidate();
lPane.repaint();
try {
Thread.sleep(80);
} catch (InterruptedException ex) {
Logger.getLogger(CardFlipp.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void displayCard(String fileUrl) {
Card c = (Card) lPane.getComponent(0);
if(lastCardDisplayedId == 0) {
origin = new Point(c.getBounds().x + 370, c.getBounds().y);
}
int cardId = c.getId();
setIcon(fileUrl);
c.flippCardRight(lastCardDisplayedId);
lPane.remove(c);
if(lastCardDisplayedId != 0) {
for(int i = 0; i < lPane.getComponentCount(); i++) {
c = (Card) lPane.getComponent(i);
if(c.getId() == lastCardDisplayedId) {
lPane.remove(c);
break;
}
}
}
lPane.repaint();
lPane.add(new Card(origin, icon, cardId), new Integer(0));
jPanel.add(lPane);
lastCardDisplayedId = cardId;
}
private void setIcon(String urlPath) {
try {
URL url = new URL(urlPath);
icon = ImageIO.read(url);
} catch (IOException e) {
System.err.println(e);
System.exit(-1);
icon = null;
}
}
}
class Card extends JComponent {
private BufferedImage buffImg;
private ImageIcon iconImg;
private final int id;
public Card(Point origin, BufferedImage img, int count) {
buffImg = img;
iconImg = new ImageIcon(buffImg);
setBounds(origin.x,origin.y,iconImg.getIconWidth(),iconImg.getIconHeight());
this.id = count;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(iconImg.getImage(), 0, 0, this);
}
public void flippCardRight(int count) {
int x = count * 3;
int newPosX = getBounds().x + iconImg.getIconWidth() + 20;
while(getBounds().x < newPosX + x) {
setBounds(getBounds().x+5,getBounds().y,iconImg.getIconWidth(),iconImg.getIconHeight());
wait10MiliSec();
}
// Removed to not need the scalr class
//int minimize = 10;
//while(iconImg.getIconWidth() > minimize) {
//buffImg = Scalr.resize(buffImg, Scalr.Method.SPEED, Scalr.Mode.FIT_EXACT, //iconImg.getIconWidth()-minimize, iconImg.getIconHeight(), Scalr.OP_ANTIALIAS);
//iconImg = new ImageIcon(buffImg);
//setBounds(getBounds().x+minimize-1,getBounds().y,iconImg.getIconWidth(),iconImg.getIconHeight());
//wait10MiliSec();
//}
}
private void wait10MiliSec() {
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(Card.class.getName()).log(Level.SEVERE, null, ex);
}
}
public int getId() {
return this.id;
}
public int getImgWidth() {
return iconImg.getIconWidth();
}
}
Question = What can be done so that the animation is shown regardless if its called from the listener?
using timer would be the solution for you:) Example of a similar problem that guy had a similar problem, you can read the answers he received. good luck
When I run this class(Try class), it calls the Stacker class completely(with full functions), but when I use other class(I used JFrame with button that has actionlistener that calls Stacker class) to run this(Stacker class), the JFrame(Stacker class) will pop-up but empty and I can't close the program.
I tried to run this(Stacker) from other class like this:
public class Try {
/**
* #param args
*/
public static void main(String[] args) {
run();
}
public static void run(){
new Stacker();
}
}
The Stacker class ran fully(I can interact with it). But when I tried to call the stacker class from an actionlistener of a button in JFrame, it's blank and can't be closed.
Please help me.
here are my codes for the Stacker class:
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Stacker extends JFrame implements KeyListener {
int iteration = 1;
static double time = 200;
static int last = 0;
static int m = 10;
static int n = 20;
JButton b[][];
static int length[] = {5,5};
static int layer = 19;
static int deltax[] = {0,0};
static boolean press = false;
static boolean forward = true;
static boolean start = true;
JPanel panel;
public static void main (String[] args) {
Stacker stack = new Stacker();
stack.setVisible(true);
}
public Stacker() {
panel = new JPanel();
panel.setLayout(new GridLayout(20,10));
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
b = new JButton [m][n];
for (int y = 0;y<n;y++) {
for (int x = 0;x<m;x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.white);
b[x][y].setBorderPainted(false);
panel.add(b[x][y]);
b[x][y].setEnabled(true);
}//end inner for
}
setSize(390, 560);
setLayout(new GridBagLayout());
add(panel);
setFocusable(true);
addKeyListener(this);
pack();
setVisible(true);
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
do {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10-length[1]){
forward = false;
} else if (deltax[1] == 0){
forward = true;
}
draw();
try {
Thread.sleep((long) time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}while(press == false);
if (layer>12) {
time= 150-(iteration*iteration*2-iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
System.exit(0);
}
last = deltax[1];
start = false;
go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last<deltax[1]) {
if (deltax[1]+length[1]-1 <= last+length[0]-1) {
return length[1];
} else {
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
} else if (last>deltax[1]) {
return length[1]-Math.abs(deltax[1]-last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0;x<length[1];x++) {
b[x+deltax[0]][layer].setBackground(Color.white);
}
for (int x = 0;x<length[1];x++) {
b[x+deltax[1]][layer].setBackground(Color.BLUE);
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
press = true;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
Your do-while loop and thread sleep look suspicious in that it is running on the Swing event thread tying it up. The problem is that these longer running tasks lock the Swing Event Dispatch Thread, or EDT, the one thread that is responsible for all Swing graphics and user interactions, preventing your application from drawing itself and all of its widgets and prevents the application from responding to any user input.
Likely the application ran in isolation because the issues that I identified above ran off of the EDT, and doing this code wise is one possible solution to your problem, but I'm betting that there are better solutions available that we can help you with if you tell us more about the details of your problem.
Also you appear to be using recursion in a dangerous way having the go method call itself. Can't really say more since I'm on a cell phone.
Firstly, I would replace:this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
with the following:
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
as DISPOSE_ON_CLOSE is a static/class variable.
In fact, I am surprised that your IDE did not pick that up (unless, you aren't using an IDE).
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Stacker stack = new Stacker();
stack.setVisible(true);
return null;
}
};
worker.execute();
after using this method, when I call Stacker class from another JFrame, the game executes perfectly. Just like when calling Stacker class from Try class.
Thank you for all the responses I had. :D