Multi-level ExpandableListView in Android - java

I'm trying to create a category tree from a given unknown-size nor level list of categories. So I'm trying to create a general expandable list which can contain 2+ number of levels.
The general idea is to add to every child who has childs another ExpandableListView in it's layout. The problem is that the second level wont open, it looks like it rendered it over the child or something. Here's some screen-shots of the result:
Here's how it's look like before opening
And after opening the first option: (It's supposed to be empty)
And now open the second one: (Has 4 childs and one of them have childs of his own)
As you can see the third option looks like it's been rendered another option or something on it, after clicking to open it:
It's does nothing except change the state of the arrow. At least it tries to open it...
Here's the code:
CatAdapter.java: (extends BaseExpandableListAdapter)
http://pastebin.com/6yUTkMbJ
Category.java:
http://pastebin.com/E7yWwpna
catitem.xml:
http://pastebin.com/G5MPT3Ua
The usage:
http://pastebin.com/Wk0FqJhn
Sorry for the long question, I was trying to be clear as possible.
Thanks in advance! Sorry for my bad english!
Edit:
I ended up making a custom view for this task, thank you all for your answers!

I believe the problem is in your getChildView() method:
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
convertView = inflater.inflate(R.layout.catitem, parent, false);
TextView textView_catName = (TextView)convertView.findViewById(R.id.textView_catName);
Category current = categories.get(groupPosition).childs.get(childPosition);
textView_catName.setText(groupPosition + " , " + childPosition);
if(current.childs.size() > 0 ) {
ExpandableListView elv = new ExpandableListView(context);
elv.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT, AbsListView.LayoutParams.WRAP_CONTENT));
elv.setAdapter(new CatAdapter(context, current.childs));
((ViewGroup)convertView).addView(elv);
}
return convertView;
}
When you encounter an 'expandable' child, you are still inflating R.layout.catitem and adding your new elv to it. Since the catitem is a RelativeLayout and you don't add any parameters for alignment, each view is placed at the top-left corner, overlaying whatever else is there.
You may want to try changing R.layout.catitem to have a vertical LinearLayout as its root. This should prevent them from overlapping the child's title, but I can't guarantee that the children's children will not still overlap. It's an easy change, though, and worth a shot.
Also, from the docs for ExpandableListView:
Note: You cannot use the value wrap_content for the android:layout_height attribute of a ExpandableListView in XML if the parent's size is also not strictly specified (for example, if the parent were ScrollView you could not specify wrap_content since it also can be any length. However, you can use wrap_content if the ExpandableListView parent has a specific size, such as 100 pixels.
That says "in XML", so I'm not sure if it means to apply to code or not. It seems to me that they'd use the same mechanism, so it might be worth looking into. I'm not sure how you'd go about setting a maximum size for the parent. You may be able to measure the size of one item, and multiply by the number of children. You'd have to take margins/separators into account, so it may not be simple.
If that doesn't work, you may need to roll your own ViewGroup directly. It shouldn't be too hard to implement it from scratch, just don't forget to try to take advantage of view recycling(which your current method doesn't do anyway).

I was have same situation where I had to use ExpandableList and at each level I needed to have groupPosition and childPosition but using multi-level ExpandableList, It was not possible to get accurate groupPosition at second level data. So that, I used another approach. I have given a demo link which I used in a project.
1) In this library, you need to bind parent child relation in advance before you bind your actual data.
2) In your Layout, top most layout must be LinearLayout. Please refer header.xml, groups.xml and childs.xml in demo link.
Demo Link: MultiLevelTreeView
Thanks.

Related

Performance concern in Recycler view while rendering various types of cell

