{"id":104,"date":"2014-04-03T19:04:46","date_gmt":"2014-04-03T19:04:46","guid":{"rendered":"https:\/\/idunnololz.com\/wordpress\/?p=104"},"modified":"2014-04-07T20:31:13","modified_gmt":"2014-04-07T20:31:13","slug":"the-animatedexpandablelistview","status":"publish","type":"post","link":"https:\/\/idunnololz.com\/wordpress\/?p=104","title":{"rendered":"The Animated ExpandableListView"},"content":{"rendered":"<p>In android, the ListView is one of my favorite widgets due to its beautiful implementation, it&#8217;s high performance and it&#8217;s flexibility. However, the ExpandableListView (which extends ListView) is much more primitive. A much needed feature that I was looking for is missing and the solutions offered online seemed less then ideal. The feature that I&#8217;m talking about is the animating of expanding and collapsing groups. Disappointed that there were no great solutions to this I decided to write my own.<\/p>\n<p><strong>The problem with the popular solution<\/strong><\/p>\n<p>There is a very popular solution floating online which is to use a ListView instead of an ExpandableListView and populates the ListView with custom views which are animated to expand and contract. Although this solution does work if the number of children of a group is small, it will miserably fail when the child is more complex and large. This is because this solution uses a ListView therefore although group items will enjoy the benefits of being in a ListView, child items will not meaning they will take up more memory (so if you have 100 child views for one group, all 100 child views will get loaded into memory) and all child views will not be optimized (for instance if you have 100 child views, they will be iterated over every time the screen is drawn). So the main draw back here is performance when the number of children of a group is sufficiently large.<\/p>\n<p><strong>A better implementation<\/strong><\/p>\n<p>A better implementation does exist, the main drawback being it is a bit more difficult to implement correctly but once implemented will still offer all the benefits of an ExpandableListView while providing great animations. However, we can ignore the drawback since I&#8217;ve already done the implementation part, all you need to do is include it in your project! You can view my implementation and a sample application here:<\/p>\n<p><a href=\"https:\/\/github.com\/idunnololz\/AnimatedExpandableListView\">https:\/\/github.com\/idunnololz\/AnimatedExpandableListView<\/a><\/p>\n<p><strong>How does it (the AnimatedExpandableListView) work?<\/strong><\/p>\n<p>Well one thing you will need to offer all of the benefits of an ExpandableListView is to have an ExpandableListView, so the AnimatedExpandableListView does just that and extends ExpandableListView. The rest, however, is a bit complicated. I have done my best to put a lot of comments in my source, so you may find you&#8217;ll learn a lot from reading the source. However, to summarize how it all works:<\/p>\n<blockquote>\n<div id=\"LC25\">\u00a0\/*<\/div>\n<div id=\"LC26\">* A detailed explanation for how this class works:<\/div>\n<div id=\"LC27\">*<\/div>\n<div id=\"LC28\">* Animating the ExpandableListView was no easy task. The way that this<\/div>\n<div id=\"LC29\">* class does it is by exploiting how an ExpandableListView works.<\/div>\n<div id=\"LC30\">*<\/div>\n<div id=\"LC31\">* Normally when ExpandableListView.collapseGroup(int) or<\/div>\n<div id=\"LC32\">* ExpandableListView.expandGroup(int) is called, the view toggles<\/div>\n<div id=\"LC33\">* the flag for a group and calls notifyDataSetChanged to cause the ListView<\/div>\n<div id=\"LC34\">* to refresh all of it&#8217;s view. This time however, depending on whether a<\/div>\n<div id=\"LC35\">* group is expanded or collapsed, certain childViews will either be ignored<\/div>\n<div id=\"LC36\">* or added to the list. Creating the effect of group expansion or collapse.<\/div>\n<div id=\"LC37\">*<\/div>\n<div id=\"LC38\">* Knowing this, we can come up with a way to animate our views by using a<\/div>\n<div>* custom adapter which will supply dummy views which we will animated. For<\/div>\n<div id=\"LC39\">* instance for group expansion, we tell the adapter to animate the<\/div>\n<div id=\"LC40\">* children of a certain group. We then expand the group which causes the<\/div>\n<div id=\"LC41\">* ExpandableListView to refresh all views on screen. The way that<\/div>\n<div id=\"LC42\">* ExpandableListView does this is by calling getView() in the adapter.<\/div>\n<div id=\"LC43\">* However, since the adapter knows that we are animating a certain group,<\/div>\n<div id=\"LC44\">* instead of returning the real views for the children of the group being<\/div>\n<div id=\"LC45\">* animated, it will return a fake dummy view. This dummy view will then<\/div>\n<div id=\"LC46\">* draw the real child views within it&#8217;s dispatchDraw function. The reason<\/div>\n<div id=\"LC47\">* we do this is so that we can animate all of it&#8217;s children by simply<\/div>\n<div id=\"LC48\">* animating the dummy view. After we complete the animation, we tell the<\/div>\n<div id=\"LC49\">* adapter to stop animating the group and call notifyDataSetChanged. Now<\/div>\n<div id=\"LC50\">* the ExpandableListView is forced to refresh it&#8217;s views again, except this<\/div>\n<div id=\"LC51\">* time, it will get the real views for the expanded group.<\/div>\n<div id=\"LC52\">*<\/div>\n<div id=\"LC53\">* So, to list it all out, when expandGroupWithAnimation(int) is<\/div>\n<div id=\"LC54\">* called the following happens:<\/div>\n<div id=\"LC55\">*<\/div>\n<div id=\"LC56\">* 1. The ExpandableListView tells the adapter to animate a certain group.<\/div>\n<div id=\"LC57\">* 2. The ExpandableListView calls expandGroup.<\/div>\n<div id=\"LC58\">* 3. ExpandGroup calls notifyDataSetChanged.<\/div>\n<div id=\"LC59\">* 4. As an result, getChildView is called for expanding group.<\/div>\n<div id=\"LC60\">* 5. Since the adapter is in &#8220;animating mode&#8221;, it will return a dummy view.<\/div>\n<div id=\"LC61\">* 6. This dummy view draws the actual children of the expanding group.<\/div>\n<div id=\"LC62\">* 7. This dummy view&#8217;s height is animated from 0 to it&#8217;s expanded height.<\/div>\n<div id=\"LC63\">* 8. Once the animation completes, the adapter is notified to stop<\/div>\n<div id=\"LC64\">* animating the group and notifyDataSetChanged is called again.<\/div>\n<div id=\"LC65\">* 9. This forces the ExpandableListView to refresh all of it&#8217;s views again.<\/div>\n<div id=\"LC66\">* 10.This time when getChildView is called, it will return the actual<\/div>\n<div id=\"LC67\">* child views.<\/div>\n<div id=\"LC68\">*<\/div>\n<div id=\"LC69\">* For animating the collapse of a group is a bit more difficult since we<\/div>\n<div id=\"LC70\">* can&#8217;t call collapseGroup from the start as it would just ignore the<\/div>\n<div id=\"LC71\">* child items, giving up no chance to do any sort of animation. Instead<\/div>\n<div id=\"LC72\">* what we have to do is play the animation first and call collapseGroup<\/div>\n<div id=\"LC73\">* after the animation is done.<\/div>\n<div id=\"LC74\">*<\/div>\n<div id=\"LC75\">* So, to list it all out, when collapseGroupWithAnimation(int) is<\/div>\n<div id=\"LC76\">* called the following happens:<\/div>\n<div id=\"LC77\">*<\/div>\n<div id=\"LC78\">* 1. The ExpandableListView tells the adapter to animate a certain group.<\/div>\n<div id=\"LC79\">* 2. The ExpandableListView calls notifyDataSetChanged.<\/div>\n<div id=\"LC80\">* 3. As an result, getChildView is called for expanding group.<\/div>\n<div id=\"LC81\">* 4. Since the adapter is in &#8220;animating mode&#8221;, it will return a dummy view.<\/div>\n<div id=\"LC82\">* 5. This dummy view draws the actual children of the expanding group.<\/div>\n<div id=\"LC83\">* 6. This dummy view&#8217;s height is animated from it&#8217;s current height to 0.<\/div>\n<div id=\"LC84\">* 7. Once the animation completes, the adapter is notified to stop<\/div>\n<div id=\"LC85\">* animating the group and notifyDataSetChanged is called again.<\/div>\n<div id=\"LC86\">* 8. collapseGroup is finally called.<\/div>\n<div id=\"LC87\">* 9. This forces the ExpandableListView to refresh all of it&#8217;s views again.<\/div>\n<div id=\"LC88\">* 10.This time when the ListView will not get any of the child views for<\/div>\n<div id=\"LC89\">* the collapsed group.<\/div>\n<div id=\"LC90\">*\/<\/div>\n<\/blockquote>\n<p>And that pretty much sums up how this thing works. Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In android, the ListView is one of my favorite widgets due to its beautiful implementation, it&#8217;s high performance and it&#8217;s flexibility. However, the ExpandableListView (which extends ListView) is much more primitive. A much needed feature that I was looking for is missing and the solutions offered online seemed less then ideal. The feature that I&#8217;m &#8230; <a class=\"more-link\" href=\"https:\/\/idunnololz.com\/wordpress\/?p=104\"><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[10],"tags":[11,12],"_links":{"self":[{"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/104"}],"collection":[{"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=104"}],"version-history":[{"count":8,"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/104\/revisions"}],"predecessor-version":[{"id":120,"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/104\/revisions\/120"}],"wp:attachment":[{"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=104"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=104"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/idunnololz.com\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=104"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}