RecyclerView jump when item size changes - android

I have stumbled upon a problem regarding the RecyclerView. The situation is as follows:
I navigate from one Activity to another, the second one having a RecyclerView
The second activity reads an index out of the Intent and jumps its RecyclerView to that position (so it may "start" in the middle, from the user perspective)
The RecyclerView houses a list of news, each one having a network-obtained thumbnail
The thumbnails are obtained using Glide, with a placeholder (so a View inside a ViewHolder is at first inflated with height X, which may change after the thumbnail is obtained)
Now the problem is that when the user scrolls up from the item they started up, the ViewHolder is created and Glide makes a request for a thumbnail. It results in the list item having some height and changing it a second later, after the thumbnail is obtained, which in turn results in a nasty "jump" (because of the size change). It only happens 3-4 times, until enough ViewHolders get created to handle the whole RecyclerView.
Any idea how to prevent this? Ideally I would like to make the item above current to expand upwards.
I already tried making the items stack from the bottom, but it does not solve the issue, just makes it happen for the below items instead of the above ones. Also, I can not and don't want to predict the size of the obtained thumbnail.

I would use a place holder image in the Recycled View. And replace that image when it becomes available (downloaded).
This only works of course if you know the size of the tumbnails.

Related

RecyclerView scroll single row at a time

I have a RecyclerView which contains one screen full of information per record. I want to restrict user to scroll single record at a time and like to visualize it as if user is viewing one page at a time.
The problem is, I have create a layout of full screen size, and can populate it with data as well. But, on scrolling it gives an effect of as if I am scrolling a role, and on fast swap, it jumps multiple records as well.
I was thinking of ViewPager, but as records size can vary, I don't know how to use it
I have no idea how to solve this problem, please help.
There is an alternative option, instead of using a recycler view you can use this library.
https://github.com/castorflex/VerticalViewPager
It's a view pager but it has an adapter that makes it Vertical, so in that way, you'll have that behavior that you want.

RecyclerView LayoutManager - force to keep a view even if not visible

