I have a class which generates a pdf file.
This class is extended by Activity that means is have onCreate, onPause (and so on) methods.
While a pdf file is generating it takes some time to be generated so I need to show a progress bar to notify the user that the file is generating at the moment.
Everything works fine expect that when I click on a generate button the progress bar freezes for a few seconds (according to Logcat: progress bar freezing while file is generating).
So my problem is that, that I don't know how to make the progress bar unstoppable while the file is generating.
Below is my code:
Log.i(TAG, "PDF generation: " + position);
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setCancelable(true);
dialog.setIndeterminate(true);
dialog.setMessage("Generating...");
dialog.show();
new Thread(new Runnable() {
#Override
public void run() {
startActivity(new Intent(getApplicationContext(),
PdfGenerator.class));
runOnUiThread(new Runnable() {
#Override
public void run() {
dialog.hide();
}
});
}
}).start();
Change your code as for starting an Activity from Thread using runOnUiThread:
///... your code here..
new Thread(new Runnable() {
#Override
public void run() {
Your_Current_Activity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
dialog.cancel();
// START ACTIVITY HERE
Your_Current_Activity.this.startActivity(new
Intent(Your_Current_Activity.this,
PdfGenerator.class));
}
});
}
}).start();
Related
I'm using Picasso to display many small ImageViews in a RecyclerView.
The ImageViews change their Image for 500 milliseconds when I click on them and change it back when the handler with 500 milliseconds end.
This is the code:
Picasso.get().load(imageResources[position]).into(holder1.itemImageView);
holder1.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
final Context context = v.getContext();
Picasso.get().load(pressedResource[position]).into(holder1.itemImageView);
new Handler().postDelayed(new Runnable() {
public void run() {
Picasso.get().load(imageResources[position]).into(holder1.itemImageView);
}
}, 500);
new Thread(new Runnable() {
#Override
public void run() {
EventHandlerClass.startMediaPlayer(v, soundID);
}
}).start();
}
});
I use Picasso to prevent my app from getting OutOfMemory crashes.
The Image Dissapears for like 10 milliseconds, then changes to the other image, after the 500 milliseconds it dissapears again and then it changes back to the default image.
It only dissapears for the first time I click on one of the Images, after that it works without dissapearance.
I think this happens because Picasso is loading the image too slow.
Is there a way to first load the image and display it when I click on the button?
Here is a GIF which shows how it looks like: https://media.giphy.com/media/STlGbpXvT8B9iBvnQS/giphy.gif
In this case it only dissapeared once but sometime it dissapeares twice with one click.
The Images are all 200x200 and around 8kb. The format is .WEBP
How can I prevent this?
The idea is to wait for the Callback and then set the image into the image view. This is achieved by using a CallbackListener.
Picasso.get().load(imageResources[position]).into(holder1.itemImageView);
holder1.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
final Context context = v.getContext();
Picasso.get().load(pressedResource[position]).into(holder1.itemImageViewnew com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
new Handler().postDelayed(new Runnable() {
public void run() {
Picasso.get().load(imageResources[position]).into(holder1.itemImageView);
}
}, 500);
new Thread(new Runnable() {
#Override
public void run() {
EventHandlerClass.startMediaPlayer(v, soundID);
}
}).start();
}
#Override
public void onError() {
}
}));
}
});
I try to make a progressbar on a button click. the progressbar is only visible after everything else is procesed.
MainActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
} });
Can I force the UI thread to join or something like that?
EDIT: Complete onClick code
public void onClick(View view) {
if (view == etLocation){
etLocation.setText("");
}
if (view == btnGo){
//progressBar.setVisibility(View.VISIBLE);
MainActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
} });
Thread buttonpress = new Thread(new Runnable() {
public void run()
{
buttonGo();
}
});
buttonpress.run();
}
if (view == datePicker){
new DatePickerDialog(MainActivity.this, date, myCalendar
.get(Calendar.YEAR), myCalendar.get(Calendar.MONTH),
myCalendar.get(Calendar.DAY_OF_MONTH)).show();
}
}
runOnUiThread is async. Looking through your code, I see an easy fix to it too:
//progressBar.setVisibility(View.VISIBLE);
Thread buttonpress = new Thread(new Runnable() {
public void run()
{
buttonGo();
}
});
//Move runOnUiThread down. The thread above will not start doing its thing until you tell it to run
MainActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
buttonpress.run();//TO HERE. Now the progressbar will be initialized, THEN the thread will start
} });
//buttonpress.run(); MOVE THIS
What I basically did was move runOnUiThread down, so it can access the ButtonPress thread.
Then, after the progressBar is visible, you run the thread.
The UI thread is intentionally asynchronous. The job of the application layer is to tell the UI thread what to do, not when to do it.
If your application logic depends on the UI updating, then you should consider your application logic to be too tightly coupled anyway.
I searched for a solution and couldn't find one so I'll ask here:
I'm trying to use setText command in the mainActivity, Until now I've used:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
textViewPrograss.setText(finalI + "");
}
});
now I'm trying to do the same thing, but from another class so i cant use:MainActivity.this.
I was trying to use code i found on another question with no success,This is the code:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Log.d("UI thread", "I am the UI thread");
}});
Any suggestions?
This (the second code sample from your question) is the correct way to access UI Thread from random location, although you should always try to have a Context to do this :-)
new Handler(Looper.getMainLooper()).post(
new Runnable() {
#Override
public void run() {
Log.d("UI thread", "I am the UI thread");
}});
It does work, and if it does not, check if you have debug logs enabled in your debugger ^^
You can use this snippet
textView.post(new Runnable() {
#Override
public void run() {
textView.setText("Text");
}
});
Try this just pass the context to other class and then use it like this
((Activity)context).runOnUiThread(new Runnable() {
public void run() {
textViewPrograss.setText(finalI + "");
}
});
I suggest you tu use a BroadcastReceiver in the MainActivity. Register a new receiver with a specific action and send an Intent with that action from "another class". The MainActivity will receive the notification and can edit the TextView content in a clean way
MainActivity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// update your text view
String text = intent.getStringExtra("text");
}
};
registerReceiver(mReceiver, new IntentFilter(MY_ACTION));
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
}
Another class:
Intent intent = new Intent(MY_ACTION);
intent.putExtra("text", "Your wonderful text");
// take a context reference (e.g. mContext) if you don't have a getContext() method
getContext().sendBroadcast(intent);
This is my solution (using MVVM, so no business logic in activity), run this in your class (viewmodel in my case):
runOnUiThread(() -> methodToExecute());
here is the method implementation (I have this in my base viewmodel):
private Handler messageHandler;
protected void runOnUiThread(Runnable action) {
messageHandler.post(action);
}
Don't forget to init messageHandler:
messageHandler = new Handler(Looper.getMainLooper());
I am using the Android Studio template called "Navigation Drawer Activity". It comes with an ActionBar activity and 3 preloaded "sections" in the menu.
I am trying to create a "loading" popup when one of the sections is opened. To do this, I swipe the menu open and select section 2.
I can only figure out how to do this using a button and onClick action. I do not want the button, but I want it to auto run when the section is opened then dismissed 3 seconds later. Here is what I have come up with so far:
// get button
Button btnShow = (Button)findViewById(R.id.btn_show);
btnShow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
builder.setTitle("Connecting");
builder.setMessage("Please wait while we connect to your devices...");
builder.setCancelable(true);
final AlertDialog dlg = builder.create();
dlg.show();
Handler mHandler = new Handler();
Runnable mRunnable = new Runnable() {
public void run() {
if (dlg != null && dlg.isShowing()) dlg.dismiss();
}
};
mHandler.postDelayed(mRunnable, 2000);
}
});
U=Try this code
Runnable mRunnable = new Runnable() {
public void run() {
sleep(3000);
if (dlg != null && dlg.isShowing()) dlg.dismiss();
}
};
This seems to be the easiest method. Open and closed after 3000ms.
final ProgressDialog progress = new ProgressDialog(this);
progress.setTitle("Connecting");
progress.setMessage("Please wait while we connect to devices...");
progress.show();
Runnable progressRunnable = new Runnable() {
#Override
public void run() {
progress.cancel();
}
};
Handler pdCanceller = new Handler();
pdCanceller.postDelayed(progressRunnable, 3000);
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.