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!
Related
I am making an app that has a list of two items that have two gifs, one in each (the same gif). As you can see in the video, when you click on the gif, the gif is not shown in motion and it is shown as a solid image, but when you click the right or left arrow it is shown correctly in motion despite that the function to load the gif is called in both the OnCreate function and the btn_next.setOnClickListener() function and the btn_prev.setOnClickListener() function. So the same thing is done but onCreate() the gif is not shown in motion and on btn_next and btn_prev setOnClickListener() it is shown. Please, someone could tell me what could be happening here? Bcause actually the same code is executed but on the first clikc is not working
Video: https://youtube.com/shorts/S5fDsqu3CeI?feature=share
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialog);
init();
glide();
}
private void init() {
img_1 = findViewById(R.id.img_image1);
btn_prev = findViewById(R.id.dialog_btn_prev);
btn_next = findViewById(R.id.dialog_btn_next);
btn_next.setOnClickListener(view -> {
glide();
});
btn_prev.setOnClickListener(view -> {
glide();
}
});
}
public void glide() {
Glide.with(getApplicationContext()).load(R.drawable.earth).into(img_1);
}
Change your code when you are initialising Glide with this.
Glide.with(this).asGif().load(R.raw. earth).into(img_1);
Try this!
Glide.with(this)
.asGif()
.load(R.raw._img)
.centerCrop()
.into(new ImageViewTarget<GifDrawable>(img) {
#Override
protected void setResource(#Nullable GifDrawable resource) {
img.setImageDrawable(resource);
}
});
Add dependencies build.gradle(App Level)
implementation 'com.github.bumptech.glide:glide:4.13.2'
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 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'm trying to programmatically click on an item of a recyclerView. I'm using:
recyclerView.findViewHolderForAdapterPosition(index).itemView.performClick();
This perfectly works when the index is of a visible item. If the item is not visible (at the last position of the recyclerview, for istance), an Exception is thrown.
What can I do?
I just had a similar question with yours.And I had solve it! Here is what I did.
xxx.mRecyclerView.postDelayed(new Runnable() {
#Override
public void run() {
xx.mRecyclerView.scrollToPosition(position);
}
},300);
xxx.mRecyclerView.postDelayed(new Runnable() {
#Override
public void run() {
xxx.mRecyclerView.findViewHolderForAdapterPosition(position).itemView.performClick();
}
},400);
}
You can scroll to the specific item, then perform click.
Because the doc say
If the item at the given position is not laid out, it will not create a new one.
But I know the adapter has the data, so scroll to it first, and findViewHolderForAdapterPositionwill not be null.
One more thing, I do not know how you use the RecyclerView. In my application, I use it in a fragment, and I don not know why we should delay it scroll and perform click. (Maybe it is because of the life circle?).But this really works.
You could call onClick directly, assuming that view manages its own click listener.
View view = recyclerView.findViewHolderForAdapterPosition(index).itemView;
view.onClick(view);
If the click listener is located somewhere else, you just need to get a reference to the object with the onClick method and call it with the correct view as a parameter.
try this for kotlin and viewBinding
viewBinding.catList.post {
val view = viewBinding.catList.findViewHolderForAdapterPosition(0)?.itemView?.findViewById<CheckBox>(R.id.catButton)
view?.callOnClick()
}
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