Having read a lot of questions answering "how to update one single row of ListView or RecyclerView (which would be notifyItemChanged(position)), i have a more "nested" question:
My custom RecyclerView ImageRecycler holds a couple of CardViews. In these cardviews, we can find a ProgressBar and an ImageView.
Logic: I open a file chooser elsewhere, choose an image, starts displaying / fading it in for 2 seconds in the newly added CardView, while i am also uploading it to a server. So during my image animation, i want to be updated for the upload progress as well. For the last said, i use a LocalBroadcastReciever calling notifyItemChanged(position).
To load the ImageView, i use Glide for asynchronous image loading in my adapter:
File f = new File(entries.get(position).getFilePath());
Glide
.with(main)
.load(f)
.diskCacheStrategy(DiskCacheStrategy.RESULT)
.crossFade(2000)
.into(holder.iv1);
Which leads to my problem:
Image is uploading, notify-method called a few times, which uploads the ProgressBar (yay!), but also makes Glide reloading the image again and again, so the screen starts flickering.
I already tried to animate the image only for the first time, after that, i only call my view holder to just set the already loaded image:
holder.iv1.setImageBitmap(entries.get(position).getBitmap());
But this makes the scrolling of my RecyclerView very faltering.
So how can i refresh only the ProgressBar as a part of CardView, but allowing the "old" image to stay?
Related
I wanted to create a recycler view of photo albums and when the user taps on an album, it expands to grid layout showing images in that album.
For example, non-expanded:
and expanded:
The layout of outer recyclerView item only contains a textView, checkbox, and an invisible recyclerview that becomes visible on click of the item.
I declared the outerAdapter like this:
photosVideosAdapter = new PhotosVideosAdapter(getContext(),new ArrayList<PhoneAlbum>());
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(photosVideosAdapter);
Here is the onBindViewHolder of outer adapter:
holder.tvFolder.setText(phoneAlbums.get(position).getName());
ArrayList<PhonePhoto> phonePhotos = phoneAlbums.get(position).getAlbumPhotos();
InnerAdapter innerAdapter = new InnerAdapter(context, phonePhotos);
holder.innerRecyclerView.setHasFixedSize(true);
holder.innerRecyclerView.setLayoutManager(new GridLayoutManager(context,3));
holder.innerRecyclerView.setAdapter(innerAdapter);
.... OnClickMethod to toggle visibility of innerRecyclerView
And onBindViewHolder for inner adapter:
Glide.with(context)
.load(phonePhotos.get(position).getPhotoUri())
.override(200,200)
.into(holder.imgThumbnail);
The problem is that if inner recyclerView contains more than 200 items, the app crashes due to high memory usage. But as you can see, I'm using glide to load images and also RecyclerView shouldn't create all the views at once. But, what I can see is that the inner RecyclerView is creating all the item views at once which is causing the app to crash.
How can I fix this problem? Any help will be appreciated.
Trust me, dont go with recyclerview inside recyclerview.
I had the similar situation in my current app where my colleague built solution by using nested recyclerview.
I rewrote the entire logic using insert and delete with animation and multiple view type.
It will involve some extra code to manage it. But the result would be quite satisfying.
In fact, I used same logic in iOS collectionview as well. App on both platform is live.
Try add this to your manifest.xml
android:largeHeap="true"
Your memory consumption is likely to high because you retrieved all the photos from the album at once with the line:
ArrayList<PhonePhoto> phonePhotos = phoneAlbums.get(position).getAlbumPhotos();
I assume you are also pulling all the photos at their original quality, so having 200+ full size photos on phone's RAM all at once could be the cause of the crash.
One way you could fix this would be to load in lower resolution previews of the photos into RAM, and once a photo is actually being displayed in the RecyclerView you could load a full resolution photo.
I have a list which I use recyclerView to display. Each row in the list displays an image that is downloaded from a remote server. In order to speedup the display of the list I am downloading the images in the background. Each time the bind() wants to display the image I call a method that starts an asynchronous task that download the image and in the onPostExecute() method I call setImageBitmap() of the image to display the bit-mapped downloaded.
The problem is as follows
As I scroll down, the recyclerView loads more images that fits the screen and when the setImageBitmap() is called the list jumps up, so that the last 1 or 2 items in the list almost never displayed.
When I disable the call to setImageBitmap() there is no problem in displaying the last items (the list now is without images), and scrolling up and down the list.
The problem also shows up when I scroll down very slow. I limited the screen to show only 4 items, and as I scroll down to the 6th or 7th item suddenly the display jumps and displays again from item 2 to 5.
What is going on there and how can I make the display to scroll smoothly and without jumping all over?
There problem here comes to the fact your recycler view at the time it is inflated has size X, when you load your image the size changes but the LayoutManager is not aware of this change, hence it won't update your view.
You should post a snippet of your code, it will help to clarify.
Two questions:
a) is there any way to stop views from getting "thrown out" when you scroll? I'm fetching images from the web using the YouTubeAPI (YouTubeThumbnailView) and it takes some time to fetch these - when scrolling the images are loaded in a couple seconds after scrolling has stopped.
and b) can I withhold elements of the listview until they are completely loaded? I'd prefer the elements to render when their thumbnails have loaded.
It seems like you're going about this the wrong way. You should be fetching the images and storing them in some sort of cache (there are numerous topics on this, so I'll leave that part as an exercise) and then notifying when the data has loaded and updating the thumbnail if it's still on screen. It shouldn't matter whether the View was "thrown out"; on the next time you try to View that particular thumbnail it should already be in the cache and should be able to be loaded near-instantly.
As for the second question, you could initially set the visibility of the View that you return in your Adapter's getView() to INVISIBLE, then when you display the thumbnail, set the thumbnail and then set the View to VISIBLE.
Feed in Four Square android app looks like this:
A rectangular box with actual post, below which is a Text like 10 people like this and so on.
Is this just a listView with Actual content having margins and a different background, and TextViews below it.
Because when i had a complicated ListView with an ImageView and 5-6 TextViews, it takes lot of time to load the UI.
1 of my friends who work on iOs said Instead of Custom ListView Items, use ImageView which look like a Custom View with images and textviews. Which makes loading lot faster. But ofcourse it takes lot to implement such thing.
I just want to know, if its just another custom listview?
And that my delay to load the UI is because of someother reason?
Such techniques are available in android too??
Thank You
Could be a ListView with complicated children views. Could also be an ExpandableListView.
There's a few reasons why it may load faster than something you've used before:
The images were probably already cached on the file system, so it loads from local storage rather than downloading the image.
The images were already the correct dimensions, so no resizing was needed.
Image loading was done on a background thread as to not lag the UI while images were being loaded into memory.
You could expand a complex layout for each ListView item in Adapter.getView() and remove (View.setVisibility(View.Gone)) on the views you don't need based on the item's object you want to render.
If you want to reuse the expanded layout don't forget to set the visibility of everithing again to View.VISIBLE.
I'm already doing this in my code:
if (post.getImageURL() != null && !post.getImageURL().isEmpty()) {
holder.image.setVisibility(View.VISIBLE);
holder.image.setScaleType(ScaleType.FIT_CENTER);
displayImage(post.getImageURL(), holder.image);
} else {
holder.image.setVisibility(View.GONE);
}
In this case I'm hidding an optional image.
I am new Android developer. I want to achieve the following: On a particular screen (Activity) I have a grid view with images being set onto it. I have hundreds of thumbnail images coming from a server via http request. Now I don't want the gridview (with 4 columns in one row) to load the images in one go. Instead I want images to be loaded one row at a time (4 images), with other tiles of the grid view showing progess bar. Also when only few images are displayed the user should still be able to scroll the gridview vertically and thereby displaying empty gridview frames or tiles but if the scrolling is paused the frames or tiles of gridview are again loaded one row at a time.
I am looking for ideas to achieve this.
Thanks in advance for your responses.
In your Adapter's getView method set the image with a indefinite progress spinner.
After that start an AsyncTask to download the image if its not present in the object returned by getItem. Pass the item's position to the asynctask.
In AsyncTask's onPostExecute set the image to the view at position and hide the progress spinner.
Downloading the image in AsyncTask will ensure that your gridview is filled with items(empty) and responds to scrolling.