RecyclerView with unknown/variable row layouts - java

Since RecyclerView uses ViewHolders, it means that you must have a predefined layout for each row. But what if each row needs to have a variable amount of views displayed?
For example, say I'm creating an instant messaging application where users can attach pictures to messages. They could attach anywhere from 0 to x pictures. When you create a RecyclerView to display these pictures (that are downloaded from a server), how would you make the rows include the correct number of ImageViews to display them?
There's a few ways I can think of doing this, but none really seem like good options.
Create ImageViews in onBindViewHolder, add them to a layout in the ViewHolder. (Isn't this what the ViewHolder tries to prevent? This would probably be laggy, especially with lots of pictures)
Restrict the amount of pictures the user can attach to a message to x, then add x ImageViews to the layout that are set to invisible/gone. In onBindViewHolder, set the ImageViews to display the picture and set these ImageViews visible. (What if I allow pictures & videos to be attached? Do I then need to add x ImageViews and x VideoViews as well?)
Put a GridView in each RecyclerView item and populate the GridView
inside onBindViewHolder. (I assume this would have no benefit over
option #1 because it's pretty much the same thing?)
That's all I can think of. Is there any other option that is designed for this sort of situation that I have not come across? Or what are the typical approaches taken towards this problem?

You need to create a heterogenous RecyclerView which will display multiple different ViewHolders. There are several ways to accomplish this.
The basic approach is that based on the number of photos/videos associated with each item in your RecyclerView.Adapter you would override getItemViewType(int position) (a method which is part of the RecyclerView.Adapter class) to return a different int. This int is the viewType parameter passed into createViewHolder(ViewGroup parent, int viewType). You would then use the correct ViewHolder/layout based on the viewType argument.
See here for a good tutorial.
If you don't mind using a library for an alternate approach I recommend AdapterDelegates.

Related

how to show mutiple image in grid like facebook and linked in

I need to show multiple image in grid like example given here.
and also need plus icon while images is more than some certain counts
There are multiple ways to implement it
Two horizontal/vertical LinearLayout
TableLayout with two rows
ConstraintLayout which its children are chained together
GridView
RecyclerView with GridLayoutManager
and more ... :D
Possibly you want to fetch the images dynamically, and there are several collection views: RecyclerView, ListView etc. RecyclerView is common since you can modify the layout(for your case, columns) with various LayoutManagers. Since you haven't provide any code, I cannot help you with that, but here is a guide that might be helpful.
For the plus icon mechanism, you can modify your RecyclerViewAdapter code, limiting the count(in getItemCount()) to a certain number and visible a transculent foreground with '+number' in the ViewHolder code for the last item.

Recyclerview onCreateViewHolder being called despite all views being recycled?

Short Story:
I have a (pre-caching)Custom LinearLayoutManager, RecyclerView, Custom RecyclerView.Adaptor, Custom RecyclerView.ViewHolder setup. I have only one viewType and lightweight binding functions. Its really nothing super special which is why I hoping I don't need to post code. I also don't want to confuse with all the irrelevant code.
The issue I have is that despite not failing to recycle any views, onCreateViewHolder is still being called occasionally (after initial row inflations) leaving me wondering that maybe I have a memory leak? Do you think this is the case and why? What is deciding that my app still needs to create more views instead of recycling?
I will add one thing that maybe factoring in somehow. My rows have two visual states (they expand and collapse) and it kinda appears that having a more random mix of rows in different states worsens the problem.
Full Story:
Ive noticed an occasional hiccup in the smooth scrolling of my RecyclerView. Using android studio's profiler, I have noted the following:
All my bindViewHolder methods are plenty fast and are not holding back the scrolling.
OnCreateViewHolder is what is causing the stuttering. This explains why there is always a few stutters during the first scroll. Moreover, its the inflation thats taking a ridiculously high percentage of the cpu time.
With an item/row layout constructed using ConstraintLayout, the onMeasure functions' poor performance, destroyed the scrolling performance on weaker devices.
With an item/row layout constructed using LinearLayouts, the performance improved drastically. However, the inflation of views still takes long enough to cause hickups.
With this information, I have simplified my row item's layout as much as possible making sure to use LinearLayouts. Irregardless, the rendering of the recyclerview's items shouldnt result in stuttering after the first items come on screen because one, the rows are all the same except for the data binded to them and two, the RecyclerView is supposed to recycle the rows. So onCreateViewHolder should be called a lot initially and then rarely called again. What about pre-caching? I have found this to be one reason that new viewholders are requested when scrolling. I set the cache and created a custom LinearLayoutManager that overrides the pre-caching (pre-fetching?) method called getExtraLayoutSpace(RecyclerView.State state) and adjusted the two so that there are enough existing recyclable views to cover the request during scroll. My tests confirm that after initial scroll, new views arent requested when transitioning into scrolling state.
All that and I have two issues remaining. One of them being that onCreateViewHolder is called every so often during the use of the app and this causes a little hiccup. I put a Log.w() inside onFailedToRecycleView() to see any views are not being recycled and it looks like views are being recycled. So now I think there is some memory leak and the memory profiler shows jumps in memory usage often occuring when onCreateViewHolder is called.
The three questions I posted where,
Do I have a memory leak because Recyclerview keeps creating Viewholders?
Why do I have a memory leak?
What is deciding that my app still needs to create more views instead of recycling?
I believe I have essentially found the solution to the 2 out of 3 of the questions or the respective underlying problems.
Inside Recyclerview class is a Recycler class and RecycledViewPool class. The internal Recycler object is what makes a request to the RecycledViewPool object for an available detached scrap viewholder to recycle if it doesnt already have an attached scrap viewholder to recycle. And specifically, this search for a recyclable viewholder happens inside Recycler's getViewForPosition(int position) function. If (inside this function), a recyclable view is not found, then mAdapter.createViewHolder(RecyclerView.this, type) is called. This leads to onCreateViewholder being called. So to answer the 3rd question, Recycler is deciding when to call onCreateViewholder.
Note that the RecycledViewPool's job is to maintain an array of arraylists of detached scrap viewholders (1d array of an arraylist of each viewType). The maintaining functions include clear() , reset(), and setMaxScrap(int viewType, int max) which do what their names indicate. So the pathway to the solution to second question is to create a custom RecycledViewPool that indicates when scraplist is cleared or diminished so I can track the creation and destruction of scrap views. As far as question 1, this can be solved by using setMaxScrap(int viewType, int max). Also, on initialization of the recyclerview we can put a few extra scrapviews in the pool and try to maintain a minimum number of scrapviews which can probably be done with a listener.

Expandable Items in ListView

I am writing my self a ListView that will contain multiple types of items. I have done that using ArrayAdapter and a ListView now my issue is that I need to have some Items inside my Adapter that is gonna expand and that will have inner items of any type. Now the issue is how can I do this? I know I can use ExpandableListView but I don't need some items to be expanded. I saw this post about something similar, The person suggested to use ExpandableListView or a Custom Item, I would like to do the Custom Item but my concern is that the OP of the answer quoted this.
If the number of items is low consider using a linearlayout to look
like a listview and another linearlayout for the last item.
I'm not sure what they mean by "low" How many items can I have inside it? Would it cause lag?
so my question is what is the best way to do this? I need to put items
in a ListView that is of multiple types, and one of the types can
expand and have inner views that can again contain the same types and
so on.
Edit: Since 2 of you confused of what I want. I want to do the following inside my ArrayAdapter
Item
Item
Item
ExpandableItem {
Item
Item
Item
Item
}
Item
Item
I am trying to make some of the Items expand to have more items inside them and control the onClick of the inner items and such.
About the meaning of "low": I think it's about performance but devices today are better than devices were in 2013 when the linked post was written - maybe you'll never experience a sluggish UI with your approach. Of course this depends not only on your UI structure but also on the type of data you'll be showing (videos or just text?) and on other factors like does the device have to perform heavy work in the background or not.
RecyclerView was developed to be a "better ListView", so if performance can be an issue, then maybe it is a good choice. (The adapters for both ViewGroups can handle different View types but RecyclerView offers a better means of showing changes to single items and customizing change animations, another point in its favor)
How can I do this?
Have different View types for expandable and flat items
You can "manually" expand/ shrink a View (AFAIK your only choice when working with ListView) by using some type of animation (or the Transition framework) but you always have to...
Keep track of the expanded state of each expandable item (e.g. in the Adapter) and use them in getView() respectively onBindViewHolder().
Add another ListView like RecyclerView on clicking of any item.

One large scrolling list of different items -> Better solution than RecyclerView?

I was prototyping an app with a recycler view where it boiled down to basically this concept:
There is one single list, which is entirely scrollable (this bit is important).
The items in question in the recycler view are however very heterogeneous. It is not like I want to have either an image or a text, but sometimes it is a simple "list item (clickable text)", sometimes it is a row with 3 buttons, sometimes it is an icon with text, another one is a button, etc.
While similar item types have similar behaviour, the groups itself are different. They need a different manager, a different ViewHolder to process their very different button events etc.
I find it not very convenient to put everything into the recycler view's adapter with some common base class and delegate everything those different items can do to some callbacks. It feels very clunky.
Is there some better way of handling that? The advantage of the recycler view is that it scrolls well. I personally do not need any lazy creation of those items (= recycling) so I am not winning much here. The other advantage is that I do not need to handle every items creation. Which is also the downside, I need to channel it through the adapter with its getItemViewType based on position etc.

How to display varying cell layouts with asynchronously updating text and images?

I am working on a remote controlling app for a home automation system. The mobile and the home automation device communicate via WebSockets.
To display the functions of the automation device, I am currently using ListViews and Adapters with a varying cell layout. One adapter is displaying all of the device's functions.
The biggest problem I encountered: Adapters keep calling getView() very often, which triggers my functions to register themselves with my state handle and action dispatcher over and over again.
Please note:
There are about 20 different types of functions, all of them requiring a different cell layout. There are sliders, buttons, state text, ...
The functions have asynchronously updating states.
I need to find a way to display those functions in a ListView or a ListView-like layout.
Can you please help me?
Make sure to implement BaseAdapter.getViewTypeCount() which should return the count for the number of different view types your listView is expected to have for its enteries.
And also implement ListAdapter.getItemViewType(int position) to return an int that identifies a specific view and which view type it will be.
Following the above two recommendations will ensure that your ListView will be efficient and it will guarantee that your getView(...) method is called with the appropriate view type, if there is one already infalted.
That said, if you have a few fixed number of view in your list and they are all different, then consider using LinearLayout in a ScrollView.

Categories

Resources