I have a splashscreen which lasts 5 seconds, and I want to represent the progress using a ProgressBar.
public class SplashActivity extends Activity {
private static final long SPLASHTIME = 5000;
private ProgressBar progressBar;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.splash);
SplashHandler handlerSplash = new SplashHandler();
progressBar = (ProgressBar) findViewById(R.id.progression);
progressBar.setMax((int) ((SPLASHTIME) / 1000));
progressBar.setProgress(0);
Message msg = new Message();
msg.what = 0;
handlerSplash.sendMessageDelayed(msg, SPLASHTIME);
ThreadProgressBar threadProgress = new ThreadProgressBar();
threadProgress.start();
}
private class ThreadProgressBar extends Thread {
ProgressBarHandler handlerProgress = new ProgressBarHandler();
public void run() {
try {
while (progressBar.getProgress() <= progressBar.getMax()) {
Thread.sleep(1000);
handlerProgress.sendMessage(handlerProgress.obtainMessage());
}
} catch (java.lang.InterruptedException e) {
}
}
}
private class ProgressBarHandler extends Handler {
public void handleMessage(Message msg) {
progressBar
.incrementProgressBy((int) (SPLASHTIME / SPLASHTIME));
}
}
private class SplashHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
default:
case 0:
super.handleMessage(msg);
// new ProgressBarIncrease().execute();
Intent intent = new Intent();
intent.setClass(SplashActivity.this, RdvTab.class);
startActivity(intent);
}
}
}
I guess since SplashActivity itself is an activity, it is ok to block UI right? AsyncTask sometimes run not gradually, if you want the update to be displayed smoothly, how about changing to worker Thread and Handlder.
Create Handler inside SplashActivity
In the main Thread, display progressBar or splash or whatever you like
While make another Thread to run the countdown, once in every second sendEmptyMessage() to Handler. After 5 seconds, send a message to Handler to dismiss dialog and splash then the worker Thread ends.
UPDATED: Sorry for the slow feedback. How about this? I haven't tested it yet and it's not the best implementation though, you can use this logic
public class SplashActivity extends Activity {
private static final int SPLASHTIME = 5000;
// u can change the value here for smoother effect
private static final long UPDATEINTERVAL = 1000;
private ProgressBar progressBar;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.splash);
progressBar = (ProgressBar) findViewById(R.id.progression);
new Thread(new Runnable(){
#Override
public void run(){
int progress = 0;
while(true){
// send current progress to handler
mHandler.sendEmptyMessage(progress);
// break from the loop after SPLASHSCREEN millis
if(progress > SPLASHSCREEN)
break;
// increase the progress
progress = (int) (progress + UPDATEINTERVAL);
// sleep the worker thread
Thread.sleep(UPDATEINTERVAL);
}
}
}).start();
}
private Handler mHandler = new Handler(){
#Override
public void handleMessage(Message message){
// get the current progress
int progress = message.what;
if(progress <= SPLASHTIME){
progressBar.setProgress(progress);
}
else{
// finish splashActivity here & do what u want do to after splashscreen, for example
finish();
Intent intent = new Intent(SpashActivity.this, MenuActivity.class);
startActivity(intent);
}
}
}
}
You should divide the splash screen time into equal amounts and send the progress update at those intervals.
For example:Lets say the total time is divided into 50 equal intervals as follows
protected void onPreExecute() {
progressbarStep = progressbarWidth/50;
timeStep = 5000/50;
progress = 0;
}
#Override
protected Void doInBackground(Void... params) {
try {
while(progress < progressbarWidth){
progress += progressbarStep;
publishProgress(progress);
SystemClock.sleep(timeStep);
}
return null;
} catch (Exception e) {
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
progressBar.setProgress(values[0]);
}
Related
I want to make a simple button which will start to loop a function every period of time which I can set. But not only it will start the loop, but also stop the loop if I click the button again. Is there anyway I can achieve this with a single button?
Here's how I'd do it
public class MainActivity extends AppCompatActivity {
private Button btn;
private View.OnClickListener runOnClickListener;
private View.OnClickListener stopOnClickListener;
void init() {
Handler handler = new Handler();
int duration = 5000;
Runnable runnable = new Runnable() {
#Override
public void run() {
foo();
handler.postDelayed(this, duration);
}
};
runOnClickListener = view -> {
runnable.run();
btn.setOnClickListener(stopOnClickListener);
};
stopOnClickListener = view -> {
handler.removeCallbacks(runnable);
btn.setOnClickListener(runOnClickListener);
};
btn.setOnClickListener(runOnClickListener);
}
void foo() {
Log.i("foo", "foo");
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
init();
}
}
Yeah, give you a simple example.
First, create two constant values and one instance variable:
//indicate whether or not the loop is running
private boolean isRunning = false;
//used for handler to send empty msg
private final static int MSG_LOOP = 1;
private final static long LOOP_INTERVAL = 5000;
Then create a Handler instance to handle the loop logic:
Handler handler = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_LOOP:
doStuff();
break;
}
}
};
private void doStuff() {
//after what you want to do is done, send another MSG_LOOP msg with delay
handler.sendEmptyMessageDelayed(MSG_LOOP, LOOP_INTERVAL);
}
And finally:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (isRunning) {
//cancel if any in the message queue
handler.removeMessages(MSG_LOOP);
} else {
//if you do not want to start the loop immediately, then use: "sendEmptyMessageDelayed"
handler.sendEmptyMessage(MSG_LOOP);
}
}
});
Can anyone explain when I put the thread to sleep for 30 milliseconds it updates the UI from the background thread. but when I put the sleep for 300 milliseconds it crashes saying that you cannot touch view from another thread. I expect it to crash in both cases but somehow it is working in 30 milliseconds. Needs some clarification on this issue.
public class HandlerDemo extends Activity implements Handler.Callback{
private static final String TAG = "FFFF";
private Handler mHandler = null;
private Handler mUIHandler = null;
private HandlerThread backgroundThread = null;
public static final int BACKGROUND_OPERATION = 10;
public static final int MAIN_THREAD_OPERATION = 20;
private TextView asd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HandlerThread backgroundThread = new HandlerThread("BACKGROUND_THREAD");
backgroundThread.start();
mHandler = new Handler(backgroundThread.getLooper(), this);
mHandler.sendEmptyMessage(BACKGROUND_OPERATION);
// mUIHandler = new Handler(Looper.getMainLooper(), this);
// mUIHandler.sendEmptyMessage(MAIN_THREAD_OPERATION);
}
#Override
protected void onDestroy() {
super.onDestroy();
mHandler.getLooper().quit();
}
#Override
public boolean handleMessage(Message message) {
Log.d(TAG, Thread.currentThread() + "");
switch (message.what) {
case MAIN_THREAD_OPERATION:
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
((TextView) findViewById(R.id.textview)).setText("Updating from UI Handler");
findViewById(R.id.textview).invalidate();
break;
case BACKGROUND_OPERATION:
try {
**Thread.sleep(300);**
} catch (InterruptedException e) {
e.printStackTrace();
}
((TextView) findViewById(R.id.textview)).setText("Updating from background Handler");
findViewById(R.id.textview).invalidate();
break;
default:
break;
}
// message.recycle();
return true;
}
}
runOnUiThread(new Runnable() {
#Override
public void run() {
// do what you want
}
});
I am trying to put a progressDialog in my fragment so my app feels smoother between each actions. The problem that I have is that the main thread is returning the view before the async thread has modified it. I was doing a Thread.join() before switching to this methode.
public class MyFragment extends Fragment {
public View onCreateView(LayoutInflater inflater,ViewGroup container, Bundle args) {
mLLayout = new LinearLayout(getActivity());
mLLayout.setPadding(0, 0, 0, 0);
mLLayout.setOrientation(LinearLayout.VERTICAL);
mScroll = new ScrollView(getActivity().getApplicationContext());
mScroll.setVerticalScrollBarEnabled(true);
mScroll.addView(mLLayout);
new AsyncCaller().execute();
return mScroll;
}
private class AsyncCaller extends AsyncTask<Void, Void, Void>
{
ProgressDialog nDialog = new ProgressDialog(getActivity());
#Override
protected void onPreExecute()
{
super.onPreExecute();
//this method will be running on UI thread
nDialog = new ProgressDialog(getActivity());
nDialog.setMessage("Loading..");
nDialog.setTitle("Checking Network");
nDialog.setIndeterminate(false);
nDialog.setCancelable(true);
nDialog.show();
}
#Override
protected Void doInBackground(Void... params)
{
//Doing http requests and modifying views
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
//this method will be running on UI thread
nDialog.dismiss();
}
}
...
}
I cant find a solution without blocking the main thread and by the same way not seeing the progressDialog at all and having a big lag instead. Also, I never saw my code go into "onPostExecute()".
Thanks in advance for your help!
Striaght from an app i've done, this code should help:
ProgressBar mProgress = (ProgressBar) findViewById(R.id.progressBar);
private float mProgressStatus = 0;
private Handler mHandler = new Handler();
private void startProgressBar()
{
new Thread(new Runnable()
{
#Override
public void run()
{
while (mProgressStatus < 100)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
mProgressStatus += 1.3;
mHandler.post(new Runnable()
{
#Override
public void run()
{
mProgress.setProgress((int) mProgressStatus);
}
});
}
}
}).start();
}
I'm new to android and I want to do a simple example where I click on a start button and another Activity is open, there a simple number starting in one and counting upwards, but I'm facing a problem, after I initialize some variables on onCreate method (In the second activity), where should I actually start the while statement to count and modify the text view?.
I wrote this class:
public class Counter extends Thread{
private TextView tv;
private int i;
public Counter( TextView tv ){
this.tv = tv;
}
#Override
public void run() {
while( true ){
tv.setText(i);
try {
sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
i++;
}
}
And started the thread over here:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
counter = new Counter( (TextView) findViewById(R.id.textView2) );
counter.start( );
}
#SuppressLint("UseValueOf")
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
// change your text here
if (condition) {
i++;
txt_TimeRecord.setText("" + i);
} else {
i = 0;
}
handler.postDelayed(this, 1 * 1000L);
}
});
[Formatted the code properly]
//In first activity
//set onclick method to that button.
public void onclick(view v)
{
Intent intent = new Intent(this, secondactivity.class);
startActivity(intent);
}
// in second activity in
// initi i value.
while(i<10)
{
i++ ;
// sleep statement
//print that i in textview
}
I've found what I need from a similar post, the handler was needed since only the UI Thread can update the user interface.
private void countNumbers() {
final TextView numbers = (TextView) findViewById(R.id.textView2);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
int i = 0;
while (i++ < 500) {
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
e.printStackTrace();
}
final int j = i;
handler.post(new Runnable(){
public void run() {
numbers.setText(Integer.toString(j));
}
});
}
}
};
new Thread(runnable).start();
}
The method countNumbers() is called in onCreate().
Solution:
Okay,I've found the solution from your answers since your answers are not completely working
First we need to use Handler to post a Runnable to main/UI thread to run UpdateDisplay() and but define ProgressDialog under UIThread not other threads which was also crucial.
Here is the final code :
public final String RSSFEEDOFCHOICE = "http://www.deals2buy.com/rssgen/tech.xml";
public final String tag = "RSSReader";
private RSSFeed feed = null;
private Handler handler = new Handler();
private ProgressDialog dialog;
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
dialog = ProgressDialog.show(RSSReader.this, "Loading",
"Loading, please wait..");
Thread t = new Thread() {
public void run() {
feed = getFeed(RSSFEEDOFCHOICE);
handler.post(new Runnable() {
public void run() {
dialog.dismiss();
UpdateDisplay();
};
});
}
};
t.start();
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Problem :
When Android application first load, this is what happens :
Retrieving RSS content from Url and parse it into RSSFeed feed instance variable via getFeed(String url).
Showing these feeds to users in ListView via UpdateDisplay() method.
When these are happening, I wanted to show a ProgressDialog to users to notify them about system are retriving content like "Loading, please wait..." Once getFeed(String Url):RSSFeed is done, ProgressDialog will be dismissed and UpdateDisplay():void will be invoked.
So this is my first attempt :
public final String RSSFEEDOFCHOICE = "somewhere.xml"; //coming from http
public final String tag = "RSSReader";
private RSSFeed feed = null;
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
ProgressDialog dialog = ProgressDialog.show(RSSReader.this, "Loading...", "Loading, please wait");
feed = getFeed(RSSFEEDOFCHOICE);
dialog.dismiss();
UpdateDisplay();
}
And it doesn't show the ProgressDialog so it didn't work the way I want. Since I belived it is because ProgressDialog is continues progress, it should be handled by another Thread.
Here is my second attempt :
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Thread t = new Thread()
{
public void run()
{
ProgressDialog dialog = ProgressDialog.show(RSSReader.this, "Loading...", "Loading, please wait");
feed = getFeed(RSSFEEDOFCHOICE);
dialog.dismiss();
}
};
UpdateDisplay();
}
It was okay till UpdateDisplay() since this method was handled by main thread and it wasn't waiting anything so I thought maybe I should have put it under t Thread after dialog.dismiss().
So I did this :
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Thread t = new Thread()
{
public void run()
{
ProgressDialog dialog = ProgressDialog.show(RSSReader.this, "Loading...", "Loading, please wait");
feed = getFeed(RSSFEEDOFCHOICE);
dialog.dismiss();
UpdateDisplay();
}
};
}
But then debugger said at run-time : only the main thread which handles onCreate():void can reach and use View objects because it creates them.
Now, I am so messed up and feel like quitting programming.
Some one can help me :/
Thanks in advance.
You could use AsyncTask, thats exactly what its for. Since onPostExecute(Long result) is done on the UI thread, it wont crash when looking for View objects.
Here is a example taken from the asyncTask reference
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
What you want to do is obtain the RSS feed in doInBackground(), and then update the UI in onPostExecute.
In your onCreate() method, invoke the asyncTask.
new DownloadFilesTask().execute(url1, url2, url3);
Okay,I've found the solution from your answers since your answers are not completely working
First we need to use Handler to post a Runnable to main/UI thread to run UpdateDisplay() and but define ProgressDialog under UIThread not other threads which was also crucial.
Here is the final code :
public final String RSSFEEDOFCHOICE = "http://www.deals2buy.com/rssgen/tech.xml";
public final String tag = "RSSReader";
private RSSFeed feed = null;
private Handler handler = new Handler();
private ProgressDialog dialog;
/** Called when the activity is first created. */
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
dialog = ProgressDialog.show(RSSReader.this, "Loading",
"Loading, please wait..");
Thread t = new Thread() {
public void run() {
feed = getFeed(RSSFEEDOFCHOICE);
handler.post(new Runnable() {
public void run() {
dialog.dismiss();
UpdateDisplay();
};
});
}
};
t.start();
}
You have to run your request in another thread than the progressdialog. So try with
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
ProgressDialog dialog = ProgressDialog.show(RSSReader.this, "Loading...", "Loading, please wait");
Thread t = new Thread()
{
public void run()
{
feed = getFeed(RSSFEEDOFCHOICE);
dialog.dismiss();
UpdateDisplay();
}
};
}
And depending on what you do in update display you should run it in the UI thread.
Actually the best way would be to use AsyncTask.
However as a quick workaround you could do smth like this:
private Handler handler = new Handler();
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
Thread t = new Thread()
{
public void run()
{
ProgressDialog dialog = ProgressDialog.show(RSSReader.this, "Loading...", "Loading, please wait");
feed = getFeed(RSSFEEDOFCHOICE);
dialog.dismiss();
handler.post(new Runnable() {
public void run() {
UpdateDisplay();
}
});
}
};
}