having problems with keyListener - java

i am trying to make a simple reaction test in java.
when the screen turns green i press space witch is supposed to change te boolean "clicked into false and stop the loop that measures time.
in reality the key listner does nothing.
am i adding the keay listener to the right compnent( jpanel panel)?
is there any other problems?
import java.awt.Color;
import java.awt.RenderingHints.Key;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
public class mainCheck {
// //////////////////////////////////////
public static void timeKeeper() {
boolean clicked=false;
long time = 10000;
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
panel.setBackground(Color.GREEN);
while (time > 0 && !clicked) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
time--;
}
panel.setBackground(Color.gray);
long time2= 10000-time;
JLabel x = new JLabel("" +time2+"");
panel.add(x);
}
// //////////////////////////////////////
static boolean clicked;
JFrame frame;
static JPanel panel;
public mainCheck() {
frame = new JFrame();
panel = new JPanel();
clicked = false;
Handler handler = new Handler();
frame.addKeyListener(handler);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setVisible(true);
}
// //////////////////////////////////////
public static void main(String[] args) {
mainCheck f = new mainCheck();
panel.getActionMap();
f.timeKeeper();
}
// //////////////////////////////////////
public class Handler implements KeyListener {
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
clicked = false;
System.out.println("space pressed");
}
}
}
}

do not use Thread.sleep(5000); block Event Dispatch Thread and during sleep you can lost all events to the already visible Swing GUI
use Swing Timer instead
Thread.sleep(1); could be proper delay for space enviroment, non_human, very short period attacking latency in Native OS (8-14miliseconds, depends of Native OS)
JLabel x = new JLabel("" +time2+""); and panel.add(x); in AWT/Swing isn't any notifiers that some, any, part of JComponents are removed or added, have to notify used LayoutManager (JPanel has FlowLayout in API) by using methods revalidate and repaint, e.g.
.
JLabel x = new JLabel("" +time2+"");
panel.add(x);
panel.revalidate();
panel.repaint();
Swing GUI should be created on Initial Thread
don't to use KeyListener use KeyBindings instead, otherwise you (are focus hunter) would need to set panel.seFocusable(true);

The problem is that you "e.getKeyCode()" always is "0" so
change for "e.getKeyChar()" and "(char)32"
the other problem is in that you put clicked = false and must be
"true"
And the last problem you have is in "timeKeeper()" you have to erase "boolean" because it is already declarated
Bad
public static void timeKeeper() {
boolean clicked=false
....}
Good
public static void timeKeeper() {
clicked=false
....}
This is the correct code:
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
public class mainCheck {
// //////////////////////////////////////
public static void timeKeeper() {
clicked=false;
long time = 10000;
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
panel.setBackground(Color.GREEN);
while (time > 0 && !clicked) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
time--;
}
panel.setBackground(Color.gray);
long time2= 10000-time;
JLabel x = new JLabel("" +time2+"");
panel.add(x);
}
// //////////////////////////////////////
static boolean clicked;
JFrame frame;
static JPanel panel;
Handler handler = new Handler();
public mainCheck() {
frame = new JFrame();
panel = new JPanel();
clicked = false;
frame.addKeyListener(handler);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setVisible(true);
}
// //////////////////////////////////////
public static void main(String[] args) {
mainCheck f = new mainCheck();
panel.getActionMap();
f.timeKeeper();
}
// //////////////////////////////////////
public class Handler implements KeyListener {
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == (char)32) {
clicked = true;
System.out.println("space pressed");
}
}
}
}

It's a really bad idea to use Thread.sleep() to decrement the time value.
Use a Timer object instead:
public void myTimer(){
Timer myTimer = new Timer(delay, new ActionListener(){
public void actionPerformed(ActionEvent e){
//Stuff to do
}
});
}
Where delay is the amount of time you delay the timer. You start the timer with myTimer.start();

Related

question about wait-notify in java thread

