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();
Related
I am creating a file manager and I'd like to create thumbnails for videos/pictures on device. But when I scroll items too fast, the performance falls and sometimes my app just crashes because too many thumbnails are being created at one time
The main BIND part of my ViewHolder:
new Thread(new Runnable() {
#Override
public void run() {
final Bitmap[] btm = {null};
if(mimeType.startsWith("video/") || mimeType.startsWith("image/")){
if(mimeType.startsWith("video/")){
btm[0] = ThumbnailsHelper.createThumbForVideo(file.getAbsolutePath());
}else if(mimeType.startsWith("image/")){
btm[0] = ThumbnailsHelper.createThumbForPic(file.getAbsolutePath());
}
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mFileIcon.setImageBitmap(btm[0]);
}
});
}
}
}).start();
My RecyclerView settings:
mRecyclerView.setItemViewCacheSize(20);
mRecyclerView.setDrawingCacheEnabled(true);
mRecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
In apps like ZArchiver or similar I saw that thumbnails are shown only when user isn't scrolling RecyclerView items. So, how can I improve the situation and maybe anybody knows how to make it like in ZArchiver?
The thing is when you scroll your recycler it create a lot of new threads which try to load your thumbnails.
You should:
Use RxJava for thumbnail loading task to have the possibility to dispose this loading when view was scrolled and already not visible (recycled).
When your view is recycled (onViewRecycled() method. See: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter) you should dispose this network request.
Another workaround is to create thumbnails as gif images for every video, store it on server and then show it with Glide (see: Show GIF file with Glide (image loading and caching library) and https://biteable.com/blog/tips/make-gif-video/)
I can't tell about the efficiency or [the right] method of doing what you are doing, but since you asked you wanted to know how to make thumbnail when user is not scrolling, I will tell you just that!
The RecyclerView can be attached with a scroll listener, and you can do what you want inside its methods.
Let me show you :
myRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//Recyclerview isnt' scrolling!
//do the work
makeThumbnail();
}
if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
//Recyclerview scrolling is slowing down and about to stop
makeThumbnail(); //optional
}
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
//RecyclerView scrolling is started
stopPendingThumbnailMakingProcess(); //in case that's what you want to do!
}
}
});
Hope it helps !
Cheers!
I am trying to make a app where there are two activity..
First activity have all images loaded using Picasso in a RecyclerView,When clicked it will take to second activity, which will enlarge the clicked picture. Again there is a another RecyclerView which will show other random images to pick from...
I just want to know how to load the second activity only after every photos are loaded in the first activity,else the screen looks blank and ugly.
You can using Picasso loading callback
Picasso.with(YourActivity.this)
.load(imageUrl)
.into(imageView, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
// can click to open second activity
}
#Override
public void onError() {
//do smth when there is picture loading error
}
});
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'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.