I am animating views between two RecyclerView. The first one is something like a list of folders showing the first item as cover, clicking it opens a new view showing the folders content animating the cover to the first item. Clicking back animates all visible views back to the folder where they came from (the cover being the top most view). This looks great as long as the opened folder shows the first item. If I scroll down the first item will be offscreen and the back animation does not look that good anymore because the cover view is not animated (I'm only animating all visible views currently).
What I think would work is following: the LayoutManager could position the first item at a position shortly offscreen and keeps it as a special view in it's pool so that u always can access the first view and when I animate back to the folder view I can animate the cover in addition to all other currently visible items ( the cover will be animated from top of the screen).
This means I need following:
the LayoutManager must handle the first item as a special one that is not recycled (I may need it any time for the back animation)
the first item must always be layed out (either at the default position in the list, if it is visible or offscreen directly above the screen), again because I may need it at any time for the back animation
Can someone help me where to start here? I think this is possible with extending the LayoutManager but I don't know where to start...
Have you tried the following?
recView.getRecycledViewPool().setMaxRecycledViews(TYPE_XXXX, 0);

ListView doesn't resize after rows become bigger loading images from asynctask

I have a ListView that is inside a TabHost. There's a custom CursorAdapter to return 4 different views into the list. One of the 4 types is an image pulled from either the SD or the assets folder. Originally I had the image loading in from the CursorAdapter itself and this would cause the UI thread to hang while each image loaded so I moved the code to an AsyncTask.
This all works EXCEPT for the case where a very short list of items is returned. When this happens, the ListView doesn't re-size after the images have loaded and you can only scroll in a tiny portion of the screen at the top.
I cannot figure out how to get get the listView to relayout. Calling relayout and invalidate on the View itself doesn't work. Trying to find the parent of the View returns null. The default ListFragment layout already sets the height to "Match_Parent". Manually setting the layout in onCreateView to a layout with all heights set to "Match_Parent" doesn't fix it. Passing in the adapter itself to call notifyDataSetChanged() just causes a loop and doesn't re-size the ListView.
Frustratingly, when I change tabs the view flashes up correctly just before the new tab is drawn over it. Very occasionally when I rotate the device or flick between tabs it renders correctly, but most of the time it doesn't.
For debugging purposes I tried adding various relayout commands to onListItemClick and found that calling ListView.relayout() sometimes fixes the problem, but often doesn't.
On the left is how it loads with the AsyncTask, on the right is how it loads with the main thread loader. The content is there on the left but you have to scroll because the listview is incorrectly sized.
What am I missing? Is there some way to get the ListView or the current tab or even the whole screen to redraw?
You need to notify the ListView to redraw itself. This is expensive and you should try to avoid it. It will also likely cause frustration with the user as the scroll position keeps changing. I would recommend trying to find an ideal ImageView size and use that to lock the row heights/widths so dynamic content does cause these issues.
http://developer.android.com/reference/android/widget/ArrayAdapter.html#notifyDataSetChanged()
arrayAdapter.notfityDataSetChanged();
That should do it.
Issue is cause by the ImageView resizing after the ListView has been laid out. Setting the ImageView's height and width to the dimensions of the image before spinning off the AsyncTask makes the ListView layout correctly.My images are all local with the filenames etc in a db so I can get the correct dimensions prior to loading the image. If you couldn't get the image dimensions ahead of time, I'm not 100% sure what you'd do.

control when listView removes nonvisible views?

I find listView recycles its views too fast.
When my listView scrolls, views falls off the screen gets removed right away.
Each cell(row) has image loaded using universal-image-loader.
Views which fell off the screen has to reload the image when they comes back into visible area. (it shows the stub image for short time period and loads the correct image).
I definately need to keep the view recycling behavior, but can I modify the list view's behavior so that user won't notice constant reloading of images?(maybe I keep 2-3 times of # of views in a cache than a regular list view would)
Unfortunately the code for ListView and friends is horribly complicated by the fact that it's designed to scroll unevenly-sized items without knowing the height ahead of time. That makes it brutally difficult to run with anything but the default behavior. In addition, most of the methods you'd need access to, to easily customize the behavior are hidden or private. It would be a massive job to try and roll your own (across multiple platforms, subtleties of scrolling, flinging, dragging, scrolling, keyboard focus &c).
The best solution is probably to maintain an image cache that fills in lazily around the view positions that are active. Not neccesarily easy. But way easier that trying to mess with ListView.
A very useful API for this is ListView.setRecyclerListener(AbsListView.RecyclerListener listener), which gives you a hook to track which images are actively displayed.
I can suggest you to use a ScrollView instead of the list view. The ListView is designed to display a lot of data efficiently, that's why your off screen items are destroyed. In which concerns the scroll view, once loaded, you will be able to scroll up and down without recreating the off screen objects (because they ill not be destroyed).
See: http://developer.android.com/reference/android/widget/ScrollView.html
EDIT:
If it is mandatory for you to use the listView, you cluld take a look here: How do i prevent recycled ListView items from showing old content?
There is a posibility to override the listView getView method and keep more items not to be destroyed that the number the listView is keeping.

preventing ListView from reusing a ViewFlipper

I have a ListView and one of the rows (the first one) is a ViewFlipper.
Now, My problem is that when the ViewFlipper is scrolled out of the screen and back it gets reused. Usually it is a good thing to reuse only, but in my case when the ViewFlipper is recycled its starting from scratch.
I have 5 images in it and for example if the ViewFlipper was on the second image and you scrolled it out of the screen and back, its getting back to the first image. The timing and images are important since it is in sync with other components on screen and after reusing its out of sync.
So, How can I make the ViewFlipper continue running when its out of the screen so it will get back to the exact position and timing when its back on?
To prevent the recycling of the row containing the ViewFlipper(and because this row is the first one) you could simply set it as the header view for the ListView(but you'll need to set the header view before setting the adapter). Another approach would be to keep somewhere the current position in the ViewFlipper and when the row containing this ViewFlipper will be requested you would reset the ViewFlipper's position based on that saved position in the getView method.

Categories

Resources