Hi I'm studying the basics of java threads,
but I'd like to get some help because I don't understand the examples in the book.
https://imgur.com/a/pcdOq2n
In this example, when the user presses any key,
the yellow bar is filled with magenta and the bar's magenta is
reduced by a thread.
But the part I don't understand is if you press the key for a long time while bar is full,
the magenta of the bar does not reduce immediately when the key is released
when I checked. the fill() function was running for a certain time even when the key was released.
I want to know, why if the bar is full and the key is pressed for a long time,
the fill() function works for a certain time even when the key is released
and the bar does not reduce immediately.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class MyLabel extends JLabel {
private int barSize = 0;
private int maxBarSize;
public MyLabel(int maxBarSize) {
this.maxBarSize = maxBarSize;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
int width = (int) ( ( (double)( getWidth() ) )/maxBarSize *barSize );
if (width == 0) return;
g.fillRect(0, 0, width, this.getHeight());
}
synchronized void fill() {
if (barSize == maxBarSize) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
barSize++;
repaint();
notify();
}
synchronized void consume() {
if (barSize == 0) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
System.out.println("consume");
barSize--;
repaint();
notify();
}
}
class ComsumerThread extends Thread {
private MyLabel bar;
public ComsumerThread(MyLabel bar) {
this.bar = bar;
}
#Override
public void run() {
// TODO Auto-generated method stub
while(true) {
try {
sleep(100);
bar.consume();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
}
}
}
}
public class TabAndThreadEx extends JFrame {
private MyLabel bar = new MyLabel(100);
public TabAndThreadEx(String title) {
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(null);
bar.setBackground(Color.orange);
bar.setOpaque(true);
bar.setLocation(20, 50);
bar.setSize(300, 20);
c.add(bar);
c.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
bar.fill();
}
});
setSize(350, 200);
setVisible(true);
c.setFocusable(true);
c.requestFocus();
ComsumerThread th = new ComsumerThread(bar);
th.start();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new TabAndThreadEx("Quickly press any key to fill the bar");
}
}

Interrupt multiple threads on the same time

I have 3 threads on a frame and when I press the "Exit" button I want o stop the threads which are currently running and after that to close the frame with the program. For that I created an array where I have all the Threads of the frame and when the button "Exit" is pressed the program iterates over the array and if is there any thread running I interrupt it. The problem with my approch is that the program stops only 1 thread, not all of them. So, even though the frame is closed, there will be 2 threads running on the background.
Here is my program:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.Thread.State;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ProgramInterface extends JFrame {
private JButton stopThreadsAndExit;
private CounterThread[] counterThreadsArray = new CounterThread[3];
public static void main(String[] args) {
new ProgramInterface();
}
public ProgramInterface() {
// TODO Auto-generated constructor stub
addCounterThreadsToArray();
addThreadsOnTheFrame();
stopThreadsAndExit = new JButton("Exit");
addActionToExitButton();
add(stopThreadsAndExit);
setFrameSettings();
}
private void setFrameSettings() {
setSize(400, 400);
setLayout(new FlowLayout());
setVisible(true);
}
public void addThreadsOnTheFrame() {
for (CounterThread counter : counterThreadsArray) {
add(counter);
}
}
public void addActionToExitButton() {
stopThreadsAndExit = new JButton("Exit");
stopThreadsAndExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
exit();
}
}).start();
}
});
}
public void exit() {
for (CounterThread counter : counterThreadsArray) {
if (counter.isRunnable()) {
counter.interruptThread();
}
}
dispose();
}
private void addCounterThreadsToArray() {
counterThreadsArray[0] = new CounterThread("Thread 01");
counterThreadsArray[1] = new CounterThread("Thread 02");
counterThreadsArray[2] = new CounterThread("Thread 03");
}
}
class CounterThread extends JPanel {
Thread counter;
private String threadName;
private JButton startThread;
public CounterThread(String threadString) {
this.threadName = threadString;
initializesCounterThread();
addActionToStartButton();
setLayout(new FlowLayout());
add(startThread);
}
public void addActionToStartButton() {
startThread = new JButton("Start " + threadName);
startThread.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
counter.start();
}
});
}
private void initializesCounterThread() {
counter = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
for (int i = 0; i < 1000000000; i++) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
System.out.println(threadName + " generated " + i);
}
} catch (InterruptedException e) {
// TODO: handle exception
Thread.currentThread().interrupt();
}
}
});
}
public void interruptThread() {
counter.interrupt();
}
public boolean isRunnable() {
return counter.getState() == State.RUNNABLE;
}
}
Don't check getState on the thread, you shouldn't have to care if it's currently running or not. Just call interrupt on it.
When you call interrupt on a thread that just sets a flag on that instance. That flag gets checked when the thread does certain things, like wait or sleep, or when the thread explicitly calls isInterrupted (which you are doing). So you don't need to care what state the thread is in.

