Recyclerview with multiple dynamic views being inflated inside row - android

I'm getting my data via an API that supplies me with an array of rows for my RecyclerView. Inside each of these rows there is an array of elements which I want to add dynamically to each of my RecyclerView rows. Each element corresponds to a view. So for example I might get a title, then an image, then text. Or I might get just an image. Or a title and text. These are just some of the elements, there are more and maybe hundreds of different possible combinations. For this reason it's not viable to create and inflate different row types as you would normally do if you need just a few different types of rows.
I tried to inflate and attach my views to the row on the onBindViewHolder method, but this caused the elements to be added again and again everytime that method was called.
I have searched around and have found no similar questions to what I am after, and I was wondering if there was a nice clean and elegant way of achieving what I need.

I managed to achieve what I was looking for using the following method I had already tried:
I tried to inflate and attach my views to the row on the onBindViewHolder method, but this caused the elements to be added again and again everytime that method was called.
To prevent the views to being continuously added everytime a view was recycled, I overrided the onViewRecycled method, and removed all the views from the linear layout inside the row.
So far, I haven't had any problems with this method, but it's probably not the most efficient.

Related

How to get all children (visible and invisible) from a ListView?

My problem is similar to ListView getChildAt returning null for visible children, but despite searching I cannot find a solution.
I have a ListView with a Scroll. The ListView has 10 items, 7 of which are visible and 3 are hidden by scroll. I also have an external method (out of adapter) that must get all of the children from this ListView (e.g. using getChildAt()).
I need all 10 of the items, but the last 3 are null objects. I've tried code like the following:
getListView().smoothScrollToPosition();
But this doesn't work.
I think that I don't need to post the rest of my code, as the description says everything?
As you have already seen you can't get all the child row views from a ListView simply because a ListView holds only the views for the visible rows(plus some recycled rows but you can't reach those). The correct way to do what you want is to store whatever data in the adapter's data and retrieve it from there.
But the ListView doesn't keep the current values from RadioGroup in
running time.
I've seen that you have some problems with this so I've adapted some old code to build a basic example, code that you can find here.
I don't think so you need to add scroll view for a listView. Scroll automatically works on ListView. Try your application without adding scroll view and I'm sure it'll work as you needed.
The reason those children are null it's because they really do not exist and they will never exist, if only 7 children are on the screen at one time, the system will only create 7 and re-use by passing the convertView back to the adapter getView() method.
If you want to grab information regarding your whole dataset you should search on the dataset itself, instead of the views on the screen. E.g. if it's an ArrayAdapter, loop the array; if it's a CursorAdapter, loop the cursor; etc.
The non-visible children of a listView don't actually exist. When they become visible, one of the redundant views is recycled or a new view is generated. So you can't actually access all the views. Why do you want to? Whatever changes you want to make should be made to the data that populates the views rather than the views themselves.
There are a few point that you need to take care of:
1. List view provides inbuilt scroll functionality, So don't use Scroll view. It will only mess up things.
2. List view doesn't contain ALL the children. When you scroll it, it creates only visible items on run time.
3. If you want to get all the children altogether, Better keep an ArrayList of the child objects that your list has. You can add or remove children to this ArrayList as per requirement.

getting more data from a listview row

I have a ListView that contain several TextViews. Once a TextView is pressed I get the onClick called with the view. What is the best practice to get the other text views on that row? IDs of the text views are similar along the rows so I need to keep on the context of the given view. I would guess I need to get the Parent of the given view and then grab the rest of the text views from it.
Thanks.
Yeah, you would need to get the parent of the current textview, then get all the children contained within that parent.
Depending on what you need to do with the textviews you could either call FindViewById on the parent view to get each TextView by ID. Or you can iterate through the children as suggested in this question/answer:
Android - get children inside a View?
I know people mention that calling FindViewByID is a taxing process, so consider storing the references to the textview's in some sort of object so you can quickly get the references to the other textview's within the row without having to look them up all the time.
ViewHolder's work pretty well, as they store the references to the textview's within an object (which you only need to fill once during creation) but it requires setting up your own customized adapter.

How would I use a different row layout in custom CursorAdapter based on Cursor data?

