I have 2 buttons as well as a progressbar.
Now I want to count from 0 - 100 and update the progressbar with the counter. Unfortunately this count is so fast, that I will not see a progress (I can only see the progressbar jump from 0 to 100 within a 'millisecond'.).
This is what I tried so far, however the same thing happens as before.
I click the button to start the counter, the app becomes unresponsive (I calculate within the mainThread, so that is OKAY (and exactly what I want to accomplish here(!))) and shortly after sets the progressbar to 100. Nothing in between.
Now if you could head me into the correct direction, that'd be highly appreciated! :)
public class MainActivity extends ActionBarActivity {
public String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addButtonListeners();
}
public void addButtonListeners() {
Button firstButton, secondButton;
final ProgressBar pgBar;
firstButton = (Button) findViewById(R.id.first_button);
secondButton = (Button) findViewById(R.id.second_button);
pgBar = (ProgressBar) findViewById(R.id.progressBar);
firstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "First Button was pressed.");
for (int i = 0; i <= 100; i++) {
countProgBarUp(pgBar, i);
}
}
});
secondButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Second Button was pressed.");
}
});
}
public void countProgBarUp(ProgressBar pgBar, int counter) {
try {
Thread.sleep(100);
pgBar.setProgress(counter);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
...
}
The UI becomes unresponsive because you are doing this on the main thread and sleeping. If you do not return from the onClick() then the main thread cannot handle UI changes, such as displaying your new progress. Only the main thread can update UI components and draw them. It is often called the UI thread because of this.
You could try creating a Runnable which updates the progress bar to the new count. Create a Handler which is bound to the UI thread and call postDelayed() for the Handler and give it an instance of the Runnable. Then in the Runnable just post it again as long as you haven't reached you max progress.
Related
I have the following code, which I was hoping would count up to 3 million on the screen.
It compiles and runs, displaying 3,000,000 at the end on the emulator. My question is how do I force a redraw / display of the textbox during the loop please?
/** Called when the activity has become visible. */
#Override
protected void onResume() {
super.onResume();
Log.d(msg, "The onResume() event");
TextView textbox1=(TextView)findViewById(R.id.TextView1);
for(double l=0; l<=3000000; l++){
textbox1.setText("" + l);
}
}
The view is only disabled after onResume has finished running.
You may want to set the text in the textview, update some state in the activity (like an int field), and register some code to run after a while and increment. Look at using Handler, AsyncTask, or other options to defer code.
Here's a quick-and-dirty example with Handler.
final long DELAY_MILLIS = 50;
final Handler handler = new Handler();
int num = 0;
final Runnable runnable = new Runnable() {
public void run() {
if (num >= 3000000) return;
textbox1.setText("" + num);
num++;
// re-register ourself to run in DELAY_MILLIS;
handler.postDelayed(runnable, DELAY_MILLIS);
}
};
TextView textbox1;
protected void onResume() {
// more efficient to look this up once
this.textbox1 = (TextView)findViewById(R.id.TextView1);
runnable.run(); // will register itself to re-run
}
According to this answer the best way to show an incremental value in a TextView is to use ValueAnimator
public void animateTextView(int initialValue, int finalValue, final TextView textview) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(initialValue, finalValue);
valueAnimator.setDuration(1500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
textview.setText(valueAnimator.getAnimatedValue().toString());
}
});
valueAnimator.start();
}
I have a button in my activity. When I press it, following method "ProcessNumbers()" is called. I need the functionality that "Processing..." text should be displayed in the textview first, then the loop should get processed, and when the loop finishes after 2-3 seconds, the textview should show "Processed" text. But this is not happening. When I click the button, loop runs first and after 2-3 seconds, the textview contains "Processed". Why the first step is missing where textview should contain "Processing..."?
private void ProcessNumbers()
{
TextView textViewProcessNumbers = (TextView) findViewById(R.id.textViewProcessNumbers);
textViewProcessNumbers.setText("Processing...");
int k = 0;
for (long i = 0; i <= 1000000000; i++)
{
k = 1;
}
textViewProcessNumbers.setText("Processed...");
}
Am I missing something here?
private void ProcessNumbers()
{
TextView textViewProcessNumbers = (TextView) findViewById(R.id.textViewProcessNumbers);
textViewProcessNumbers.setText("Processing...");
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 30000ms
textViewProcessNumbers.setText("Processed...");
}
}, 30000);
}
Use handler for delay. Hope it helps
You need to move that process to a background thread e.g. using an AsyncTask since textview won't get updated until you leave the main thread.
Executing long running operations on the UI thread is also not recommended and it might cause an Application Not Responding dialog to appear.
So
1. Set the label to Processing...
2. Start the AsyncTask with your process
2. When AsyncTask finishes update the label in onPostExecute
You can use CountDownTimer, after find text view add this code.
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
textViewProcessNumbers.setText("Processing...");
}
public void onFinish() {
textViewProcessNumbers.setText("Processed...");
}
}.start();
CountDownTimer(long millisInFuture, long countDownInterval)
I have spent almost the day trying to figure it out and yet could not manage to achieve it. I basically have two activities, MainActivity.java and GameActivity.java. The music stops but after going back to MainActivity.java and coming back to GameActivity.java using ImageView several times.
MainActivity.java
public class MainActivity extends AppCompatActivity {
ImageView iv;
public static final String BG_SOUND_CHECK = "background playing!";
// public static MediaPlayer backgroundSound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Handler handler = new Handler();
final MediaPlayer backgroundSound = MediaPlayer.create(this, R.raw.background_music);
handler.postDelayed(new Runnable() {
#Override
public void run() {
backgroundSound.setLooping(true);
backgroundSound.start();
Log.v(BG_SOUND_CHECK, "After loop Started!");
}
}, 3000);
getSupportActionBar().hide();
iv = (ImageView)findViewById(R.id.playImage);
iv.setOnClickListener(new View.OnClickListener(){
public void onClick(View view1){
// backgroundSound.setLooping(false);
backgroundSound.pause();
// Log.v(BG_SOUND_CHECK, "Playing After Play Button Click");
Intent myIntent = new Intent(MainActivity.this, GameActivity.class);
// myIntent.putExtra("backgroundSoundObj",backgroundSound);
startActivity(myIntent);
// Log.v(BG_SOUND_CHECK, "After Game Scene");
}
});
}
}
GameActivity.java
public class GameActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
// MainActivity.backgroundSound.pause();
// Rest of the code
}
}
I tried putting the music onPause(); in
1) ImageView onClick();
2) As soon as the GameActivity.java is loaded
but both ways end up pausing it only after I try coming back to MainActivity.java and going to GameActivity.java several times.
Do you see how you put your backgroundSound.start() inside the postDelayed-3000 ?
backgroundSound.pause() anytime before that 3000ms is not going to do anything.
It has nothing to do with going back and forth between your two activities, it just so happens that doing that takes up more than 3000ms, so when you do click for pause(), 3000ms has passed.
What you can do, to fit your current code, is to separate that into a Runnable.
Runnable bgSound = new Runnable() {
#Override
public void run() {
backgroundSound.setLooping(true);
backgroundSound.start();
Log.v(BG_SOUND_CHECK, "After loop Started!");
}
}
Use it with handler.postDelayed(bgSound, 3000)
In your onClick, if you want to stop it from playing before it starts, can use handler.removeCallbacks(bgSound), to stop it after it plays, your pause(). If you don't care to know if it has or hasn't started, you can do both alongside each other, should work fine too.
(if you don't want the music to play after you've left your main activity, you should also do the same actions in your onDestroy and/or onPause)
i have a button with onclicklistener that download a picture from internet and update progress-bar in UI thread . when users click on the button for first time , it work correctly , but if the users click on the button for seconds &... when download is not completed , a duplicate process happens .how could i get rid of this problem?
Button btnDownload = (Button) findViewById(R.id.btndownload);
final TextView txtcaption = (TextView) findViewById(R.id.txtcaption);
final ProgressBar progress = (ProgressBar) findViewById(R.id.progress);
btnDownload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
new Thread(new Runnable() {
OnProgressDownloadListener listener = new OnProgressDownloadListener() {
#Override
public void progressDownload(final int percent) {
new HANDLER.post(new Runnable() {
#Override
public void run() {
progress.setProgress(percent);
txtcaption.setText(percent + " %");
if (percent >= 100) {
txtcaption.setText("completed");
Toast.makeText(activity.this, "download completed", Toast.LENGTH_SHORT).show();
}
}
});
}
};
#Override
public void run() {
//my download manager
FileDownloader.download("address/file", DIR + "/file");
}
}).start();
}
});
}
An easy way to do this would be the following...
First, begin by declaring a thread...
Thread myThread
Then create a simple method that contains the thread you wish to execute when the button is pressed...
private void getPicture()
{
myThread = new Thread()
{
public void run()
{
// Place thread code here...
}
};
myThread.start();
}
Then you can do a simple check when the button is pressed and, if the thread is active, don't call the getPicture method...buttonDownload.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
if (myThread.isAlive())
{
// Thread is alive, do not launch again
}
else
{
// Thread is not running so call method...
getPicture();
}
}
});
Have a Thread variable in your class that's initialized to NULL. In your onClickListener, check the value of that variable. If its null, start a new thread and save the value of that thread in the variable. If it isn't, ignore the button press or pop up a downloading toast. Remember to set the variable back to null when your thread is completed.
I'd highly recommend using an AsyncTask for this rather than a thread, it will be cleaner.
I have two simple activities MainActivity and ThreadActivity. I call ThreadActivity from MainActivity.
The code ofMainActivity:
public class MainActivity extends Activity {
private Button btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.btn2);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
startActivity(intent);
}
});
}
}
And the code of ThreadActivity:
public class ThreadActivity extends Activity{
private Thread myThread=null;
Button btn;
int i = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
runThread();
}
});
}
void runThread(){
myThread = new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText("#" + i);
Log.d("Thread", "I am running " + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
}
};
myThread.start();
}
}
When I start ThreadActivity I run a simple thread and change button text.
My Problem
When I loose focus from application, i.e when application becomes partially visible, and I come back I am redirected to ThreadActivity and the thread is still running.
When I leave application running and open a new application, and then come back, I am again redirected to ThreadActivity.
The problem is when I press back button, I am being redirected to first activity MainActivity. But instead when back button is being pressed I want my application to exit. In a few words MainActivity should not exist in the stack.
I tried setting android:noHistory="true" for MainActivity but I could not keep the behavior explained in bullet points working. I mean when I pause the application and restore it back, it redirected me to MainActivity instead of ThreadActivity.
Just call finish() when starting the ThreadActivity:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ThreadActivity.class);
startActivity(intent);
finish();
}
});
BUT there is a problem with your app. Use a Timer to set the text of the Button! By using a Thread like you do you are creating a memory leak and that is very bad. Try this:
private int i = 0;
private Timer timer;
private final TimerTask timerTask = new TimerTask() {
#Override
public void run() {
btn.post(new Runnable() {
#Override
public void run() {
btn.setText("#" + i++);
}
});
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.custom);
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timer = new Timer();
timer.schedule(timerTask, 300, 300);
}
});
}
#Override
public void onPause() {
super.onPause();
if(timer != null) {
timer.cancel();
}
}
From your comments to other peoples' answers, it seems like you want the ThreadActivity to always be resumed instead of the MainActivity when your thread is running.
Do the thread in a Service - the service will mean your application's VM is likely to be kept alive longer. An app with no foreground activities can be killed off quite quickly (even if it has background threads running).
You need to persist that the thread is running, and the progress (if, in the real code that is applicable). Currently you could persist the value of i in your while loop.
Your application's default launcher activity (MainActivity) will launch when you click on it from your launcher. Check if the persisted value has been set, and act as though the user started the ThreadActivity in onCreate, if you finish() in onCreate, the user won't see any UI from the MainActivity
Depending on what you're actually trying to do, you might be able to resume the thread depending on the progress persisted - in this example, you could start from the persisted value of i (instead of 0).
What I had to is set android:noHistory="true" for MainActivity and in the ThreadActivity I had to add the solution mention by #NeTeInStEiN in this quesiotn
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
return true;
}
return super.onKeyDown(keyCode, event);
}