I tried plugging a SeekBar into my video app, but it doesn't seem to ever reach the end of the bar. Here's the code:
videoSeekBar = (SeekBar) activity.findViewById(R.id.videoPlayerSeek);
videoDuration = player.getDuration();
videoSeekBar.setMax(videoDuration);
videoSeekBar.setProgress(0);
// Start lengthy operation in a background thread
new Thread(new Runnable() {
public void run() {
while (videoPlayProgress < videoDuration) {
videoPlayProgress = player.getCurrentPosition();
// Update the progress bar
handler.post(new Runnable() {
public void run() {
videoSeekBar.setProgress(videoPlayProgress);
}
});
}
}
}).start();
Then, I have an OnCompleteListener attached to the MediaPlayer. When it is called,
player.getCurrentPosition() returns 6209
player.getDuration() returns 6592
Which can't be right. At this point, the slider status indicator isn't quite at the end of the slider bar, but there's a slight gap. The shorter the video's length, the bigger the gap.
First, consider <= instead of <.
More importantly, you do not need or want a busy-loop background thread for this. Go with something like:
Runnable r=new Runnable() {
public void run() {
videoSeekBar.setProgress(player.getCurrentPosition());
if (player.getCurrentPosition()<player.getDuration()) {
videoSeekBar.postDelayed(r, 33);
}
}
};
and somewhere call run() on r, when your video starts up. This will update the SeekBar about 30 times per second.
Your current implementation is a foreground busy loop, posting thousands of messages onto the main application thread's queue, which will make your video playback performance worse and waste battery for no particular advantage.
This still might not get all the way to the end -- it's possible that the video will reach the end between the setProgress() call and the if test. The true test of it being at the end is the OnCompletionListener, so just position the SeekBar at the end at that point.
Related
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 the Problem that my Android app does not delay a second (or 10 seconds), if I use the postDelayed method..
Basically I would like my program to wait one second after I clicked the button, then update the text on my textview ("READY"), wait another 2 seconds, then update the textview again ("SET") and then it should start another activity (not yet implemented :-) ).
With my code, the programm starts and after I click the button the textview shows the last text ("SET") immediately.. It just does not wait.
What am i doing wrong?
Here is my code:
public class MyCounterActivity extends Activity {
private long mInternval = 100000;
private Handler mHandler;
private Runnable mStatusChecker = new Runnable() {
#Override
public void run() {
//updateInterval(); //change interval
startRepeatingTask();
}
};
void startRepeatingTask(){
mHandler.postDelayed(mStatusChecker, mInternval);
//mStatusChecker.run();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gym_counter);
final TextView tv1 = (TextView) findViewById(R.id.fullscreen_content);
final Button startButton = (Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final long up;
EditText textUp = (EditText) findViewById(R.id.editTextUp);
up = Integer.parseInt(textUp.getText().toString());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//
}
},1000);
Log.d("after 1 runnable", "whaaat");
tv1.setText("Ready");
handler.postDelayed(new Runnable() {
#Override
public void run() {
//
}
}, 2000);
Log.d("after 2nd runnable", "whaaat 2");
//startRepeatingTask();
tv1.setText("SET");
}
});
}
I also tried to run it with the runOnUiThread() (within the onClick(View v) but with with the same result). I expected it to wait 1 second (startRepeatingTask()) and then runs the loop and waits several seconds...
runOnUiThread(new Runnable() {
#Override
public void run() {
startRepeatingTask();
for (int u = 0; u < up; u++){
startRepeatingTask();
}
}
}
});
Hope my description makes sense :-).
Thank you for your help!
EDIT:
I was now able to find a solution for my first problem. The answer from #mad in this post helpded me: How to start a different activity with some delay after pressing a button in android?
(Thats probably the same thing that #laalto tried to tell me. Thanks for the hint!)
In the onClick()
tv1.setText("READY");
mHandler.postDelayed(mDelay1, 2000);
And then the Runnable
private Runnable mDelay1 = new Runnable() {
#Override
public void run() {
if (tv1.getText()=="READY")
tv1.setText("SET");
}
};
BUT:
If i want to refresh the text on my Textview after every second, how do i do that? I cant just call mHandler.postDelayed() several times.. Any help is appreciated.
When you call postDelayed(), it just places the Runnable in a queue and returns immediately. It does not wait for the runnable to be executed.
If you need something to happen after a delay, put the code in the run() method of the runnable.
Whenever you call something like Thread.start(), handler.postDelayed, view.postDelayed, AsynchTask, TimerTask .. you enter the world of threading or you might call it parallel computing.
So there can be multiple threads ("codes") running at the same time.
When you are inside your Activity it is running in a Thread that is calld UI-thread or main thread. All graphics is handled in that thread and that thread alone.
Do NEVER wait in the UI-thread!
Example: you have a button that switches color from say gray to yellow on pressing it. Now you enter a Thread.sleep(10000); - waiting 10 seconds at the start of your onClick.
You will then see that the button stays yellow (=pressed) for 10 seconds even if you only pressed very shortly. Also: if you overdo it android os will become angry and post the user if he wants to force-close your app.
So what happens on handler.postDelayed?
Android will very quickly open a thread that runs in the background parallel to your UI thread. So in some nanoseconds it has done that and will execute the next command in UI thread (in the example above it is Log.d). In the background it will wait and count the millis until time is up. Then any code that is inside the runnable.run method will again be executed in the ui-thread after the wait.
Note also: postDelayed will not be super precise with the wait time as usually the ui-thread is quite buisy and when the wait time is up it may have something else to do. Your runnable code will be added to a queue and executed when ui-thread is ready again. All this happens without you having anything to do about it.
Also:
Remember to work with try/catch inside the runnable.run as many things can happen while waiting - for example user could press Home button closing your app - so the ui-element you wanted to change after the wait could already been destroyed.
I have an Android app that needs to display images and videos in a loop, it has no problem with playing the videos, but when it gets to the images the screen goes blank and nothing happens. the program is still running just not displaying.
I'm using SurfaceView.setBackground() to draw the image because swapping between an ImageView and the SurfaceView that the videos are played on has caused problems before.
I was having a similar issue when displaying the videos and I solved it by removing the loop I had waiting for the video to be prepared so ideally I would like to remove the loop I have waiting for a timer to elapse.
I have a function to display the new media when the old one is finished and it also prepares the next one for when the current one is finished. If the media is a video this is called from the onComplete function which works fine. But if it is an image I have a wait loop.
What I want to do is have something like the MediaPlayer.onComplete() function to be called when the image has been displayed for the desired amount of time. Does anything like this exist?
I have since changed this to use handlers because of some other error checking I needed to do. However the other way does work
mHandler.postdelayed(run, delay);
Runnable run = new Runnable(){
public void run()
{
//run function
}
};
I figured it out. I made a new thread that ran a timer and calls a function in the main activity that updates the screen. The call to the function that updates the screen is called using runOnUiThread() so that it has permission to change the screen.
public class ImageTimer implements Runnable{
private long dur, cur,start;
private boolean needed, run;
private Activity mAct;
ImageTimer(boolean running, long duration, Activity act)
{
run = running;
dur = duration;
needed = false;
mAct = act;
}
#Override
public void run()
{
while(run)
{
if(needed)
{
start = System.currentTimeMillis();
cur = System.currentTimeMillis() - start;
while(cur < dur)
{
cur = System.currentTimeMillis() - start;
}
needed = false ;
mAct.runOnUiThread(new Runnable()
{
public void run()
{
MainActivity.ImageDone();
}
});
}
}
}
I have created a custom control panel for a video player. Now I want to give a effect like default MediaController where the panel becomes visible when the screen is touched and it becomes invisible again after the last touch time. I can use this type of code for that.
Thread thread = new Thread() {
#Override
public void run() {
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
}
runOnUiThread(new Runnable() {
#Override
public void run() {
// make the panel invisible
}
});
}
};
I can start the thread when the screen is touched and make it invisible after 60 seconds. But in my case, if the user touches the screen again in between this 60 seconds, the panel should vanish after 60 seconds from the last touch. How to consider this case also?
I would recommend using a combination of Runnables and a Handler. You can do Handler calls using postDelayed() to do something after, say, 60 seconds.
Here's an example:
private Handler mHandler = new Handler();
mHandler.post(showControls); // Call this to show the controls
private Runnable showControls = new Runnable() {
public void run() {
// Code to show controls
mHandler.removeCallbacks(showControls);
mHandler.postDelayed(hideControls, 60000);
}
};
private Runnable hideControls = new Runnable() {
public void run() {
// Code to hide the controls
}
};
Simply delete/cancel current timer.
Btw, you should not do it by Thread, but by posting message to a Handler. Such future timer task doesn't need another thread.
I have an imageView and want it to work like this:
ImageViewer visible
5 second pause
image view invisible
5 second pause
ImageViewer visible
and so on ...
How do I do that? I have tried sleep but it freezes the whole program in 5 seconds. I just want to affect my imageView.
I'm not an Android programmer, but, as a general advice, I'd say you should perform the sleep, better said the waiting, on another thread and execute at the end of the waiting period, on the main thread, a method that toggles the visibility of your imageview.
Getting into more specific detail, I'd say you must use a Handler object because you cannot update most UI objects while in a separate thread. When you send a message to the Handler it will get saved into a queue and get executed by the UI thread as soon as possible:
public class MyActivity extends Activity {
// Handler needed for callbacks to the UI thread
final Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mUpdateUIState = new Runnable() {
public void run() {
updateUIState();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[ . . . ]
}
protected void startToggle() {
// Fire off a thread to do the waiting
Thread t = new Thread() {
public void run() {
Thread.Sleep(5000);
mHandler.post(mUpdateUIState);
}
};
t.start();
}
private void updateUiState() {
// Back in the UI thread -- toggle imageview's visibility
imageview.setVisibility(1 - imageview.getVisibility());
}
}
or, a snippet of a shorter version,
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
imageview.setVisibility(1 - imageview.getVisibility());
}
}, 5000);
using the postDelayed method, that incorporates the delay within the message posting logic.
Use an AlphaAnimation on the ImageView with a 10 second duration going from alpha 100 to 0 and back to 100 again.
Then use a repeact count of INFINITE.
You can use an interpolator to produce a much pleasant effect while the ImageView appears or disappears.