I'm making a custom ImageView . One of the methods is to load an image from a URL. And I want to retrieve the Bitmap in a Thread and load the bitmap in the UI thread.
How can I make a runOnUIThread() call for painting the bitmap?
Is there some kind of built in function? Or should I create a Handler in the constructor and use it for running runnables in the UI thread?
Download the Image via AsyncTask and set to your view in its onPostExecute method
OR
From a separate image downloading thread use the post method of View which will always run its Runnable on UI-thread:
yourImageView.post(new Runnable() {
#Override
public void run() {
// set the downloaded image here
}
});
You can do something like this:
new Thread(new Runnable() {
public void run() {
final Bitmap bm = getBitmapFromURL(myStation.getStation().imageURL);
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
icon.setImageBitmap(bm);
}
});
}
}).start();
This will work outside of an activity, like in a ListAdapter.
Create a class that extends from AsyncTask. Pass the ImageView in the constructor. In the doInBackground method, download the image. In the postExecute method, set the image to the ImageView.
Related
Hi all I am a beginner in android programming and I am having a little trouble trying to understand how HandlerThread works. Specifically, I am not sure if the method in the custom view class is executed in the background thread (or non-UI thread) whenever I call the method in a runnable that is added to the thread's message queue.
I have a custom view and HandlerThread initialized in mainactivity:
HandlerThread mainHandlerThread = new HandlerThread("mainhandler");
mainHandlerThread.start();
Handler mainHandler = new Handler(mainHandlerThread.getLooper());
myCustomView mcv = (MyCustomView) findViewById(R.id.customView);
And in MyCustomView class (which extends view), i have a method called update():
public void update(int number, String txt) {
//perform some calculations
invalidate(); //redraw the view
}
Everytime MainActivity detects a change, it will use mainHandler.post() to call MyCustomView's update method:
mainHandler.post(new Runnable() {
#Override
public void run () {
mcv.update(123,"test")
}
});
Does the above code cause the custom view to be redrawn from the HandlerThread(which is a non-UI thread)? I was able to draw the view using both invalidate() and postInvalidate(), hence I am confused on whether the update() method is running on the UI-thread or on the HandlerThread I created.
I have two activities in my Android app.
1) A list which shows items
If the user clicks on one item it opens activity 2
2) Shows a big image of the item
Now, when I click on an item and thus start the second activity there are a few seconds which the app needs to start the activity. (I suspect because of the image it needs to load). This completely destroys the flow of the use case.
What is the best practice to deal with this?
Loading Bitmaps on Main UiThread takes time so you have to load your bitmaps off the Ui Thread. Use the Universal Image Loader for loading/caching the bitmaps. See the link https://github.com/nostra13/Android-Universal-Image-Loader
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
// Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view
// which implements ImageAware interface)
imageLoader.displayImage(imageUri, imageView);
Hope this helps.
In Second Activity use new Thread.. like below
new Thread(new Runnable() {
#Override
public void run() {
//here put your code
runOnUiThread(new Runnable() {
#Override
public void run() {
//here set your image.
}
});
}
}).start();
I have an AsyncTask that gets a URL as a parameter, and returns a Bitmap of an image from that URL when it finishes. When I start the activity, I want to show a text (instead of the image) saying "loading...", run the ImageFetcher in the background, and when it finishes, set the image to the ImageView.
The problem is, if I run:
ivPoster.setImageBitmap(imgFetcher.execute(m.getPosterUrl()).get());
It doesn't load the entire activity until the image is loaded. It skips frames.
So I set an on finish listener on the task, that returns the bitmap, and tried to set the image after the bitmap is retrieved:
imgFetcher.setOnFinishListener(new OnTaskFinished() {
#Override
public void onFinish(Bitmap bm) {
// hide the loading image text
tvLoading.setVisibility(View.INVISIBLE);
// place the image
ivPoster.setImageBitmap(bm);
}
});
imgFetcher.execute(m.getPosterUrl());
Yet it still skips frames and 'does too much work on the main thread'.
How can I prevent the frame skipping?
You can instead utilize the UI thread in AsyncTask. If you run the code in that thread to load the image, the rest of the program will not have to wait.
Use this documentation for more insight. You can use either the onPostExecute() method or onProgressUpdate() method instead of causing wait in the background thread.
The main thread is the same as the UI thread. You should be using that to make changes in your UI. That is so that your application does not wait for graphics to load before executing background code, instead doing them simultaneously. In order to use the ImageView from AsyncTask, you can make the ImageView global if the AsyncTask is a subclass and instantiate before execution. Otherwise, pass it as a parameter to the AsyncTask.
private class ImageDownloaderTask extends AsyncTask<String, Void, Bitmap> {
ImageView mImageView;
String mUrl;
public ImageDownloaderTask(ImageView imageview, String url) {
this.mImageView = imageview;
this.mUrl = url;
}
#Override
protected Bitmap doInBackground(String... params) {
//Download here
}
#Override
// Once the image is downloaded, associate it to the imageView
protected void onPostExecute(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}
Hope the code snippet helps
You can think about Picasso lib: http://square.github.io/picasso/
or
Universal Image Loader: https://github.com/nostra13/Android-Universal-Image-Loader
I need execute a method when the fragment is visible (to the user).
Example:
I have 2 buttons (button 1 and button 2) ,
2 fragments(fragment 1 and fragment 2)
and the method loadImages() inside the class fragment 2.
when I press "button2" I want to replace fragment 1 by fragment 2
and then after the fragment 2 is visible (to the user) call loadImages().
I tried to use onResume() in the fragment class but it calls the method before the fragment is visible and it makes some delay to the transition.
I tried setUserVisibleHint() too and did not work.
A good example is the Instagram app. when you click on profile it loads the profile activity first and then import all the images.
I hope someone can help me. I will appreciate your help so much. Thank you.
Use the ViewTreeObserver callbacks:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
final View view = v;
// Add a callback to be invoked when the view is drawn
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Immediately detach the listener so it only is called once
view.getViewTreeObserver().removeOnDrawListener(this);
// You're visible! Do your stuff.
loadImages();
}
});
}
I'm a little confused by what you are trying to do. It sounds like the images are loading too fast for you... so does that mean that you have the images ready to display? And that is a bad thing?
My guess (and this is just a guess) is that Instagram does not have the profile pictures in memory, so they have to make an API call to retrieve them, which is why they show up on a delay. If the same is the case for you, consider starting an AsyncTask in the onResume method of the fragment. Do whatever loading you need to do for the images in the background, and then make the images appear in the onPostExecute callback on the main thread. Make sure you only start the task if the images are not already loaded.
However, if you already have the images loaded in memory, and you just want a delay before they appear to the user, then you can do a postDelayed method on Handler. Something like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
Edit
As kcoppock points out, the handler code is pretty bad. I meant it to be a quick example, but it is so wrong I should not have included it in the first place. A more complete answer would be:
private Handler handler;
public void onResume(){
super.onResume();
if(handler == null){
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
}
}
public void onDestroyView(){
super.onDestroyView();
handler.removeCallbacksAndMessages(null);
handler = null;
}
Use the onActivityCreated() callBck
I have a button that I don't want to be clickable until a certain amount of time has run (say, 5 seconds?) I tried creating a thread like this
continueButtonThread = new Thread()
{
#Override
public void run()
{
try {
synchronized(this){
wait(5000);
}
}
catch(InterruptedException ex){
}
continueButton.setVisibility(0);
}
};
continueButtonThread.start();
But I can't modify the setVisibility property of the button within a different thread. This is the error from the LogCat:
10-02 14:35:05.908: ERROR/AndroidRuntime(14400): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Any other way to get around this?
The problem is that you can touch views of your activity only in UI thread. you can do it by using runOnUiThread function. I would like to suggest you to use
handler.postDelayed(runnable, 5000)`
You must update your view from UI-thread. What you are doing is you are updating from non-ui-thread.
Use
contextrunOnUiThread(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
}});
or use handler and signalize hand.sendMessage(msg) when you think is right time to update the view visibility
Handler hand = new Handler()
{
#Override
public void handleMessage(Message msg) {
/// here change the visibility
super.handleMessage(msg);
}
};
You can use the postDelayed method from the View class (A Button is a child of View)
here is simple answer i found
Button button = (Button)findViewBYId(R.id.button);
button .setVisibility(View.INVISIBLE);
button .postDelayed(new Runnable() {
public void run() {
button .setVisibility(View.VISIBLE);
}
}, 7000);