In a recycler view there are mainly 3 types of cell. I have achieved that through the following piece of code
final int TYPE_COMMENT = 10004;
final int TYPE_UPDATE = 10005;
final int TYPE_ACTIVITY = 10006;
public int getItemViewType(int position) {
if(activity.getActivity().getType().equalsIgnoreCase(CHECKIN_COMMENT_STRING)){
return TYPE_COMMENT;
}else if(activity.getActivity().getType().equalsIgnoreCase(CHECKIN_UPDATE_STRIND)){
return TYPE_UPDATE;
}else if(activity.getActivity().getType().equalsIgnoreCase(CHECKIN_HISTORY_STRING)){
return TYPE_ACTIVITY;
}
return TYPE_ACTIVITY;
}
Then the issue came while i am inflating the second one.It have a complex layout with mainly 5 parts. Each cell have a chance to come with the one or combination of this 5 parts(having LinearLayout root and followed by some complex widgets). So i have done research and find we can overcome it by following ways.
By using visibility (View.GONE), I can hide/show the desired part.
Define each part in view stub and inflate it when required.
Programatically add widget to desired parts(But i am not recommend this since views are much complex)
Define separate cell for each combination of parts but it may take 5! cell design thats 120 cells. So not possible
Can you please suggest which is the better way to do (Or suggest alternative), while performance is my key constraint.
If performance is the goal and the quantity of those complex layouts are not very high then I suggest you to don't recycle the complex views, that's to assign a different ViewType for each one and then to fill all values in onCreateViewHolder and not in onBindViewHolder. The scrolling will be smoother.
A tip:
I used this system in a complex list with layout containing many widgets, textviews, editboxes, seekbar and others it worked fine but sometimes unexpectedly onCreateViewHolder was called twice for the same ViewType, and that caused the data previuosly entered by the user to be lost. I solved it finally using a Map of already created ViewHolder vs viewType Map<Integer, MyViewHolder> holderMap = new HashMap<>();
and in onCreateViewHolder I return the previuos holder if it exist.
Hope it will help

Java android baseAdapter set list item one time

Hello i have a fully working code for my list adapter:
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.overview_item, null);
//getting id's
TextView name =(TextView)vi.findViewById(R.id.userUsername);
TextView date =(TextView)vi.findViewById(R.id.imageDate);
ImageView image=(ImageView)vi.findViewById(R.id.userImage);
ImageView avatar=(ImageView)vi.findViewById(R.id.userAvatar);
//setting text
name.setText(dataNames.get(position));
date.setText(dataDates.get(position));
//set image
Log.d("test: ", "Adapter wants to get picture");
imageLoader.DisplayImage(dataImage.get(position), image);
imageLoader.DisplayImage(dataAvatars.get(position), avatar);
return vi;
}
This code works perfect but the problem is this function runs everytime when you scroll throught the listview so whenever the lis item is getting in sight. And that's not what i want. i want it to do this function just once for every list item. This is because when your scrolling fast trought the list it has to load all images again so the loading image is showing and it keeps jumping because the loading image is another size then the image wich is getting loaded. I hope thay tou understand my question and can help me. Already thanks and if i'm not clear please ask my anything in the comments.
So short:
How do i run this code just once for every list-item and not everytime when it's getting in sight?
Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.
Check this links:
1 - http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
No, you should not. This is the way ListView works. Beside, you should use ViewHolder pattern for better performance.
If you still want to do this, you could remove check NULL with convertView. It will solve your problem, but lead to performance, I think.

how to scroll live cards in aglass activity