Background: I'm trying to implement a messenging system in my app, and I'm writing a custom CursorAdapter to display the messages in a ListView in the chat window. I want to use a different row layout for incoming and outgoing messages (information that is saved in the SQLite row in the cursor). Each row has the same elements in it with the same IDs, but they are arranged differently.
The Problem: Currently, I have overridden newView() and bindView(). When the ListView is first populated, it creates all of the Views perfectly, checking each row to see if it's either incoming or outgoing, and inflating the proper XML file. However, when I scroll or a new message is added to the window, the adapter recycles Views for the wrong rows. I would override getView(), but it is not passed the Cursor as a parameter, so I have no way of knowing whether the row should be incoming or outgoing.
I'm not looking for code, but rather, some suggestion for an elegant implementation. Thanks in advance!
Here are two possible solutions:
(1) Use a single layout for all items, which you can adjust when binding to show as desired. The most straight-forward way would just to have the root view be a FrameLayout which contains N children for each of the different states, and you make one of them visible and all others gone when binding. Of course you want to take care to not let this cause your items to explode in the number of views they contain.
(2) Implement Adapter.getItemViewType() http://developer.android.com/reference/android/widget/Adapter.html#getItemViewType(int) to tell the list view about the different types of items you have so it will recycle the correct one.
Just a few thoughts, I personnaly find the whole ListView and CursorAdapter combination to be a little... err... is bloated the right word? Would it just be simpler to have a ScrollView/ LinearLayout combination that will just add the appropriate TextView as requested?
But as for your solution, since the user seems to be unable to change the order of the messages as they are added, you could add a ArrayList<String> field to your custom CursorAdapter that will keep track of whether the messages are incoming or outgoing. Something like:
private ArrayList<String> cursorMonitor; //"incoming" and "outgoing" as your options.
...and then inside wherever the ListView gets populated just use
cursorMonitor.add(my_cursor.getString("outgoing_or_incoming"));
And then in the getView() you can override it and use the cursorMonitor to determine which layout you need to inflate.

Should I use multiple ListViews?

I have a RelativeLayout with different elements. I was planning to have two ListViews on it, but I have noticed there are some problems with scrolling. Since each ListView only shows a maximum of 5 rows should I try to make some kind of custom adapter to merge those ListViews? Or is it better to replace the ListView with a LinearLayout/RelativeLayout and add the rows as I get them manually? (like the first answer in here: android listview display all available items without scroll with static header ).
Which should be the proper way on doing this? or is there another way? Also, each row will have an OnClickListener.
There's two solutions if you'd like to keep your list... list-y, without having to prerender all the row Views like the above solution suggests (which can be slow to render, eats RAM and doesn't scale nicely to more than a screen or two of Views, but is a fine quick solution for smaller lists, though I'd just use a bunch of Views in a LinearLayout in a ScrollView rather than a ListView in that case).
Write a custom ListAdapter, overriding getItemViewType, getViewTypeCount and GetView to inflate the proper kind of view and recycle appropriately for your two types of views. You'll also either need to override getItem to contain custom logic for figuring out which set of source data to look in and to map the data accordingly, or mush the data down into one list of Objects (if you're using an arrayadapter) and cast in the getView method (probably a bit slower than handling it in the getItem without casting).
Just use cwac-merge, a view-and-adapter wrapping adapter. You can put two ListAdapters into a MergeAdapter and set that as your single ListView's adapter.
I had problems with scrolling. I never figured out how to have the ListView share vertical space with a different View, and have a single scrollbar for them both.
I worked around it by having everything that needs to scroll on the layout a row in the ListView.
Adding views as rows to a LinearLayout may have problems scaling up, but I think you'll be OK if you only have 10 rows in total. On 1st gen Android devices it'll probably start to get sluggish around 20 items (depends on Layout complexity obviously). ListView scales up by only inflating views as they come on screen.
So in answer to your question either of the two alternatives you suggest will be OK, but the LinearLayout option will be the easiest to code.

How to generate a ListView with headers above some sections?

I want to generate a ListView that has some dividers between some of the entries, like it can be seen in some of the property sections. See the example below. I try to generate a List that consists of some textviews followed by one of the fancy dividers explaining the next part of the list and then again some text views. How can this be done? I thought about creating different views to add to the list? Is this the way to go?
I got a solution. I don't know if it is the best one.
I use a custom adapter derived from ArrayAdapter for the list as described in this tutorial. In the adapter class I check if the position in the getView method is a normal row, then I inflate the row layout. If it is the first row from a new group I inflate a headline layout that is a normal row plus the group headline above it.
If you don't want to mix the header into one of your rows. Consider the following solution:
You can overwrite the two methods getItemViewType and getViewTypeCount.
You now have a list that can display different rows. You need to check the expected view type for the item in the getView Method and inflate different layouts depending on it.
The list will handle the recycling for you in a way that it will return only correct recycle views to your getView method, this means if the recycleView is not null it can be used to display your current cell.
You can use my SectionedAdapter, if GPLv3 is acceptable (licensed that way due to some upstream code). You can use my MergeAdapter, if you need something more flexible and with a less-limiting license (Apache 2).
I think you might be looking for android.widget.ExpandableListView
http://developer.android.com/reference/android/widget/ExpandableListView.html
I'm also interested in an answer to this. There must be a more straightforward way to do this.
In looking at the Adapter, there's a method, Adapter.getItemViewType(int position).
ListView defines a return value, ITEM_VIEW_TYPE_HEADER_OR_FOOTER which indicates if the returned item is a header or footer.
I haven't tried it, but I assume if you create your own Adapter and return an item with the type indicating it is a header or footer, that the ListView will display it appropriately.

Categories

Resources