Let's say you have a TextView that displays an number like 0 and you have a Button.
Now if the user presses the Button the Number in the TextView will increase for one (this i know how to do) but if the user presses the Button and don't release it then the number in the TextView should be increased an this should repeat it self as long the user don't release the Button.
In other words: How to increase the Number over and over again as long the user holds down the Button?
A general approach (not specific to Android) would be to detect the press and release event separately. The press event starts a periodic task (Runnable or Thread) which adds to the counter (let us say 5 times a second, or once every 200 ms). The release event stops the periodic task.
You'll need to schedule an asynchronous repeating event when you receive a mousePressed event and stop it when you receive a mouseReleased event.
There's lots of ways to handle this in Java. I like using the java.util.concurrent classes, which are quite flexible. There's a few things to keep in mind though:
If your asynchronous events don't happen on the Event Dispatch Thread, you need to set the JButton text using SwingUtilities.invokeLater().
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Frame
{
public static void main( String[] args )
{
JFrame frame = new JFrame( );
final JButton button = new JButton( "0" );
final ScheduledExecutorService executor = Executors.newScheduledThreadPool( 1 );
button.addMouseListener( new MouseAdapter( )
{
int counter = 0;
ScheduledFuture<?> future;
#Override
public void mousePressed( MouseEvent e )
{
Runnable runnable = new Runnable( )
{
public void run( )
{
SwingUtilities.invokeLater( new Runnable( )
{
public void run( )
{
button.setText( String.valueOf( counter++ ) );
}
} );
}
};
future = executor.scheduleAtFixedRate( runnable, 0, 200, TimeUnit.MILLISECONDS );
}
#Override
public void mouseReleased( MouseEvent e )
{
if ( future != null )
{
future.cancel( true );
}
}
} );
frame.add( button );
frame.setSize( 400, 400 );
frame.setVisible( true );
}
}
Set up a View.OnLongClickListener for your button
Give your activity a Runnable, and initialize (but don't start it) when you load the activity
Have the OnLongClickListener do its regular async task of updating the textbox and checking the time since it was first clicked
Create an OnTouchListener that pauses the Runnable when the touch event is realeased.
I know that's a rough draft, but this is a really useful pattern to be able to reuse and modify, so it's worth sinking your talons into it...
Related
Repaint is not calling PaintComponent.
I tried to call it from another method of the Try class too but it did not work out.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Try extends JPanel {
int i =0;
#Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
System.out.println("hey");
}
public static void main(String[] args) {
JFrame f=new JFrame();
Try t = new Try();
f.setSize(500,600);//400 width and 500 height
Container contentPane = f.getContentPane();
contentPane.add(new PaintComponent());
f.setVisible(true);//making the frame visible
while(true){
t.repaint();
}
}
}
There are multiple issues some of which are mentioned by #trashgod in comments.
You are calling repaint on the instance which you did NOT add to the content pane - you have a different one there (actually, you have something completely different there - new PaintComponent()).
Do not remove super.paintComponent ( g ); unless you clean up the area on your own (pretty much fill the whole component background if it is opaque), otherwise you will get visual glitches on such components upon repaint.
You are spamming repaint operations which is extremely bad, make at least some delay between the repaints giving Swing time to perform the repaints. The best case is if you repaint component only when it actually will display something different visually. If you need to update the view all the time - at least limit it to 30-60 frames (repaints) per second. Also, some internal Swing optimizations might "eat" some of the repaint calls, so expect that you might not see as many paintComponent calls as a number of repaints you call on the component.
You are working with Swing components outside of Event Dispatch Thread (shortly EDT) which might cause issues. Make sure you always use it to create Swing components and call any methods on them. SwingUtilities helps with that.
Any heavy operations that take a long time (or unknown time) to be completed should be executed outside of EDT, otherwise, your UI will simply hang while you are waiting for that operation to complete because all UI updates are performed on EDT and not anywhere else.
Considering all I said above, this is how your example should look like:
public class Try extends JPanel
{
#Override
public void paintComponent ( final Graphics g )
{
super.paintComponent ( g );
final Graphics2D g2d = ( Graphics2D ) g;
g2d.drawString ( Long.toString ( System.currentTimeMillis () ), 25, 35 );
System.out.println ( "repainted" );
}
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
final JFrame f = new JFrame ();
final Try t = new Try ();
f.getContentPane ().add ( t );
f.setSize ( 500, 600 );
f.setVisible ( true );
new Thread ( new Runnable ()
{
#Override
public void run ()
{
try
{
while ( true )
{
t.repaint ();
Thread.sleep ( 25 );
}
}
catch ( final InterruptedException e )
{
//
}
}
} ).start ();
}
} );
}
}
Hope that clarifies it a bit for you.
There is also SwingWorker class that helps to perform long-running tasks in Swing, but I didn't use it here to keep the example as simple as possible.
Also a side note - you do not need to call repaint () within EDT because it sends the repaint request to EDT on its own, so that method is safe to use on any thread (like I do in the example).
Well, I have been working on a piece of code for almost 2 days now and not able to resolve the issue.
DESIRED BEHAVIOUR
The following code is supposed to display 10 strings one by one (the next replacing the previous one) with a gap of aprox. 200 ms.
q1
q2
q3
...and so on upto q10
This display sequence starts when the user presses ENTER key.
REFLECTED BEHAVIOUR
The screen waits for aprox. 2 sec after pressing and then shows q10.
Some more info
The label stringText changes value during execution (which I found by writing to console) but the same is not updated on screen (JFrame).
The label changes values through click event on a button, everything else remaining same (as much as possible).
The timer is through a while loop - this may not be as per most people's liking, but lets forget it for the time being.
The method displayQuestion(int number) has a few unnecessary lines. I put them all because I was not sure what would work. Actually, nothing worked!
THE CODE
package sush4;
import java.util.Date;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sush4 {
// Timer control variables
static long holdTimeWord = 200L;
static long elapsedTime = 0L;
// Counter for Strings displayed
static int i = 0;
// Strings in use
static String[] questionStack = {"q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", "q9", "q10"};
// UI: String display variables
static JLabel stringText;
static JFrame mainWindow;
// Key binding action object
private static Action userKeyCommand;
/// Display the question
private static void displayQuestion(int number) {
mainWindow.remove(stringText);
stringText.setText(questionStack[number]);
mainWindow.add(stringText);
mainWindow.setVisible(true);
mainWindow.revalidate();
mainWindow.repaint();
}
private static void q120(){
//// Initiate the text
for(i = 0; i < questionStack.length; i++) {
displayQuestion(i);
//// And wait for Word hold time
long startTime = System.currentTimeMillis();
elapsedTime = 0L;
// Now wait for event to happen
while ( (elapsedTime < holdTimeWord) ) {
elapsedTime = (new Date()).getTime() - startTime;
}
}
}
Sush4() {
//// Create the Window
mainWindow = new JFrame("Sush");
mainWindow.setSize(700, 500);
mainWindow.setLayout(new FlowLayout());
//// And add key bindings for user events
userKeyCommand = new UserKeyCommand();
JRootPane rootPane = mainWindow.getRootPane();
rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ENTER"), "doEnterAction");
rootPane.getActionMap().put("doEnterAction", userKeyCommand);
// Terminate the program when the user closes the application.
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setResizable(false);
//// Add the text label
stringText = new JLabel("Random Text");
mainWindow.add(stringText);
//// Finally, display the frame.
mainWindow.setVisible(true);
}
static class UserKeyCommand extends AbstractAction {
public void actionPerformed( ActionEvent tf ) {
q120();
}
}
public static void main(String[] args) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Sush4();
}
});
}
}
The timer is through a while loop - this may not be as per most people's liking, but lets forget it for the time being.
Actually we can't forget about this while loop because this is what is causing troubles. See, q120() method is called when you click a button:
static class UserKeyCommand extends AbstractAction {
#Override // don't forget #Override annotation
public void actionPerformed( ActionEvent tf ) {
q120();
}
}
It means this code is executed in the context of the Event Dispatch Thread (EDT). This is a single and special thread where Swing components must be created/updated and event handling (i.e.: action events) must be performed. If we have a loop in this thread waiting for some condition to continue we'll block the EDT and GUI won't be able to repaint itself until the thread is unlocked.
For repetitive tasks (such as the one in your question) consider use a Swing Timer. For heavy tasks with interim results consider use a SwingWorker instead.
This question already has an answer here:
Using a Swing Timer to hide a notification temporarily
(1 answer)
Closed 9 years ago.
I was trying a simple code in java for test, just a button when u click it it sleeps for 5 seconds, here's the handler
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
System.out.println ("hiiii");
Thread.sleep (5000);
System.out.println ("bye");
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
I want this button doesn't receive any events until it finishes working (5 seconds), I tried to disable and enable it in the handler but in vain
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jButton1.setEnabled(false);
try {
System.out.println ("hiiii");
Thread.sleep (5000);
System.out.println ("bye");
} catch (InterruptedException ex) {
Logger.getLogger(NewJFrame.class.getName()).log(Level.SEVERE, null, ex);
}
jButton1.setEnabled(true);
}
You have to move any long-running task (in your case it is a simple sleep call) outside the EDT (Event Dispatch Thread) - that thread is used to render UI, if you block it with some operation - you block all your UI at once.
Here is a proper example how you can disable/enable button:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* #author Mikle Garin
* #see http://stackoverflow.com/a/18590057/909085
*/
public class ButtonSleep
{
public static void main ( String[] args )
{
JFrame frame = new JFrame ( "Custom list renderer" );
final JButton button = new JButton ( "Make me sleep 5 seconds" );
button.addActionListener ( new ActionListener ()
{
#Override
public void actionPerformed ( ActionEvent e )
{
button.setEnabled ( false );
new Thread ( new Runnable ()
{
#Override
public void run ()
{
try
{
Thread.sleep ( 5000 );
}
catch ( InterruptedException e1 )
{
//
}
SwingUtilities.invokeLater ( new Runnable ()
{
#Override
public void run ()
{
button.setEnabled ( true );
}
} );
}
} ).start ();
}
} );
frame.add ( button );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
}
}
Yes, it seems weird, but you have to move your operations to a separate thread (doesn't really matter which thread) and you have to execute any operations with UI (in this case - enabling/disabling button) inside the EDT - that is why i call setEnabled in invokeLater call and not inside the separate thread.
And yes, all Swing component listeners event calls are executed inside EDT from the beginning, so you are already inside EDT when you start executing your code inside actionPerformed method of your action listener - that is why you block the whole UI if you call sleep there.
I have a message label and a submit button. The submit button will be pressed multiple times, and the action for the each press can take up to a minute.
When the button is pressed, I want to set the message to empty, and after the task is complete, I want to set the message to "Complete".
private void submitActionPerformed(java.awt.event.ActionEvent evt) {
message = "";
updateMessageLabel();
doTheTask();
/* this update is apply to the label after completion */
message = "Complete";
}
Is it possible to update that message label before the submitActionPerformed() method is run (or in the method), but after the the button is clicked?
Although the Swing concurrency tutorial already contains some very good samples on how to deal with concurrency in Swing, find below an example which
contains a checkbox to prove the UI is still alive
has a progress bar, which gets updated from the SwingWorker
has a label, which gets updated once the SwingWorker is finished
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class SwingWorkerExample {
private static JProgressBar PROGRESS_BAR;
private static JLabel OUTPUT_LABEL;
private static JFrame createGUI(){
JFrame testFrame = new JFrame( "TestFrame" );
PROGRESS_BAR = new JProgressBar( );
PROGRESS_BAR.setMinimum( 0 );
PROGRESS_BAR.setMaximum( 100 );
OUTPUT_LABEL = new JLabel( "Processing" );
testFrame.getContentPane().add( PROGRESS_BAR, BorderLayout.CENTER );
testFrame.getContentPane().add( OUTPUT_LABEL, BorderLayout.SOUTH );
//add a checkbox as well to proof the UI is still responsive
testFrame.getContentPane().add( new JCheckBox( "Click me to proof UI is responsive" ), BorderLayout.NORTH );
testFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
return testFrame;
}
public static void main( String[] args ) throws InvocationTargetException, InterruptedException {
EventQueue.invokeAndWait( new Runnable() {
#Override
public void run() {
JFrame frame = createGUI();
frame.pack();
frame.setVisible( true );
}
} );
//start the SwingWorker outside the EDT
MySwingWorker worker = new MySwingWorker( PROGRESS_BAR, OUTPUT_LABEL );
worker.execute();
}
private static class MySwingWorker extends SwingWorker<String, Double>{
private final JProgressBar fProgressBar;
private final JLabel fLabel;
private MySwingWorker( JProgressBar aProgressBar, JLabel aLabel ) {
fProgressBar = aProgressBar;
fLabel = aLabel;
}
#Override
protected String doInBackground() throws Exception {
int maxNumber = 10;
for( int i = 0; i < maxNumber; i++ ){
Thread.sleep( 2000 );//simulate long running process
double factor = ((double)(i+1) / maxNumber);
System.out.println("Intermediate results ready");
publish( factor );//publish the progress
}
return "Finished";
}
#Override
protected void process( List<Double> aDoubles ) {
//update the percentage of the progress bar that is done
int amount = fProgressBar.getMaximum() - fProgressBar.getMinimum();
fProgressBar.setValue( ( int ) (fProgressBar.getMinimum() + ( amount * aDoubles.get( aDoubles.size() - 1 ))) );
}
#Override
protected void done() {
try {
fLabel.setText( get() );
} catch ( InterruptedException e ) {
e.printStackTrace();
} catch ( ExecutionException e ) {
e.printStackTrace();
}
}
}
}
Yes you can do this using SwingWorker thread, do all the pre submitActionPerformed() activities like updating the label, in the execute() method of the currentThread and call doTheTask() as a background job using worker Thread.
I suggest you to go through this documentation for reference about SwingWorker Thread
I want to run a timer which says time is expired after 30 seconds,how can do so?
Some task to be run only for some seconds then showing expired, how can i do so?
I'd recommend using ScheduledExecutorService from the java.util.concurrent package, which has a richer API than other Timer implementations within the JDK.
// Create timer service with a single thread.
ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
// Schedule a one-off task to run in 10 seconds time.
// It is also possible to schedule a repeating task.
timer.schedule(new Callable<Void>() {
public Void call() {
System.err.println("Expired!");
// Return a value here. If we know we don't require a return value
// we could submit a Runnable instead of a Callable to the service.
return null;
}
}, 10L, TimeUnit.SECONDS);
The actionPerformed method is called after 30 sec
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class TimerExample {
public static void main(String args[]) {
new JFrame().setVisible( true );
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
System.out.println( "expired" );
}
};
Timer timer = new Timer( 30000, actionListener );
timer.start();
}
}
Use Timer.