Android: Forcing UI refresh immediately - java

I'm trying to build an android app which contains a countdown timer and should display three images for an amount of time in each interval - a masking image for 200 ms, a stimulus image for 20 ms and another masking image for again 200 ms.
The app works with two threads - the main UI thread and the time management thread.
The problem is, that the UI thread doesn't refresh itself, because it sleeps in between and hence doesn't display any images.
I've searched many hours already to find a way forcing the UI thread to refresh itself immediately, but until now I'm unsuccessful.
The methods invalidate() or postinvalidate() for example don't do anything useful.
It would be great if anyone has a hint or a solution for this problem.
Thanks for your help.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button button;
ImageView imageView;
TextView textViewCounter;
boolean buttonWasPressed = false;
double startTime;
double currentTime;
double timer;
final int INTERVALS = 2;
final double SECONDS_TO_NANOSECONDS_COEFFICIENT = 1000000000.0;
// length of the interval in seconds
final double INTERVAL_LENGTH = 10 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
int intervalCounter = 0;
// masking time in milliseconds (200)
final double MASKING_TIME = 0.2 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
// stimulus time in milliseconds (20)
final double STIMULUS_TIME = 0.02 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
boolean stimuliShouldBeDisplayed = false;
boolean stimuliIsDisplayed = false;
boolean imageViewShouldBeCleared = false;
Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button);
if (button != null) {
button.setOnClickListener(this);
}
imageView = (ImageView) this.findViewById(R.id.imageView);
textViewCounter = (TextView) this.findViewById(R.id.textViewCounter);
// messages are sent to the thread where the Handler was created
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// not sure if you must always clear the message queue
this.removeMessages(0);
double tempStartTime = System.nanoTime();
// milliseconds are okay
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
if (stimuliShouldBeDisplayed && !stimuliIsDisplayed) {
stimuliIsDisplayed = true;
// show mask
imageView.setImageResource(R.mipmap.mask);
try {
Thread.sleep((long) (MASKING_TIME / 1000000));
} catch (InterruptedException e) {
e.printStackTrace(
// update our timer (milliseconds are okay)
timer += System.nanoTime() - tempStartTime;
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
// show stimulus
imageView.setImageResource(R.mipmap.stimulus);
try {
Thread.sleep((long) (STIMULUS_TIME / 1000000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// update our timer (milliseconds are okay)
timer += System.nanoTime() - tempStartTime;
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
// show mask
imageView.setImageResource(R.mipmap.mask);
try {
Thread.sleep((long) (MASKING_TIME / 1000000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// update our timer (milliseconds are okay)
timer += System.nanoTime() - tempStartTime;
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
}
// clear the imageView
if (imageViewShouldBeCleared) {
imageView.setImageResource(0);
imageViewShouldBeCleared = false;
stimuliIsDisplayed = false;
stimuliShouldBeDisplayed = false;
}
}
};
}
#Override
public void onClick(View v) {
if (v == button && !buttonWasPressed) {
buttonWasPressed = true;
// let's start our timer
startTime = System.nanoTime();
Runnable runnableTimeManagement = new Runnable() {
#Override
public void run() {
while (currentTime - startTime <= INTERVAL_LENGTH && intervalCounter < INTERVALS) {
currentTime = System.nanoTime();
timer = currentTime - startTime;
// next interval
if (timer > INTERVAL_LENGTH) {
intervalCounter++;
startTime = currentTime;
imageViewShouldBeCleared = true;
}
// 1 seconds extra for the communication time between TimeManagement Thread and GUI Thread
if (timer + SECONDS_TO_NANOSECONDS_COEFFICIENT >= INTERVAL_LENGTH - 2 * MASKING_TIME - STIMULUS_TIME) {
stimuliShouldBeDisplayed = true;
}
// we must always create a new empty message
Message message = Message.obtain();
// we send message to the main UI thread
handler.sendMessage(message);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// time is over
buttonWasPressed = false;
intervalCounter = 0;
}
};
new Thread(runnableTimeManagement).start();
}
}
}
Does somebody know a way to precisely control the display time for the images in an alternative way ? The best solution is displaying the stimulus image for only one frame. But I don't know how to have access to the frame rate.
Ist there a possibility to force the UI thread to refresh itself immediately ?

Thread.sleep is usually not a good idea. And I'm not sure you need a secondary thread for what you describe. You could simply use Handler.sendMessageDelayed for UI updates to happen in the future:
// will run as soon as possible (almost immediately)
handler.sendMessage(handler.obtainMessage(0, R.mimap.mask, 0));
// will run in 200ms
handler.sendMessageDelayed(handler.obtainMessage(0, R.mimap.stimulus, 0), 200);
// will run in 220ms
handler.sendMessageDelayed(handler.obtainMessage(0, R.mimap.mask, 0), 220);
And then your handler only displays the image it received in the argument to the message:
#Override
public void handleMessage(Message msg) {
imageView.setResource(msg.arg1);
}
But keep in mind that you don't exactly control the refresh rate, so this won't guarantee that the image will be displayed for exactly 20ms

Related

How to fill progressBar using separate thread?

I'm currently working on the following problem : How to use a thread in order to fill a progress bar (step by step) when the start button is clicked.
I encountered no problems building the thread or the progress bar.
However, when it came to filling the bar I proceeded as following :
public class event implements ActionListener{
#Override
public void actionPerformed(ActionEvent ae) {
Try t = new Try(250);
Thread MyT = new Thread(t);
MyT.start();
try {
MyT.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Gui.class.getName()).log(Level.SEVERE, null, ex);
}
pr.setValue(pr.getValue()+25);
System.out.println("check");
try {
MyT.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(Gui.class.getName()).log(Level.SEVERE, null, ex);
}
pr.setValue(pr.getValue()+25);
System.out.println("check");
}
}
The problem here is that I expected the bar to be filled with 25 units after 500 ms, then again filled with other 25 units(step by step) after 5000ms. Instead my program won't fill the bar with 25 after 500 ms, but with 50 after 5000 ms. Why is that and how can I make the bar charge gradually, without modifying the thread class ?
Yes, I know the question is old but maybe it helps someone.
I have implemented a progress bar class that provides a simple method, which fills the progress bar to a certain value in a specified amount of time.
Use like this:
// create progress bar
ProgressBar progressbar = new ProgressBar();
// fill progress bar in steps each one second
progressbar.fillInMills(1000, 25);
progressbar.fillInMills(1000, 50);
progressbar.fillInMills(1000, 75);
progressbar.fillInMills(1000, 100);
// resets to zero
progressbar.reset();
import javax.swing.JProgressBar;
public class ProgressBar extends JProgressBar {
Thread thread;
final int MAX = 100;
final int MIN = 0;
public ProgressBar() {
super(HORIZONTAL);
setMinimum(MIN);
setMaximum(MAX);
}
/**
* Fills the progress bar to the specified level in the specified time.
*
* #param millis the time in milliseconds
* #param fillTo the level in percentage
*/
public void fillInMills(final int millis, final int fillTo) {
thread = new Thread(() -> {
try {
final int startAt = getValue();
final int timeBetweenSteps = millis / Math.max(fillTo - startAt, 1);
for (int i = startAt; i <= fillTo; i++) {
setValue(i);
Thread.sleep(timeBetweenSteps);
}
} catch (final InterruptedException ignored) {
// thread is cancelled
}
});
thread.start();
}
/**
* Resets the ProgressBars level to zero.
*/
public void reset() {
if (thread != null) {
thread.interrupt();
}
this.setValue(0);
}
}
If you need advanced features of the JProgressBar, see this Oracle Tutorial on Progress Bars for detailed info.

Countdown Time in Android

I am developing an Android app and I have a requirement to:
1) Countdown time from 'x' minutes to '1' minute and then once it is at 1 minute, count in terms of 60 sec, 30 sec and then 0.
I am counting down in terms of 1 min(60000).
My code is:
public void countdowntimer(long timeinmillis, long countdowninterval){
Log.d("hi","Iamhere 0" + timeinmillis/60000);
new CountDownTimer(timeinmillis, countdowninterval) {
public void onTick(long timeinmillis) {
//Countdown the time in terms of minutes
Log.d("hi","Iamhere 2" + timeinmillis);
tv.setText(String.valueOf((timeinmillis)/60000) + "min");
rootView.invalidate();
if(timeinmillis <= 60000 && timeinmillis > 30000){
tv.setText(String.valueOf(60) + "sec");
rootView.invalidate();
}else if(timeinmillis < 30000){
tv.setText(String.valueOf(30) + "sec");
rootView.invalidate();
}
}
public void onFinish() {
}
}.start();
}
The logs are:
Iamhere0 4
Iamhere1 3
Why is my second log showing 1 minute lesser than first log and how do I implement 60sec, 30sec countdown once it is at 1 minute?
The expected output is:
4 min
3 min
2 min
1 min -> 60sec
30sec
0
It took me some time to answer it, this was my holydays ... but here is one other solution if this is still interresting for anyone.
I prefer to use System.currentTimeInMilli to manage the timer. When I start it, I calculate the time to end then on each tick, I calculate the remaining time to show.
This design prevent the delay you will find with any incremental/decremental variable timer. To keep this class reusable, I used the Observer pattern, on each tick (100ms on this example), the Observable instance will be send to each observer.
public class RunnableTimer extends Observable implements Runnable {
private final Handler handler;
// Number of milliseconds to run before the end
private Long delay;
// Date in millis until the end of the timer
private Long countDownEnd = null;
//The calculate remaining time
private long time;
public RunnableTimer(Observer o){
this.handler = new Handler();
addObserver(o);
}
/**
* Start the timer
* #param delay : the number of milliseconds to run
*/
public void start(Long delay){
this.delay = delay;
countDownEnd = System.currentTimeMillis() + delay;
handler.postDelayed(this, 0);
}
#Override
public void run() {
time = countDownEnd - System.currentTimeMillis();
if (time > 0)
handler.postDelayed(this, 100); //Recalculate every 100ms, to keep some precision... can be more or less.
else
time = 0; //to prevent to show negative values
sendUpdate();
}
public String toString(){
long t = time / 1000;
Log.d("time", "" + t);
if(t >= 60){
return String.format("%d min.", t/60);
} if t <= 0){
return "End";
} else {
return String.format("%d sec.", (t/30+1)*30);
}
}
/**
* Send the instance to each observer.
*/
private void sendUpdate(){
setChanged();
notifyObservers(toString());
}
}
From this, you have a timer that use the system time to calculate the remaining time and the toString method that create the desire template.
To use it, simply create an instance with the Observer you want.
public class Test implements Observer {
private RunnableTimer timer;
public Test(){
timer = new RunnableTimer(this);
timer.start(65000) //65sec
}
#Override
public void update(Observable observable, Object data) {
if(data == timer){ //If the update comes from the timer
Log.d("timer", timer.toString());
}
}
}
I had to clear a huge part of my class but this should works fine since this is simpler version.
EDIT : In your case, your activity / fragment should implements Observer and in the update, where I log the timer, you should put the toString result into you textfield.
try like this :
public class MyTimeCount extends CountDownTimer {
private Button btn_code;
public MyTimeCount(long millisInFuture, long countDownInterval,Button btn) {
super(millisInFuture, countDownInterval);
btn_code=btn;
}
#Override
public void onFinish() {
btn_code.setText("Reacquire");
btn_code.setEnabled(true);
}
#Override
public void onTick(long millisUntilFinished){
btn_code.setEnabled(false);
btn_code.setText(millisUntilFinished /1000+"s");
}
}
Use AtomicInteger to maintain the threads condition.
In First Condition its display time in minute
if (min.get() > 1) {
mCount.setText(Integer.toString(min.get())+" min");
handler.postDelayed(this, 1000);
min.getAndDecrement();
}
AND
In Last one minute its goes to second condition which display time in seconds
else {
if(min.get()== 1) {
mCount.setText("60 Sec");
handler.postDelayed(this, 500);
}
else if(min.get() == 0){
mCount.setText("30 Sec");
handler.postDelayed(this, 500);
}
else
mCount.setText("0 Sec");
min.getAndDecrement();
}
Use this ...........
final TextView mCount = (TextView) findViewById(R.id.count);
final Handler handler = new Handler();
final AtomicInteger min = new AtomicInteger(4);
final Runnable counter = new Runnable() {
#Override
public void run() {
if (min.get() > 1) {
mCount.setText(Integer.toString(min.get())+" min");
handler.postDelayed(this, 1000);
min.getAndDecrement();
} else {
if(min.get()== 1) {
mCount.setText("60 Sec");
handler.postDelayed(this, 500);
}
else if(min.get() == 0){
mCount.setText("30 Sec");
handler.postDelayed(this, 500);
}
else
mCount.setText("0 Sec");
min.getAndDecrement();
}
}
};
handler.postDelayed(counter, 0);
enjoy coding.....

Android CountDownTimer Class Lagging Main Thread

I am trying to use android.os.CountDownTimer to literally show a countdown timer via a textview for fitness purposes. The issue I am having is the timer seems to be having trouble running on the main thread i.e. the countdown will jump 2-4 secs and is clearly being "lagged" - the timer is intended to be in an endless loop until a stop button is pressed.
I am new to Java and Android and cannot figure out how to get the countdown timer running and updating the UI without any conflicts or lagging.
I have attempted to put the CountDown in a Handler/Runnable and an Asynctask with no luck.
MainActivity
CountDownTimer timer;
void countdownTimer() {
long min = countdownMins * 60000;
long sec = countdownSecs * 1000;
long milliseconds = min+sec;
timer = null;
timer = new CountDownTimer(milliseconds, 1000) {
public void onTick(long millisUntilFinished) {
long mins = millisUntilFinished / 60000;
long secs = millisUntilFinished % 60000 / 1000;
String display = String.format("%02d:%02d", mins, secs);
tvTextView.setText(display);
}
public void onFinish() {
countdownTimer();
}
}.start();
}
Many thanks to anyone who can show me how to get this running off the main thread so my UI elements will be smooth.
I decided to take a different approach which has served my purposes very well. No lag or thread issues and can easily be restarted over and over. Hope this helps someone out.
int startCountdown = 5;
int currentCountdown;
Handler countdownHandler = new Handler();
Timer countdownTimer = new Timer();
public void startCountdownTimer() {
currentCountdown = startCountdown;
for (int i = 0; i <= startCountdown; i++) {
TimerTask task = new TimerTask() {
#Override
public void run() {
countdownHandler.post(doA);
}
};
countdownTimer.schedule(task, i * 1000);
}
}
final Runnable doA = new Runnable() {
#Override
public void run() {
if (currentCountdown != 0) {
tvTextView.setText("" + currentCountdown);
currentCountdown--;
} else {
currentCountdown = startCountdown;
startCountdownTimer();
}
}
};
Try to use Handler and runnable instead of CountDownTimer. Here is a good article about this approach
http://www.mopri.de/2010/timertask-bad-do-it-the-android-way-use-a-handler/
One more thing you could try is
void countdownTimer() {
final Runnable runnable = new Runnable() {
#Override
public void run() {
tvTextView.setText(display);
}
};
long min = countdownMins * 60000;
long sec = countdownSecs * 1000;
long milliseconds = min + sec;
timer = null;
timer = new CountDownTimer(milliseconds, 1000) {
public void onTick(long millisUntilFinished) {
long mins = millisUntilFinished / 60000;
long secs = millisUntilFinished % 60000 / 1000;
final String display = String.format("%02d:%02d", mins, secs);
textView.post(runnable);
}
public void onFinish() {
countdownTimer();
}
}.start();
}
If you countdown with changing your TextView fast and frequently. You have to set your TextView width and height in a fix number, or it will lag while calculating your view scale.

Android, I can't update the image src for until another thread is sleeping 600 ms

I want to give the sensation that a button was pressed by the system. The two images are used when the user presses the button and I want to create the same effect using a thread sleeping between the display of both images.
But the system doesn't update the button src as expected. Resuming I want to change the src, wait 600 ms and update again. Can someone give me a direction on it?
switch (color) {
case 1: //green
// first image
Main.imageButtonGreen.setImageResource(R.drawable.light_green);
//this is just to make it sleep to give the sensation that the button was pressed
try {
Blink blink = new Blink();
Thread t = new Thread(blink);
t.run();
t=null;
}
catch (Exception e){if (Main.debug) Log.d("Blink.blink Switch color= ", e.getMessage());}
//scond image
Main.imageButtonGreen.setImageResource(R.drawable.dark_green);
break;
}
I recommend you to use Handler or AsyncTask. This is how I would make:
Create Handler in UI Thread:
Handler UIHandler = new Handler(this);
Now just call
UIhandler.postDelayed(new Runnable(){
#Override
public void run() {
button.setBackgroundColor(Color.GREEN);
}}, 600);
or even better to make changing color smoother
final long startTime = SystemClock.uptimeMillis();
final long duration = 600;
//here specify what interpolator you like
final Interpolator interpolator = new LinearInterpolator();
//here you can pass whatever you want
final int startColor = Color.GREEN;
final int endColor = Color.CYAN;
//take start color components
final int startRed = Color.red(startColor);
final int startGreen = Color.green(startColor);
final int startBlue = Color.blue(startColor);
//take delta color components
final int deltaRed = Color.red(endColor)-startRed;
final int deltaGreen = Color.green(endColor)-startGreen;
final int deltaBlue = Color.blue(endColor)-startBlue;
UIhandler.post(new Runnable() {
#Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - startTime;
float t = interpolator.getInterpolation((float) elapsed / duration);
if (t < 1.0) {
button.setBackgroundColor(Color.argb(255, (int)(startRed+t*deltaRed), (int)(startGreen+t*deltaGreen), (int)(startBlue+t*deltaBlue)));
// Post again 16ms later.
UIhandler.postDelayed(this, 16);
} else{
//to restore default values
button.setBackgroundColor(endColor);
}
}
});
Also you can change alpha parameter by passing value from 1 to 255 in Color.argb() as first parameter

JavaGame character is not moving accurately while drawing?

for personal practice, I am remaking Flappy bird for desktop with Java, I have managed to get all the pillars generating, screen, bg moving done, but now I have one problem, which is performance.
I sometimes feel that the game isn't moving as fast, and sometimes gets stuck for 0.5 secs or something, but that's not the case, when I move my bird, it's moving a bit weird, looks like its moving too much forward & then to the back, watch the gif in MP4 format:
http://gyazo.com/d7e94c0b772192e5a5dd1d2b61b8c529
What could be causing that? could this be my game loop or the way I draw the graphics? I don't use double buffering, I render simply by calling .repaint in jframe which repaints the JPanel I added.
Game loop:
private void gameLoop() {
new Thread() {
private long last;
private long start;
private int wait;
public void run() {
while(game) {
long now = System.nanoTime();
long length = now - lastLoop;
lastLoop = now;
double delta = length / ((double) Constants.OPTIMAL);
lastFps += length;
fps++;
if (lastFps >= 1000000000) {
System.out.println("FPS: " + fps);
lastFps = 0;
fps = 0;
}
update(delta);
render();
this.sleep
}
}
private void sleep() {
try {
Thread.sleep((this.last - System.nanoTime() + Constants.OPTIMAL) / 1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
My drawing area:
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int translateAmount = this.game.getLevel().getTranslate();
g.translate(-translateAmount, 0);
this.background.render(g2d);
this.game.getLevel().renderLevel(g2d);
g.translate(0, 0);
this.game.getBird().render(g2d);
}
The way I render the backgrounds is this: I add backgrounds one after one, and if a background is outside of the frame, i won't render it.
public void render(Graphics2D g) {
Iterator<RepeatedBackground> itr = this.repeats.iterator();
while (itr.hasNext()) {
RepeatedBackground b = itr.next();
if (this.game.getLevel().getTranslate() - b.getX() >= 300) {
itr.remove();
continue;
}
if (b.getX() - this.game.getLevel().getTranslate() < Constants.WIDTH) {
b.render(g);
}
}
}
This is how I move my bird (i dont use delta, used some tutorial on this):
private void update(double delta) {
if (System.currentTimeMillis() - this.level.getTime() >= Constants.TRANSLATE_SPEED) {
// move background
this.level.updateTranslate();
this.level.setTime();
// move bird
this.getBird().move();
}
}
public void move() {
this.x += 2 / 1.10;
}
What could be causing lag to the bird or the background? Is there something wrong with my rendering ways or game loop?
Fps always printing this:
FPS: 1724172
FPS: 1551857
FPS: 1494378
FPS: 1471987
FPS: 1434095
FPS: 1629905
Your game loop confuses me - but that's not hard ;)
Basically, as I understand these things, it should work something like...
while (gamming) {
now = get current time;
update game state
delta = get current time - now
delay = desired delay - delta
wait for delay
}
You don't seem to be taking into account the amount of time that the render and update methods "might" take to execute and calculating the time to wait based those requirements...
Now, I'm not sure what your delta values is suppose to be...the time through the current second?? So I've left it out of my example...
The following example gives me a constant 25fps, even with the random delays in the update and render methods
Now, forgive me, I typically work in milli seconds, it's just simpler on my poor, feeble mind.
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestGameLoop {
public static void main(String[] args) {
new TestGameLoop();
}
public TestGameLoop() {
gameLoop();
}
public static class Constants {
public static final double OPTIMAL = 25; // fps...
}
private boolean game = true;
private long lastLoop;
private long lastFps;
private long fps;
private void gameLoop() {
new Thread() {
private long last;
private long start;
private int wait;
public void run() {
// Calculate the optimal/maximum delay time
// This is converted to nanos so it can be
// used to calculate the actual delay...
long millisPerSecond = TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS);
long optimalDelay = Math.round(millisPerSecond / Constants.OPTIMAL);
optimalDelay = TimeUnit.MILLISECONDS.toNanos(optimalDelay);
// Last start of a "second" loop
long loop = System.nanoTime();
// While gaming...
while (game) {
// Start of this cycle...
long now = System.nanoTime();
// Update the state and render the
// current frame...
update();
render();
// How long did that update take??
long timeTaken = System.nanoTime();
long delta = timeTaken - now;
// Subtract the delay from the maximum delay
long delay = optimalDelay - delta;
if (delay > 0) {
try {
// Sleep expects milliseconds...
delay = TimeUnit.NANOSECONDS.toMillis(delay);
Thread.sleep(delay);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
// Calculate if we've being running for a second yet...
long loopDelay = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - loop);
// If the loop has been cycling for a second...
if (loopDelay >= 1) {
// Reset the loop time
loop = System.nanoTime();
System.out.println("FPS = " + fps);
fps = 0;
} else {
// Add another frame to the pile...
fps++;
}
}
}
}.start();
}
public void update() {
try {
Thread.sleep(Math.round(Math.random() * 20));
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public void render() {
try {
Thread.sleep(Math.round(Math.random() * 20));
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}

Categories

Resources