Multiple instances of the same animated GIF in a Canvas (Java) - java

So I'm making a game where you can put bombs on the location of your character. Each bomb is associated with a GIF image when the bomb is displayed and eventually go BOOM (think about Bomberman).
The problem was, when i tried to paint more than one bomb on the screen, it was painted from the last frame of the GIF. Investigating, I found the method image.flush() to reset the GIF cicle but now the problem is that every time I paint a second bomb on the screen, the GIF cycle is reset for all previously bombs on screen.
Here is my constructor for each bomb:
public Tnt(int x, int y){
this.x = x;
this.y = y;
ImageIcon ii = new ImageIcon("src/main/resources/modelObjects/tnt.gif");
image = ii.getImage();
image.flush();
}
Every bomb i create enters an ArrayList (listTnt) and is removed after 6 secs, so i only paint the bombs already active.
Here is my method for drawing:
public void draw(Graphics2D g2d, JPanel board){
for(Tnt tnt: listTnt){
g2d.drawImage(tnt.getImage(), tnt.getX(), tnt.getY(), board);
}
}
EDIT: Seems that the problem was ImageIcon, since it reuses the image using Toolkit.getImage. Instead, Toolkit.createImage create a not reusable image.
Here is my new constructor for Tnt that worked perfectly:
public Tnt(int x, int y){
this.x = x;
this.y = y;
Toolkit t = Toolkit.getDefaultToolkit ();
image = t.createImage("src/main/resources/modelObjects/tnt.gif");
}
I dont even need image.flush() now. Thank you all.

The underlying Image is being reused amongst each ImageIcon.
Judging by the OpenJDK source code, it appears to be due to the fact that each simply requests the Image via Toolkit.getImage.
This method has a nifty caveat, however, which explains the issue at hand:
The underlying toolkit attempts to resolve multiple requests with the same filename to the same returned Image.
Instead, you should skip the ImageIcon step completely (since it's inappropriate to be using a Swing class unnecessarily in the first place), and instead call Toolkit.createImage, which states in the documentation:
The returned Image is a new object which will not be shared with any other caller of this method or its getImage variant.
Good luck.

As I did not know how to solve this, I tried #super_ solution and it works quite nicely. I share the code for anyone who wants an example. +1 to him
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestAnimatedGif {
private static final int IMAGE_COUNT = 9;
protected void initUI() {
JFrame frame = new JFrame(TestAnimatedGif.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
frame.add(panel);
frame.setSize(600, 400);
frame.setVisible(true);
final Timer t = new Timer(1000, null);
t.addActionListener(new ActionListener() {
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (count < IMAGE_COUNT) {
try {
JLabel image = new JLabel(new ImageIcon(Toolkit.getDefaultToolkit().createImage(
new URL("http://www.sitevip.net/gifs/bomba/BOMB-B_animado.gif"))));
panel.add(image);
count++;
panel.revalidate();
panel.repaint();
System.err.println("image added");
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} else {
t.stop();
}
}
});
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestAnimatedGif().initUI();
}
});
}
}

Related

How to make ImageIcon on JLabel update faster with less latency

