I'm having trouble with making views visible. As in: they do all appear, but at the same time, whereas I would like to show them with some delay in between. Currently I have the following code, which should make it more clear:
public void performExperiment (View v) {
Log.i(TAG, "Experiment has started on view: " + v);
final ArrayList<FocusPoint> permutation = new ArrayList<>(Arrays.asList(focusPoints));
Collections.shuffle(permutation);
for (FocusPoint fp: permutation) {
try {
Thread.sleep(1000);
fp.setVisibility(ImageView.VISIBLE);
//fp.invalidate();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Sleep failed");
}
}
}
The FocusPoint-class is an extension of ImageView in Android and the method is called upon a button-click.
What needs to happen is that all the views show up in a random order on the screen, with a second between them. This code however waits for 16 seconds (the amount of views is 16) and then shows all the views at once. I tried invalidating my the view to redraw it, and I also tried to take 'fp.setVisibility(ImageView.VISIBLE)' out of the try-catch block, but both didn't work. (Of which the latter obviously doesn't work, I didn't really expect that to work, but I'm getting really desperate :P) I have been searching for hours now, and none of the StackOverflow-pages and other fora/documentation had an answer for this problem. How can I make sure that the focuspoint draws, before the loop continues to the next?
Using Thread.sleep(ms) to delay the UI thread is a very dangerous idea, and as you can see, it doesn't lead anywhere. By blocking the thread for 16 seconds, you are effectively freezing the application for that period - including any redraws and other event handling that might happen during that time. Don't ever do that.
Instead, use a Handler and its postDelayed(Runnable, ms) method to schedule visibility changes in the future. Handlers work by adding messages to the event loop, so they don't disrupt normal behavior.
Check this modified version of your code:
private static final long FP_SHOW_DELAY_MS = 1000;
private Handler handler = new Handler();
public void performExperiment (View v) {
Log.i(TAG, "Experiment has started on view: " + v);
final ArrayList<FocusPoint> permutation = new ArrayList<>(Arrays.asList(focusPoints));
Collections.shuffle(permutation);
for (int i = 0; i < permutation.size(); i++) {
final FocusPoint fp = permutation.get(i);
handler.postDelayed(new Runnable() {
#Override
public void run() {
fp.setVisibility(View.VISIBLE);
}
}, (i + 1) * FP_SHOW_DELAY_MS);
}
}
Related
I'm working on a Tetris-app and I have a Thread.sleep command to animate the falling of the tetriminos. But that creates a conflict with the UI. So I tried runOnUiThread() like this:
while (gameover == false) {
tetriminoSettled = false;
try {
Thread.sleep(1000 - (counter * 3));
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
#Override
public void run() {
Random ran = new Random();
int x = ran.nextInt(7) + 1;
addTetrimino(createTetrimino(x)); //the UI operation
gridView.invalidate();
}
});
But the UI gets only updated after the game ends.
Do you think AsyncTask is a good approach?
Please try to keep in mind that I later need additional UI-Threads for shifting the tetriminos left and right and so on.
Thanks!
Judging from the code you posted, it looks like you are using a gridview for a game. It is possible but not worth the effort. Just use a SurfaceView as shown in this short tutorial. You'll have an onDraw callback in which you can update whatever you like every drawing cycle. Have fun, coding games is really hard :).
Try gridView.postInvalidate();
I am building my first android application and I am trying to make a memory game. Anyhow, I need to make an array of buttons change color for 1 second and then return to its original color in order, for example: button1 changes to yellow, stays like that for 1 second then returns to gray, then button2 changes to yellow for 1 second then returns, and so on. I tried using the handler but it always works only after the last iteration, this is my code:
for (i = 0; i < 9; i++) {
buttonList.get(i).setBackgroundColor(Color.YELLOW);
runnable =new Runnable(){
#Override
public void run() {
buttonList.get(i).setBackgroundColor(Color.GRAY);
}
};
handler.postDelayed(runnable,1000);}
what am I doing wrong?
EDIT
Found How to do it. First I need to make a runnable class that takes paramaters ex MyRunnable implements Runnable (using Runnable interface), then writing a method that uses this paramater, I can't do it with the regular one because it depends on i and i changes with the iteration.
You need to create a new Runnable inside each loop because all 9 delayed posts are running the same runnable that you create on the 9th and final loop since the loop no doubt takes less than a second to complete. So try something like this:
for (i = 0; i < 9; i++) {
buttonList.get(i).setBackgroundColor(Color.YELLOW);
Runnable runnable = new Runnable(){
#Override
public void run() {
buttonList.get(i).setBackgroundColor(Color.GRAY);
}};
handler.postDelayed(runnable,1000);
}
You're synchronously (at the same time) setting all buttons' colors to yellow, and also creating 9 asynchronous tasks (one for each button) to change color to gray after one second. It means all buttons will change colors back to gray after around 1 second, (more or less) at the same time.
Think of the handler as a queue that you add tasks to. The call postDelayed() is scheduling your tasks to be executed in the future, but all of them are scheduled at the same time, so all of them will be executed at the same time in the future.
I haven't run it, but I think this approach is more of what you are looking for:
// Those are fields
private int buttonIndex = 0;
private boolean yellow = false;
private final Handler handler = new Handler(new Handler.Callback() {
#Override
public void handleMessage(Message msg) {
if (!yellow) {
buttonList.get(buttonIndex).setBackgroundColor(Color.YELLOW);
handler.sendEmptyMessageDelayed(0, 1000);
} else {
buttonList.get(buttonIndex).setBackgroundColor(Color.GRAY);
if (++buttonIndex < 9) handler.sendEmptyMessage(0);
}
yellow = !yellow;
}});
// Call this to start the sequence.
handler.sendEmptyMessage(0);
Note that I'm using sendEmptyMessage*() instead of post*(), but either approach could be used. Additionally, handler's messages (tasks) can have input parameters, so it'd be nice to use them.
I have an ArrayList of values, and I would like to iterate through the ArrayList. For every new value, I would like to update the chart with that value, and then wait a set amount of time before doing the same thing to the next value.
At the moment, my log says that all of the values are being iterated over. However, on my testing device, the chart does not update until the very end; at that point, all of the values are loaded at once, so there is no desired "slideshow" effect.
When I want to start playing back the values in my ArrayList, this method is called:
public void playback(){
if(ret != null) {
for (int x = 0; x < ret.size(); x++) {
addEntry(ret.get(x));
try {
Thread.sleep(100);
} catch (Exception e){
//Do nothing
}
}
} else {
Log.d(LOG_TAG, "ret was null.");
}
}
What can I do so that the values are displayed on my chart, one after another, with a certain amount of time between each value?
Edit: Here was the solution I ended up implementing with help from Shadab Ansari:
public void playback(){
if(ret != null) {
addEntry(0);
} else {
Log.d(LOG_TAG, "ret was null.");
}
}
private void addEntry(int index) {
final int in = index;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
yVals1.get(0).setVal(ret.get(in).intValue());
RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1");
// And other UI stuff
// Recursive call!
if(in < ret.size() - 1){
addEntry(in + 1);
}
}
}, 100);
}
In case it was not clear, ret was a global variable that contained the arrays that I was going to be inserting. yVals1 was an ArrayList of Entries to populate the radar chart.
The end result is that, in this example code, the chart is updated with the next value in my ArrayList every 100 milliseconds. During this time I can still zoom in/out of the chart and rotate it with no problems.
If your addEntry() performs a UI operation then let me explain your problem -
Explanation -
Android is an event based system. Something happens on the device (the screen is touched, a key is pressed, etc.) and Android raises an event. An App is notified of an event and when one occurs that it needs to respond to it does so, often running the code that you have written. Your App runs its code in a loop under the control of the Android Operating Systems (OS). This code loop is referred to as the App's thread of execution. There is only one thread and it is responsible for both running the App code and updating the display.
So the UI update does not happen immediately and your making the UI thread sleep for 100 ms every time the loop runs. And when Android tries to update the UI, you make the thread sleep which means during this time period UI thread will not do anything. And this happens till your loop finishes. After your loop ends, the final event gets executed and you will see your UI updated by the call of addEntry() with the latest value passed.
Solution -
You can use postDelayed()-
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Perform your task and it will be executed after 100 ms
}
},100);
The app I'm making requires that a bit of code be executed whenever the value of a particular variable changes from 0 to 1.
The handler example below is the method I'm currently using to do this (I copied it from someone else).
I have a feeling it's not a proper method though because having just three of these handlers in my app causes the UI to be fairly unresponsive, and causes the device (a phone) to become quite hot.
As you can see, I've put 10ms delays in the handlers to try to deal with this.
Isn't there something more like OnClickListener that can listen at all times for a variable value change without putting such stress on the CPU?
I'm pretty new to Java and Android so a simple example would be very much appreciated.
final Handler myHandler1 = new Handler();
new Thread(new Runnable()
{
#Override
public void run()
{
while (true)
{
try
{
Thread.sleep(10);
myHandler1.post(new Runnable()
{
#Override
public void run()
{
if (myVariable == 1)
{
myVariable = 0;
//do stuff
}
}
});
} catch (Exception e) {}
}
}
}).start();
You must set your variable via a setter method. Then, you can be reactive to that change.
public void setMyVariable(int value) {
this.myVariable = value;
if (myVariable == 1) {
doSomethingWhen1();
} else if (myVariable == 0) {
doSomethingWhen0();
}
}
A more elegant way to do that will be an observer pattern, Here you can find more detailed documentation about it.
You must certainly avoid while(true) loops on mobile device, it will drain your battery and you are also blocking the UI thread. That's the reason why your UI is unresponsive and your cellphone it's quite hot.
When trying to learn how to create a delay I researched and found the dominant answer to be to use Handler/Runnable/postDelayed.
Handler handler=new Handler();
final Runnable r = new Runnable()
{
public void run()
{
delayedMethod();
}
};
handler.postDelayed(r, 1000);
That worked ok for a while, but I've added a few more things going on and now they are sometimes happening in the wrong order.
This set of events:
paintScreen1()
...
delayedPaintScreen2()
...
paintScreen3()
is screwing up (sometimes) and doing this:
paintScreen1()
...
paintScreen3()
...
delayedPaintScreen2() (runs last and gets messed up by the actions of paintScreen3)
There doesn't seem to be another good way to create delays - one that doesn't create threads.
Solutions I have tried in order to make sure the code events run in the proper order:
0 Putting the main process inside one big synchronized block.
1 Putting the synchronized keyword in the method name of every method involved in the main process.
2 Putting the synchronized keyword only on the method in the Runnable.
3 Taking away the Handler/Runnable/postdelayed and replacing with handler.sendEmptyMessageDelayed(0,1000)
4 Making one Handler instance variable, used by every Handler/Runnable block (as opposed to Handler handler1, handler2, handler3, etc.)
5
Handler handler=new Handler();
final Runnable r = new Runnable()
{
public void run()
{
waitOver = true;
}
};
handler.postDelayed(r, 1000);
while (waitOver == false) {
}
delayedMethod();
waitOver = false;
My next attempt may be to try to used the Thread class somehow so I can call thread.join().
When that fails the next thing will be very long and complicated, I fear.
Any suggestions?
Any simple examples of a solution?
Thanks
Edit: I may be confused about whether Handler/Runnable results in literal threading or not.
Edit: It's a game. User makes a move, screen updated to show the move, calculation tells that they scored a point, recolor the boxes on the screen, add delay to allow user to see their point, then call method to removed colored squares, when that method completes and we return to the method that called it (containing the Handler/runnable), the code continues down to a point where it calls another method that results in a random square of the board being turned purple. So it should happen user-move, repaint to show point scored, delay so user can see point scored, repaint to erases squares, then random purple square happens. Sometimes what will happen (as far as I can tell) is the random purple square will execute before it should, choose one of the squares where the point was scored, interfere, and make it so the cleanup method gets confused and fails to cleanup.
mainmethod() {
...
if (pointscored) {
squaresglow();
...
//delay so user can see the glow before the cleanup happens
Handler-runnable
cleanup();
postdelayed
}
...
purpleSquare();
}
I hope this is not even more confusing. purpleSquare runs before cleanup and things get screwed up.
Edit:
Tried this:
6
CountDownLatch doneSignal = new CountDownLatch(1);
Handler handler=new Handler();
final LatchedRunnable lr = new LatchedRunnable(doneSignal);
handler.postDelayed(lr, COMPUTER_MOVE_DELAY);
try {
doneSignal.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
class LatchedRunnable implements Runnable {
private final CountDownLatch doneSignal;
LatchedRunnable(CountDownLatch doneSignal) {
this.doneSignal = doneSignal;
}
public void run() {
delayedProcess();
doneSignal.countDown();
}
}
7
ExecutorService executorService = Executors.newFixedThreadPool(5);
final CountDownLatch latch = new CountDownLatch(1);
executorService.execute(new Runnable() {
public void run() {
try {
Looper.prepare();
Handler handler=new Handler();
final Runnable r = new Runnable()
{
public void run()
{
delayedMethodCleanupCalc();
}
};
handler.postDelayed(r, 4000);
} finally {
latch.countDown();
}
}
});
try {
latch.await();
delayedMethodPaintScreen();
} catch (InterruptedException e) {
// todo >> handle exception
}
purpleSquare runs before cleanup and things get screwed up
mainmethod() {
...
if (pointscored) {
squaresglow();
...
//delay so user can see the glow before the cleanup happens
Handler-runnable
cleanup();
postdelayed
}
...
purpleSquare();
}
You have a design flaw here. Think of Handlers as a queue of messages that will execute code "later" whenever the processor decides to process messages and postDelayed as an inexact way to stuff that message at the bottom of the queue. If you call postDelayed and you still have lines of code left in the current method to execute, chances are very good that those lines will execute before postDelayed messages are even received.
What you are trying to do is to make sure purpleSquare() gets called after the pointscored routine has done it's job, which may require waiting for it to finish. PostDelaying to the message queue is not what you should be doing in this case. What you should be using is a semaphore and a pointScored thread.
Consider the following code design:
final Runnable pointScoredTask = new Runnable() {
public synchronized void run() {
try {
squaresglow();
//...
Thread.sleep(2500); //2.5 sec before cleanup occurs
cleanup();
} catch (InterruptedException e) {
}
notify(); //make sure we call notify even if interrupted
}
};
void mainmethod() {
//...
if (bPointWasScored) {
synchronized (pointScoredTask) {
try {
Thread psThread = new Thread(pointScoredTask,"pointscored");
psThread.start(); //thread will start to call run(), but we get control back to avoid race condition
pointScoredTask.wait(6000); //wait no more than 6 sec for the notify() call
} catch (InterruptedException e) {
}
}
//if a point was scored, nothing past this line will execute until scoreglow has been cleaned up
}
//...
purpleSquare();
//...
}
I know you'd rather avoid threads, but there are some things that just work much better when you use them. Try the above design and see if that works out the synchronization issues you were seeing.