Two setting backgrounds and sleeping a thread

I want to change the color of JTextField to red after typing something in it, and then after a second return to a default white background. I tried this outside the listener, and it worked, but when it comes to being a part of a listener, it doesn't (it just skips setting the red color). This is weird for me..
public class Test {
JFrame frame;
JTextField field;
public Test() {
frame = new JFrame();
field = new JTextField("A");
field.addKeyListener(new KeyBListener());
frame.getContentPane().add(field);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) { new Test(); }
private class KeyBListener implements KeyListener {
#Override
public void keyTyped(KeyEvent e) {
try {
field.setBackground(Color.RED);
Thread.sleep(1000);
field.setBackground(Color.WHITE);
} catch (InterruptedException es) { es.printStackTrace(); }
}
#Override
public void keyPressed(KeyEvent e) { }
#Override
public void keyReleased(KeyEvent e) { }
}
}
Try creating a separate Thread that listens to color change on the JTextField then changes it back. In this case at least you will not block the main Thread, although I'm not sure it's the most efficient way.
public Main() {
frame = new JFrame();
frame.setSize(800, 600);
field = new JTextField("A");
field.addKeyListener(new KeyBListener());
frame.getContentPane().add(field);
frame.pack();
frame.setVisible(true);
new Thread(() -> {
while(true) {
if(field.getBackground().equals(Color.RED))
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
field.setBackground(Color.WHITE);
}
}).start();
}
Your previous solution was working because it was executed from the AWT itself.
The keyTyped() method is executed on the Event dispatch thead (EDT), so you have to move the painting actions back to the AWT.
Have a look on SwingUtilities.invokeLater() (non-blocking) or SwingUtilities.invokeAndWait() (blocking), see
Oracle Doc
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
field.setBackground(Color.RED);
Thread.sleep(1000);
field.setBackground(Color.WHITE);
} catch (InterruptedException es) {
es.printStackTrace();
}
}
});
You can spawn a different thread in which you do the color manipulation. That ensures that the color manipulation is not happening inside EDT.
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
public class Test {
JFrame frame;
JTextField field;
AtomicBoolean isColorChangeOn = new AtomicBoolean();
public Test() {
frame = new JFrame();
field = new JTextField("A");
field.addKeyListener(new KeyBListener());
frame.getContentPane().add(field);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
isColorChangeOn.set(false);
}
public static void main(String[] args) {
new Test();
}
private class KeyBListener implements KeyListener {
#Override
public void keyTyped(KeyEvent e) {
if(!isColorChangeOn.get()) {
isColorChangeOn.set(true);
Runnable setcolor = ()->{
try {
System.out.println("color changing");
field.setBackground(Color.RED);
Thread.sleep(1000);
field.setBackground(Color.WHITE);
isColorChangeOn.set(false);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
};
new Thread(setcolor).start();
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
}
}

How to know the release listener is triggered after click operation or drag operation?

I asked a fuzzy question hours ago and hope this description can make it clear.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class TestMouseEvent {
public void createUI(){
JFrame frame = new JFrame("Test Mouse Event");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
MainPanel mainPanel = new MainPanel();
mainPanel.setPreferredSize(new Dimension(800, 600));
mainPanel.addMouseListener(new ImageMouseListener());
mainPanel.addMouseMotionListener(new ImageMouseAdapter());
frame.add(mainPanel,BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
TestMouseEvent testMouseEvent = new TestMouseEvent();
testMouseEvent.createUI();
}
#SuppressWarnings("serial")
class MainPanel extends JPanel{
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setFont(new Font("Arial", Font.PLAIN, 20));
g.drawString("I'm a panel and I'm being listened now", 200, 300);
}
}
class ImageMouseListener implements MouseListener{
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("clicked");
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("released");
JOptionPane.showMessageDialog(null, "I only want to be showed when \"drag\" event over but not for click event!");
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
class ImageMouseAdapter extends MouseAdapter{
public void mouseDragged(MouseEvent e){
if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
System.out.println("dragged");
}
}
}
}
I only want to show the JOptionPane right after the drag operation but not the click operation.So how can I know the difference?
Inside your ImageMouseAdapter set a flag (let's name if dragInProgressFlag) to true. On release check the flag. If it's true show the JOptionPane and reset it back to false.
First, unify your MouseListener and MouseMotionListener into a single class, you can do this simply by using MouseAdapter...
Second, add a instance variable to act as a flag to indicate if a drag operation is taking place...
class ImageMouseListener extends MouseAdapter {
private boolean isDragging = false;
#Override
public void mouseClicked(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("clicked");
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
if (isDragging) {
System.out.println("released");
JOptionPane.showMessageDialog(null, "I only want to be showed when \"drag\" event over but not for click event!");
}
isDragging = false
}
public void mouseDragged(MouseEvent e) {
if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
isDragging = true;
System.out.println("dragged");
}
}
}
Create a single instance of the ImageMouseListener and add it as the mouseListener and mouseMotionListener for your panel...
ImageMouseListener handler = new ImageMouseListener();
mainPanel.addMouseListener(handler);
mainPanel.addMouseMotionListener(handler);
You could consider using the inbuilt Drag'n'Drop support within in Java, depending on what you want to achieve, for example, have a look at Java - How to drag and drop JPanel with its components

JPanel problem - repaint on resize on normal look

I have strange problem with JPanel. I am trying to show svg image ( SVG_class extends JSVGCanvas from batik jar). Problem is when I start this program I get this
and when I resize with pointer frame a little I get normal picture like this
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
import javax.swing.*;
import org.apache.batik.swing.JSVGCanvas;
public class Main {
static JScrollPane scrollpane;
// The frame.
protected JFrame frame;
public static void main(String[] args) {
final JFrame f = new JFrame("frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(700, 500);
f.setBackground(Color.blue);
SVG_class svg = new SVG_class();
JPanel p = new JPanel();
p.setSize(new Dimension(700, 500));
p.add(svg);
p.setBackground(Color.red);
scrollpane = new JScrollPane(p);
scrollpane.setPreferredSize(new Dimension(700, 500));
Container cont = f.getContentPane();
cont.add(scrollpane);
f.setVisible(true);
}
}
public class SVG_class extends JSVGCanvas {
private List<Type> list;
private int rad;
public boolean red_dot(int iX, int iY, String sX, String sY){
boolean b;
int x,y;
x=Math.abs(iX-(int)Double.parseDouble(sX));
y=Math.abs(iY-(getSize().height-(int)Double.parseDouble(sY)));
System.out.println("iX="+iX+" iY="+iY+" sX="+sX+" sY="+sY+" x="+x+" y="+y);
b=(x<=rad) &&(y<=rad);
return b;
}
public SVG_class(){
try {
this.list=CSV.getCSV("map2");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String fileName = "map2";
File file = new File("C:\\Users\\Gigabyte\\Desktop\\SVG\\"+fileName+".svg");
try {
this.setURI(file.toURL().toString());
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
//
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent e) {
boolean found=false;
// System.out.println("pritisnuo si x="+e.getX()+" y="+e.getY());
int x,y;
for(int i=0; i<list.size() && !found;i++ ){
found=red_dot(e.getX(), e.getY(), list.get(i).getX(), list.get(i).getY());
}
if (found) {
System.out.println("pritisao!!!!");
}
System.out.println(" ");
}
});
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Can someone tell me what is mistake ? I tried with p.repaint but it didn't help.
Posting the source for SVG_class might make it easier to see what the problem is.
As it is, I suspect the problem is because you're not explicitly telling it what kind of layout to use.
If you want svg to take up the whole window you could do p.setLayout(new GridLayout(1, 1)); after you create p.
You should call the f.setSize(700, 500); just before showing.
SVG_class has probably set its size in the constructor. Don't do that and define the size at after calling the constructor.
EDIT: Try adding
f.pack();

Categories