I am trying to update an ImageIcon on a JLabel which sits on a JLayeredPane, but there is a lot of latency between when the setting thread sends the proper state to the JLabel object and when the GUI displays the ImageIcon of the proper state. The following code is an example of the issue, look for the difference in time between the print of the button being on/off and when the displayed icon gets lighter/darker.
The setting thread:
new Thread(new Runnable() { // setting thread
#Override
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
System.out.println("testButton on"); // print that the button is on
testButton.updateState(1); // set button state to on
Thread.sleep(70 + random.nextInt(500)); //sleep between 70 and 570 milliseconds
System.out.println("testButton off");// print that the button is off
testButton.updateState(0); // set button state to off
Thread.sleep(70 + random.nextInt(500)); // sleep between 70 and 570 milliseconds
}
} catch(Exception e) {
e.printStackTrace();
}
}
}).start();
The button object:
class Button extends JLabel {
ImageIcon released;
ImageIcon pressed;
String text;
public Button(int x, int y, String text) {
released = new ImageIcon("src/components/images/button.png");
pressed = new ImageIcon("src/components/images/buttonDown.png");
setBounds(x,y, 100, 100);
this.text = text;
setIcon(released);
}
public void updateState(int data) {
if (data == 1) {
setIcon(pressed);
}
else {
setIcon(released);
}
}
}
The ImageIcons are only 325 bytes, so what might be causing the latency? I looked up about the Event Dispatcher Thread and many people say it should be instantaneous for an image to get painted.
End goal: Have many button objects on screen with the setting thread calling them to update based on randomly occurring actions. The displayed icon for a specific button object should change immediately as it is set in the function. The setting thread will not be constantly looping, instead loop once for every action sent (it is twice here just to show the issue).
Any suggestions or things to try I will test as soon as I can.
Edit: In the end the thread that gets the information will call to a device driver in Linux where it will wait for a response and only when it gets a response will it need to update the window. From what I know timer is used to update something at regular intervals, but I am likely wrong.
As explained in the comments running long processes on the The Event Dispatch Thread blocks it, so it does not respond to changes.
Also you are not suppose to update Swing components from other (not EDT) threads.
You need to use Swing tools like SwingWorker or Timer.
The following mcve demonstrates a simple slide-show using Timer:
import java.awt.BorderLayout;
import java.io.IOException;
import java.net.URL;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
public class ChangeButtonIcon extends JPanel{
private final URL[] urls = {
new URL("https://findicons.com/files/icons/345/summer/128/cake.png"),
new URL("http://icons.iconarchive.com/icons/atyourservice/service-categories/128/Sweets-icon.png"),
new URL("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS_FkBgG3_ux0kCbfG8mcRHvdk1dYbZYsm2SFMS01YvA6B_zfH_kg"),
};
private int iconNumber = 0;
private final JButton button;
private boolean stop = true;
private final Random random;
private static final int MIN_DELAY = 70, DELAY = 500;
private Timer timer;
public ChangeButtonIcon() throws IOException {
random = new Random();
button = new JButton();
button.setIcon(new ImageIcon(urls[iconNumber]));
button.setHorizontalTextPosition(SwingConstants.CENTER);
button.addActionListener(e -> startStopSlideShow());
add(button);
}
private void startStopSlideShow(){
stop = ! stop;
if(stop){
timer.stop();
return;
}
timer = new Timer( MIN_DELAY+ random.nextInt(DELAY), (e)->swapIcon());
timer.start();
}
private void swapIcon() {
iconNumber = iconNumber >= urls.length -1 ? 0 : iconNumber+1;
button.setIcon(new ImageIcon(urls[iconNumber]));
}
public static void main(String[] args) throws IOException{
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(new ChangeButtonIcon());
window.add(new JLabel("Click image to start / stop"), BorderLayout.PAGE_END);
window.pack();
window.setVisible(true);
}
}

Image Slideshow using JFrame

