I have decided to upgrade my code to use RecyclerView now instead of the list I am using now.
As I understand the following is how it normally goes:
onCreateViewHolder - this inflates a view, and does the findviewbyid and returns it as a ViewHolder object for the view
onBindViewHolder - this assigns the view holder values to the position view (which is being recycled).
This is all fine and dandy... If the views in the list contain the same fields...
My views however in my list are different, before I programmatically added views depending on the adapter List (in the getView method).
Is there a "good practice" way to handle this, I can't think of a good way to get around this.
getViewItemType does not work as the views are unique and that they are not predefined.
This is what you need to use when you different types of Views inside a ListView or RecyclerView :-
getItemViewType() and getViewTypeCount()
First you need to use getViewTypeCount() and return the number of unique views you need inside your List. Then override getItemViewType() and return the View type you want to inflate inside the List row.
Hope it will help.
If you still have any issue and need a working example, let me know, i'll update my answer.
Related
I am using kotlin and I am starting to understand how a recyclerview with multiple view types works.
From what i understand, there are two methods
To create multiple Viewholder and use a when statement (Java switch) in the OnCreateViewHolder to differentiate when what viewholder should be used. -> also overriding the getItemViewType function and bind the data in the OnBindViewHolder.
To make multiple adapters (for each viewtype one) and use adapter concatenate or whatever it was called.
Now what i want to know is when should i use what method? What is better for my app i am coding? Is one method better then the other?
Can someone explain me when to use what method?
Generally speaking you create 1 adapter and different viewHolders. Every viewHolder serves it's viewtype and has it's own inflated xml.
Every item of the list that you are feeding to your adapter has to have a viewType, so when you are about to "draw" a list item you send it to the correct viewHolder to inflate it.
Do not use more than one adapters, it will get messy and hard to maintain.
I want to have in my GridView first item different from the others. The gridview's adapter can be either adapter extending from CursorAdapter or ArrayAdapter. It is depending on from where are images path taken - db, or arraylist.
For now everything works fine, but I want to have first element different from the rest. The first element, no matter what is the adapter, has to be empty element which will be button from where I can add extra elements. The image in first element has to be from resource, while the images for rest of the elements are form uri.
Also, there is emptyView of this gridView set. I've tried adding first element into arraylist at the very beginning, but then empty view is not shown. Also, I don't know how it will work with content from DB. To be honest I haven't got any more idea, and also I cannot find anything in Google.
Do you know any way I can add this first view?
I need it to work on API10 and above.
You have 2 options
in the adapter, you can check if position is 0 than inflate the other view.
More complex but seems like better one for you is to override (Assuming you are using ArrayAdapter) the methods:
getItemViewType(int position)
getViewTypeCount()
You can find nice and friendly example here
--- Edit ---
To show your empty view you need to be sure that:
Your View inflated correctly.
you called mGridview.setEmptyView(view);
There are no items to show in the adapter, that means that in your grid view adapter the function getCount() returns 0. Note that this function should be implement with the correct logic after you implements the 2 methods I've mentioned above.
in my listview I have items with different layouts, in fact they use one sub-layout few times. I cannot use getViewTypeCount() and getItemViewType(), because I don't know how many times the sublayout will be used. Is it possible to optimize somehow getView() method and use it's convertView parameter, or do I have to inflate view each time?
Since you know how many types of layout you would have - it's possible to use those methods.
getViewTypeCount() - this methods returns information how many types of rows do you have in your list
getItemViewType(int position) - returns information which layout type you should use based on position
Then you inflate layout only if it's null and determine type using getItemViewType.
Look at this tutorial for further information.
UPDATE:
To achieve some optimizations in structure that you've described in comment I would suggest:
Storing views in object called ViewHolder. It would increase speed because you won't have to call findViewById() every time in getView method. See List14 in API demos.
Create one generic layout that will conform all combinations of properties and hide some elements if current position doesn't have it.
I hope that will help you. If you could provide some XML stub with your data structure and information how exactly you want to map it into row, I would be able to give you more precise advise
I tried to add these views to list view using this kind of factory but everytime I try and add the view to a ListActivity, it comes up with nothing. What am I doing wrong? I set my list views like so:
List<View> views = new ArrayList<View>();
for(int x =0;x<tagg_views.size();x++){
lv.addHeaderView(views.get(x));
}
It looks like you are trying to add x number of headers to your ListView. That doesn't make sense.
A ListView should contain x number of copies of the same view, with different information on each line.
Hello ListView gives a good example of the correct usage of a ListView.
Why are you adding the Views to the list yourself? I would highly recommend using any kind of apropriate Adapter for the List. The adapter will handle the creating and recycling of views while the user is scrolling etc. If you use an Adapter it is discouraged to save references to the view yourself like you are doing it in the views list.
The addHeaderView method you are using is made to one single header to the list that always will appear on the top of the list. This means calling it in a loop will not have a reasonable result.
Look into the helloListView example Mayra mentions to understand how a list in android is working. To see how a custom listadapter works have a look at this tutorial looks promising despite the bad code formatting.
A ListView is linked with and Adapter. The Adapter is responsible for the data displayed in the ListView. Take into account that internally ListView creates a pool of itmes (or a pool for each type of item that can be displayed in your case).
For this purpose your adapter needs to implement the following methods:
int getItemViewType(int position): Get the type of View that will be created by getView(int, View, ViewGroup) for the specified item. So you need to identify you types.
int getViewTypeCount(): Returns the number of types of Views that will be created by getView(int, View, ViewGroup). This is used to create a pool for each type of item.
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.