I'm writing a glass app.
In one activity I want to scroll between few cards (which were popups in my android app).
1) I thought to use cardsScrollView.
problem: Is it possible to set customView to a card object?
2) I thought to use LiveCard
problems:
Is it possible to publish them inside my app and not in the timeline?
Is there an equivalent LiveCardsScrollView?
Any other idea how to implement this?
From Google's sample code at https://developers.google.com/glass/develop/gdk/ui/theme-widgets and API documentation at https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/widget/CardScrollView and https://developers.google.com/glass/develop/gdk/reference/com/google/android/glass/widget/CardScrollAdapter, it seems your 1) is possible, because:
1) The CardScrollAdapter's method public View getView(int position, View convertView, ViewGroup parent) returns a View (not a Card);
2) CardScrollView's get methods also return a View or Object, not Card specifically;
3) You can replace private List<Card> mCards; in the sample code (link #1 above) with private List<MyView> mViews;
But the documentation at those links also use Card as example, and the word cards seem to refer to static cards. So will have to test to find out for sure. However, there's a statement in link #1 that says "You can build a standard view hierarchy yourself or use the Card class.", suggesting it's possible to use a custom view.
I'll get back with you within 12 hours after I test with my Glass tonight.
As for your question 2, the answer is yes - you publish the scrollable content inside your app and not in the timeline. You can launch the activity (as in the sample code in Google's link #1) from a menu item selection, and the menu is attached to your livecard. Then inside that scrolling view, you can only swipe left and right to see other cards (or maybe custom views) in the scrolling view, but not the timeline. You have to swipe down to exit the activity (immersion) to go back to livecard, then you can swipe left and right and see the timeline. Note the scrolling view is not like static cards and will never show in the timeline. Also note that inside the scrolling view, you may use GestureDetector to capture other gestures (besides swipe left and right and down).
Just confirmed: custom views can be added to CardScrollView! I used a view that extends FrameLayout and inflates a layout xml file, and added three such views to CardScrollView. It works nicely!
Also tried to add a custom view that does the Canvas drawing, but haven't been able to see it shown in the scrolling view. Will try more later.
Just tested and found you can add any views to the CardScrollView - I'm able to add 4 custom views to a scrollview: one static Card, one view with Canvas drawing, one with OpenGL ES 1.0 drawing, and the final one with OpenGL ES 2.0 drawing. This is good to know to me! Thanks for your question.

Explanation of the getView() method of an ArrayAdapter

Could you explain the getView() method of an ArrayAdapter.
I read the docs and it has three parameters:
position: The position of the item within the adapter's data set of
the item whose view we want.
convertView: The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.
Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)).
parent: The parent that this view will eventually be attached to
I understood the position parameter. Like they said, it's imply the position of the item, who view has been requested.
Where does convertView come from. I've seen many examples where they check if convertView is null. If is is null, they inflate a new instance of the row layout, populate it and return it. I guess I've gotten my head around that too but one thing still baffles me. What is the layout that is passed in through the convertView parameter. Is if the resource parameter that is passed in when initialising the ArrayAdapter? Is is a cached copy of the last layout returned by getView()?
And lastly. What does the parent parameter do. I haven't seen too many examples utilising this. Most of them simply reuse/inflate a row layout and return it.
(I'm asking because I have an on-click animation in my ListView. Specifically this one that aims to replicate Spotify's dropdown quick action menu. My animation has been a little sluggish. After diagnosing this issue for a while, I've realized that this has been due to the fact that my getView() method takes a bit of time to complete as I'm inflating a fresh row layout in every iteration. Someone suggested caching the row layout in a ViewHolder while other examples point to reusing the convertView parameter i.e. only inflating a row layout if convertView is null.)
Is is a cached copy of the last layout returned by getView()?
The convertView is the view of a row that left the screen(so it isn't the last view returned by the getView method). For example, the list is first shown, in this case convertView is null, no row view was previously built and left the screen. If you scroll down, row 0 will leave the screen(will not be visible anymore), when that happens the ListView may choose to keep that view in a cache to later use it(this makes sense, as the rows of a ListView generally have the same layout with only the data being different). The reason to keep some views in a cache and later use them is because the getView method could be called a lot of times(each time the user scrolls up/down and new rows appear on the screen). If each time the row view would need to be recreated this would have resulted in a lot of objects being created which is something to avoid. In your getView method you would check convertView to see if it is null. If it's null then you must build a new row view and populate it with data, if it isn't null, the ListView has offered you a previous view. Having this previous view means you don't need to build a new row layout, instead you must populate it with the correct data, as that cached view has the old data still attached to it(you would see a lot of questions on stackoverflow where users ask why the rows of their ListView are duplicating when they scroll down).
What does the parent parameter do. I haven't seen too many examples
utilising this. Most of them simply reuse/inflate a row layout and
return it.
It should be used to get the correct LayoutParams for the newly inflated/built row. For example, if you inflate a layout which has a RelativeLayout as the root and you don't use the parent to get the LayoutParams you could have some problems with the row layout. To take the parent in consideration you would use :
convertView = getLayoutInflater().inflate(R.layout.row_layout, parent, false);
My understanding of convertView is that it's essentially views that have been recycled because they're not being used at the moment - for example, you scroll down the list, the ones at the top aren't on the screen, so they get passed into this parameter for use when you need a new view (so you don't have to create a whole new one while having unused ones sitting around idle). iOS has a similar method called dequeueReusableCellWithIdentifier. If each row of your listview has the same structure, it's safe to cast this to the appropriate type and just update the information in it - text, images, etc. It will be a View that was previously returned by a getView() call for the same list.
My best guess (and it is admittedly a guess) with parent is that it's the view that this adapter's list is a child of. It gives you a route back to the rendering system if you need a context, access to the resource system, to pass information to or receive information from the list's parent view.

How to Implement Custom List View for the list Items in Android Application

I had a problem with the list view having both parent list and the child list of the list activity(implemented through database query). I wish to show them differing their properties by changing the text style (parent list items are in bold, child list items are in normal style).
I wish to differ with Parentid(Null, NotNull)in their text style(bold, normal) for parent and child items respectively. Please help me with the code/links. Thanks a lot in advance.
Maybe an ExpandableListAdapter would be a way to go? (You can make the parent elements non-expandable if you want.)
If you want to stick with the simple list, you can override getView(int position, View convertView, ViewGroup parent).
And maybe also override bindView(View view, Context context, Cursor cursor).
I have used the option of overriding bindView() to add a tag to a view and examine this later on. Maybe this would also work for you...

Categories