I have a problem with a task that seemed to be pretty easy. I have to create a program that will show images (.jpg,.png and .gif) consecutively. Images have to be the contents of some files, that is given as an argument to the program. When I have to load the images separately, it works, but the issue occurs when I load them one after another with a sleep between them.
Here is my code:
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new MyFrame(args[0],Integer.parseInt(args[1]));
//First argument is path to file with images, second - amount of time (in seconds) which every image has to stay on the screen until the next one appears
}
});
}
}
import java.io.File;
import javax.swing.*;
public class MyFrame extends JFrame{
public MyFrame(String path, int time){
super("Obrazki");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
MyPanel panel = new MyPanel();
panel.setVisible(true);
this.add(panel);
pack();
File file = new File(path);
String[] tabs = file.list();
for(int i=0; i<tabs.length; i++)
{
panel.loadImage(path+"\\"+tabs[i]);
this.repaint();
try {
Thread.sleep(time*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import javax.swing.JPanel;
public class MyPanel extends JPanel
{
Image img;
public void loadImage(String s)
{
img = Toolkit.getDefaultToolkit().getImage(s);
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(img, 1);
while(!tracker.checkID(1)) {
try {
tracker.waitForID(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.repaint();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(this.img, 0, 0, this.getSize().width, this.getSize().height, this);
}
}
but the issue is when i load them one after another with sleep time between them.
You are causing the Event Dispatch Thread (EDT) to sleep, which means the GUI can't respond to events or repaint itself. Read the section from the Swing tutorial on Concurrency for more information.
Don't use Thread.sleep() when code is executing on the EDT.
For animation you can:
use a SwingWorker (with Thread.sleep())and publish the Icon you want to paint, or
use a Swing Timer. The tutorial also has a section on How to Use Swing Timers.

Why is this JLabel's ImageIcon not updating?

SSCCE, as small as I could get it with keeping all the logic in the same order:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Test {
public static void main(String[] args) {
new Test();
}
BufferedImage img = null; // <-- needs this scope
JFrame mainWindow = new JFrame();
JLabel mainImage = new JLabel();
public Test() {
mainWindow.add(mainImage);
mainWindow.setVisible(true);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// step 5
mainImage.addMouseListener(new MouseListener() {
#Override public void mouseClicked(MouseEvent e) {
dostuff();
}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
});
dostuff();
}
private void dostuff() {
// step 1
try {
JFileChooser fc = new JFileChooser();
fc.showOpenDialog(null);
File file = fc.getSelectedFile();
img = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
//step 2
mainImage.setIcon(new ImageIcon(img));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
Graphics2D g = img.createGraphics();
g.setColor(new Color(0xFFFF0000));
g.drawOval(10, 10, 10, 10);
try{Thread.sleep(2000);}catch(Exception e){}
// step 3
BufferedImage img2 = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
for (int i = 10 ; i < 20 ; i++) {
for (int j = 10 ; j < 20 ; j++) {
img2.setRGB(i,j,0xFF0000FF);
}
}
// step 4
mainImage.setIcon(new ImageIcon(img2));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
}
}
It should be obvious what I'm trying to do, and compiling will show that it's not doing that. But I want this post to have a question mark so here is the problem description and question:
What I want to happen:
The program loads, and the user is prompted to select a file (an image).
Upon a selecting an image, that image is displayed in a JFrame, and some Graphics2D drawings happen on it. (I included the sleep() because these drawings take a while in the actual program)
When the drawings are finished, a new image is created and it is also drawn on.
The new image replaces the old image in the JFrame
When the user clicks on the image, they are prompted to select a new image, and we repeat from step 1.
What is happening:
Steps one through four work fine. On step five, after selecting an image, the JFrame is populated by a black rectangle the size of the user-selected image, with the image from step three overlayed in the top left corner, step two does not happen, and steps three through five appear to work just fine.
What's more, in my actual program, I can tell that the new user-selected-image is doing its job just fine by the output that step three produces.
So the question is, how can I fix later repetitions of step two such that the appropriate image is shown in the JFrame?
EDIT: This imgur album shows step by step the results I am getting.
http://imgur.com/a/xW051
EDIT2: updated without Thread.sleep()
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Test {
public static void main(String[] args) {
new Test();
}
BufferedImage img = null; // <-- needs this scope
JFrame mainWindow = new JFrame();
JLabel mainImage = new JLabel();
public Test() {
mainWindow.add(mainImage);
mainWindow.setVisible(true);
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// step 5
mainImage.addMouseListener(new MouseListener() {
#Override public void mouseClicked(MouseEvent e) {
dostuff();
}
#Override public void mouseEntered(MouseEvent e) {}
#Override public void mouseExited(MouseEvent e) {}
#Override public void mousePressed(MouseEvent e) {}
#Override public void mouseReleased(MouseEvent e) {}
});
dostuff();
}
private void dostuff() {
// step 1
try {
JFileChooser fc = new JFileChooser();
fc.showOpenDialog(null);
File file = fc.getSelectedFile();
img = ImageIO.read(file);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
//step 2
mainImage.setIcon(new ImageIcon(img));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
Graphics2D g = img.createGraphics();
g.setColor(new Color(0xFFFF0000));
for (int h = 0 ; h < 0xFF ; h++) {
for (int i = 0 ; i < img.getWidth() ; i++) {
for (int j = 0 ; j < img.getHeight()/2 ; j++) {
img.setRGB(i,j,0x88FF0000 + h);
}
}
mainImage.repaint();
}
// step 3
BufferedImage img2 = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
for (int i = 10 ; i < 20 ; i++) {
for (int j = 10 ; j < 20 ; j++) {
img2.setRGB(i,j,0xFF0000FF);
}
}
// step 4
mainImage.setIcon(new ImageIcon(img2));
mainWindow.pack();
mainWindow.setLocationRelativeTo(null);
}
}
Edit:
If I then load a 1000x1000 image, I see a 1000x1000 black square...
I would guess the black images are because you are causing the Event Dispatch Thread (EDT) to sleep. so although the frame is resizing with the pack() statement the newly loaded image is not being painted.
Don't use Thread.sleep() in code that is executing on the EDT. All code executed from a listener executes on the EDT. Instead you should be using a Swing Timer to schedule the event to update the image of the label.
Edit 2:
Read the section from the Swing tutorial on Concurrency. In general all event code executes on the EDT. When the EDT is blocked, Swing components can't be repainted.
mainImage.setIcon(new ImageIcon(img));
The above statement will cause repaint() to be invoked on the "mainImage" label. The repaint() request is passed to the RepaintManager and a painting request is added to the end of the EDT.
So the label will be painted AFTER all the code in the "doStuff()" method has finished executing.
However you also invoke
mainImage.setIcon(new ImageIcon(img2));
at the end of the method. So by the time the image actually gets painted its Icon has been changed a second time so only the second Icon is painted.
In between those two statements, after reading the image the frame is resized when the pack() method is invoked. I believe this is because the packing of the frame results in OS level painting since a frame is an OS widget not a Swing component. In other words the frame can be resized even if the components on the frame are not repainted.
If you want a responsive GUI then you can't execute long running code on the EDT. In the case of the second SSCCE the "for loop" you added takes a long time
to run which is effectively blocking the EDT and preventing the newly read Icon from being painted.
Therefore long running tasks should be executed on a separate Thread. The concurrency tutorial will explain how you can use a SwingWorker for long running tasks.

Why does my AWT/swing GUI JAR capture stylus event data only in a specific region of the screen on a Windows 8.1 tablet?

QUESTION SUMMARY: I generated a .JAR file using Netbeans 7.2.1 for a Java Swing/AWT program developed on Windows 7 (Java version 1.8.0_40), that helps collect user handwriting from the screen. It runs fine on a Windows 7 notebook PC but due to some reason captures handwriting data only on a specific region of the screen on a Windows 8.1 tablet (Java version 1.8.0_45). Could someone kindly tell me why this is happening?
DETAILS: I have a requirement of collecting online handwriting samples (i.e. those acquired from electronic devices like a tablet PC using a pen/stylus and writing surface) for some analysis
Being new to developing programs of this nature, I read up about it on the web and decided to use the Java Swing/AWT toolkits
A person's handwriting is composed of strokes which are in turn composed of points. My objective was to capture:
- the X- and Y-coordinates of a point on the screen
- the timestamp of creation of this point
- the stroke's start-time, end-time and color (color not too important)
To this end I wrote the following program using Netbeans 7.2.1 IDE with Java 1.8.0_40 on a Windows 7 Home Basic OS
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package handwritingsamplerawt;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class HandwritingSamplerAWT {
static JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
CreateAndShowGUI();
}
});
}
private static void CreateAndShowGUI() {
frame = new JFrame("Writing Surface v0.1");
frame.getContentPane().setLayout(new FlowLayout(FlowLayout.RIGHT));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.LIGHT_GRAY);
frame.pack();
frame.add(new MyPanel());
frame.setVisible(true);
}
}
class MyPanel extends JPanel{
private int x,y;
static int strokeIndex;
private long reducedMillis;
private ArrayList<StrokeInfo> strokes;
private JButton btnSave;
public MyPanel() {
MyPanel.strokeIndex=0;
this.reducedMillis = 1435800000000L;
this.strokes = new ArrayList<>();
this.btnSave = new JButton("SAVE SAMPLE");
this.btnSave.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
WriteCoordinates();
}
});
this.add(this.btnSave);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
x=e.getX();
y=e.getY();
SaveCoordinates(x,y,"PRESSED");
repaint();
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
x=e.getX();
y=e.getY();
SaveCoordinates(x,y,"DRAGGED");
repaint();
}
});
addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
x=e.getX();
y=e.getY();
SaveCoordinates(x,y,"RELEASED");
repaint();
}
});
}
void SaveCoordinates(int xCoordinate, int yCoordinate, String actionIndicator){
try {
Calendar cal = Calendar.getInstance();
Date currDate = cal.getTime();
double timeStamp=(double)(currDate.getTime()-reducedMillis);
PointInfo pointObj = new PointInfo(xCoordinate, yCoordinate, timeStamp);
switch (actionIndicator) {
case "PRESSED":
StrokeInfo newStroke = new StrokeInfo();
newStroke.points.add(pointObj);
strokes.add(newStroke);
break;
case "DRAGGED":
strokes.get(strokeIndex).points.add(pointObj);
break;
case "RELEASED":
strokeIndex+=1;
break;
}
} catch (Exception ex){
String errMsg = ex.getMessage();
System.out.println(errMsg);
}
}
void WriteCoordinates() {
try {
Calendar cal = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String currTimeString = dateFormat.format(cal.getTime());
DecimalFormat decFormat = new DecimalFormat("#");
decFormat.setMaximumFractionDigits(2);
FileWriter writer = new FileWriter("D:\\HandwritingCaptures\\HandwritingText\\"+currTimeString+".txt");
SetStrokeAttributes(strokes);
ListIterator<PointInfo> pointItr;
if (strokes.isEmpty()==false) {
for (int index = 0; index < strokeIndex; index++) {
writer.write(strokes.get(index).colour);
writer.append('\t');
writer.write(decFormat.format( strokes.get(index).startTime));
writer.append('\t');
writer.write(decFormat.format( strokes.get(index).endTime));
writer.append('\n');
pointItr = strokes.get(index).points.listIterator();
while (pointItr.hasNext()) {
PointInfo currPoint = pointItr.next();
writer.write(String.valueOf(currPoint.x));
writer.append('\t');
writer.write(String.valueOf(currPoint.y));
writer.append('\t');
writer.write(decFormat.format(currPoint.timestamp));
writer.append('\n');
}
writer.append('#');
writer.append('\n');
}
}
writer.close();
SaveScreenshot("D:\\HandwritingCaptures\\Screenshots\\"+currTimeString+".png");
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
void SetStrokeAttributes(ArrayList<StrokeInfo> strokeList) {
double startTime, endTime;
String colour;
StrokeInfo tmpStroke;
ArrayList<PointInfo> points;
if (strokeList.isEmpty() == false) {
for (int index = 0; index < strokeList.size(); index++) {
tmpStroke = strokeList.get(index);
points = tmpStroke.points;
tmpStroke.colour = "black";
tmpStroke.startTime=points.get(0).timestamp;
tmpStroke.endTime=points.get(points.size()-1).timestamp;
strokeList.set(index, tmpStroke);
}
}
}
void SaveScreenshot(String imgFilePath){
try {
Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage capture = new Robot().createScreenCapture(screenRect);
ImageIO.write(capture, "png", new File(imgFilePath));
} catch (IOException | AWTException ex) {
System.out.println(ex.getMessage());
}
}
public Dimension getPreferredSize() {
return new Dimension(1366,768);
}
protected void paintComponent(Graphics g) {
super.paintComponents(g);
g.setColor(Color.BLACK);
g.drawLine(x, y, x, y);
}
}
class PointInfo {
int x,y;
double timestamp;
public PointInfo(int px, int py, double ts) {
this.x=px;
this.y=py;
this.timestamp=ts;
}
}
class StrokeInfo {
ArrayList<PointInfo> points;
double startTime, endTime;
String colour;
public StrokeInfo() {
points= new ArrayList<>();
}
}
I generated the .jar file using the IDE itself (Project Properties-> Build-> Packaging-> Compress JAR file)
Then copied the .jar file to a HP EliteBook 2730P Notebook PC with JRE 1.7.0.800 and Windows 7 Pro OS (32-bit) where it ran fine collecting handwriting strokes from all areas of the screen
But when I copied the same .jar to a HP Elite x2 1011 G1 tablet with JRE 1.8.0_45 and Windows 8.1 (64-bit) and ran it, I found that strangely it captures stylus inputs ONLY from a specific region of the screen - more specifically towards the upper right. It is completely non-responsive on the other areas
Could someone please help me understand why this is happening? Would have posted couple screenshots here but my low reputation prevents me from doing so.
ADDITIONAL THOUGHTS: Would it be better to use .NET or Java FX for developing such a tool for using in a Windows 8.1 environment?
Your panel seems to have a fixed size:
return new Dimension(1366,768);
Is the resolution of your tablet bigger than that?
Edit:
This should help:
private static void CreateAndShowGUI() {
frame = new JFrame("Writing Surface v0.1");
// Using the default BorderLayout here.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.LIGHT_GRAY);
frame.getContentPane().add(new MyPanel(), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
However there are still problems with your layout, like the "Save" button jumping around. You should take a look at this tutorial:
https://docs.oracle.com/javase/tutorial/uiswing/layout/index.html
The fact that the problem could be related to the hardcoded dimensions became clear on seeing the behavior of the .jar file upon running
The tablet had a higher resolution (1920 x 1080 pixels - found by checking the screen resolution through a right click on the desktop) than the machine I had initially run the program on (1366 x 768 pixels). The dimensions of the writable area towards the upper right corner were infact true to the hardcoded dimensions
Ergo, I modified the overridden method getPreferredSize() in the following manner:
public Dimension getPreferredSize() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
return new Dimension((int)screenSize.getWidth(),(int)screenSize.getHeight());
}
This causes the preferred size components to take on the effective height and width of the screen of the device the .jar is executed on. This is important because depending on the layout manager used the treatment of the above method by it does differ
The "SAVE SAMPLE" button intermittently still seems to cast its images (non-clickable) on other areas of the screen - this probably might have something to do with the layout manager handling of the UI components. Shall add an edit for that issue too once I find a solution

Stopping a window from displaying till it is fully drawn?

I am working on a Java program that takes in a large amount of files (3000 max) with an associated array of 1/0's. Currently I have a visualization of the array where there is a grid where each box is filled black for 1 or white for 0. When drawn it runs well but takes around a minute to fully load (and potentially locks the computer up in the meantime.) Is there a way I can: 1, not display the window till it is done
(i.e JFrame create,
//draw window
frame.setVisible(true))
and 2, track the progress of the process so that I can use a progress bar with it?
edit: Can I run a thread to draw it and then simply make a while loop to only display it once the thread is completed?
In the example below, a SwingWorker sets pixels in a BufferedImage based on the data read from a random file. Note that Thread.sleep() is used to simulate latency; it is otherwise not required. You can add a JProgressBar as shown here.
Is there a better way to get simple colored boxes?
Yes. In the example below, each pixel represents one cell. For larger boxes, return a multiple of the image size, e.g.
#Override
public Dimension getPreferredSize() {
return new Dimension(2 * N, 2 * N);
}
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
/**
* #see https://stackoverflow.com/a/25043676/230513
*/
public class WorkerTest {
private static final int N = 256;
private final BooleanPanel panel = new BooleanPanel();
private class BooleanPanel extends JPanel {
private BufferedImage image;
public void setImage(BufferedImage bi) {
this.image = bi;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(N, N);
}
}
private class BufferedImageWorker extends SwingWorker<BufferedImage, BufferedImage> {
#Override
protected BufferedImage doInBackground() throws Exception {
BufferedImage image = new BufferedImage(N, N, BufferedImage.TYPE_INT_ARGB);
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(new FileInputStream("/dev/random")))) {
for (int row = 0; row < N; row++) {
for (int col = 0; col < N; col++) {
image.setRGB(col, row, dis.readByte() < 0 ? 0xffffffff : 0xff000000);
}
Thread.sleep(40); // ~25 Hz
publish(image);
}
return image;
}
}
#Override
protected void process(List<BufferedImage> list) {
for (BufferedImage bi : list) {
panel.setImage(bi);
panel.repaint();
}
}
}
private void display() {
JFrame f = new JFrame("WorkerTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
new BufferedImageWorker().execute();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new WorkerTest().display();
});
}
}
I would definitely use a SwingWorker in this case. Basically, maybe something along these lines (I'm not sure what type of object your 'visualization' is, so for simplicity, I'll just say it's an Image). You can add this at the bottom of your class. You'll obviously have to edit it to make it work for you.
protected class DrawGridTask extends SwingWorker<Image, Object> {
ObjectToPutImageOn imageObject;
public DrawGridTask(ObjectToPutImageOn obj) {
this.imageObject = obj;
}
protected Image doInBackground() {
// generate your Image or graphic or whatever here
return Image;
}
protected void done() {
imageObject.drawThisCompletedImage(get());
}
}
To call this method, you would run (new DrawGridTask(objectToPutImageOn)).execute();
All the code in doInBackground() will run on it's own worker thread. Done() runs on the event dispatch thread, and gets the reference doInBackground() returns when it calls get().
There is more information here, including how to do progress updates at: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Since I mentioned Images, if you do work with them, you might also want to take a look at the MediaTracker class, this can be very useful for blocking until an image is ready.

Categories

Resources