Android ExpandableListView using animation
Asked Answered
P

5

22

I'm using

<ExpandableListView
     android:id="@+id/listView"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
</ExpandableListView>

i want add animation slide for child when onclick parent . So How can i do ?

Particular answered 17/12, 2013 at 6:5 Comment(0)
T
55

Final Update

It's been quite a while since I wrote this answer. Since then a lot has changed. The biggest change is with the introduction of RecyclerView that makes animating a list or grid easy. I highly recommend switching over to RecyclerViews if you can. For those who can't I will see what I can do regarding fixing the bugs for my library.


Original answer

I actually do not like the popular implementation of an animated ExpandableListView that simply uses a ListView with an expand animation because in my use case, each of my groups had a lot of children, therefore it was not feasible to use a normal ListView as the child views will not be recycled and the memory usage will be huge with poor performance. Instead, I went with a much more difficult but more scalable and flexible approach.

I extended the ExpandableListView class and overrode the onCollapse and onExpand functions, I also created a subclass of a BaseExpandableListAdapter called AnimatedExpandableListAdapter. Inside the adapter, I overrode the getChildView function and made the function final so that the function cannot be overrode again. Instead I provided another function called getRealChildView for subclasses to override to provide a real child view. I then added an animation flag to the class and made getChildView return a dummy view if the animation flag was set and the real view if the flag was not set. Now with the stage set I do the following for onExpand:

  1. Set the animation flag in the adapter and tell the adapter which group is expanding.
  2. Call notifyDataSetChanged() (forces the adapter to call getChildView() for all views on screen).
  3. The adapter (in animation mode) will then create a dummy view for the expanding group that has initial height 0. The adapter will then get the real child views and pass these views to the dummy view.
  4. The dummy view will then start to draw the real child views within it's own onDraw() function.
  5. The adapter will kick off an animation loop that will expand the dummy view until it is of the right size. It will also set an animation listener so that it can clear the animation flag once the animation completes and will call notifyDataSetChanged() as well.

Finally with all of this done, I was able to not only get the desired animation effect but also the desired performance as this method will work with group with over 100 children.

For the collapsing animation, a little more work needs to be done to get this all setup and running. In particular, when you override onCollapse, you do not want to call the parent's function as it will collapse the group immediately leaving you no chance to play an animation. Instead you want to call super.onCollapse at the end of the collapse animation.

UPDATE:

I spent some time this weekend to rewrite my implementation of this AnimatedExpandableListView and I'm releasing the source with an example usage here: https://github.com/idunnololz/AnimatedExpandableListView/

Tun answered 12/3, 2014 at 7:36 Comment(12)
You're expandable listview works great. Love the animation :)Seko
love your widget but I have a big problem with it: My child views are a pretty big image (360x150) that I use typical preload/sd caching/memory cache. When I call notifyDataSetChanged there's a very small flicker. Using your widget, the flicker is very noticeable, I get it 2-3 times per group expand/collapse, on every call to notifyDataSetChanged(). I guess it's gonna be very hard to get rid of it as I more or less understand why it happens (your trick with dummy views then calling the real notifyDataSet...). Do you think I can alleviate this effect with hardware layers/graphics cache?Locker
@idunnololz, I tried your lib, its working fantastic, Thank you for the great lib. I would like to know that, i have cursor from database, i need the same effect using cursor, with array list it works fine, what to do for cursors?. any help or hint or suggestion from your end. Thank you in advanceCatholicize
HI, Please help me, i have posted a query related to this. #28093604. If you have any suggestion pls guideCatholicize
How can we change arrow position or can we customise arrow icon and want to change its positionSpatter
@Rohit: any luck with the arrow thing?Entire
I am using your library but there is no animation(I am using your java class as well as animated adapter). Can you tell where I am doing wrong?Stelu
@Spatter you should define your own ImageButton for the group layout and in ExpandableListView set android:groupIndicator="@null". Then you after click on that button you can call expandableListView.expand()/collapse() .Fovea
@Tun How is it you came to the conclusion that ExpandableListView does not recycle views? It certainly appears to recycle views, just like a ListView does. It even extends from ListView. Looking at the source for ExpandableListView, there are lots of calls relating to view recycling. I'm just curious why it is that you believe that? I believe it to be inaccurate.Comely
@EricSchlenz If you read the first paragraph of the post carefully you should see that I was talking about a popular method by which people animated listviews at the time of writing. This method can be found here: https://mcmap.net/q/457562/-slide-down-effect-on-expandablelistview. In the method shared in that post, a normal ListView (not an ExpandableListView) is used. The items in the ListView are the groups and the children views of the groups are animated in and out. These child views are the views that are not being recycled. That is what I'm referring to in the first paragraph.Tun
@Tun Are you still working on it? There are 22 open issues at the repo & it was updated a couple of years ago for the last time.Cassowary
@Cassowary whoops. Almost forgot about it. I am not continuing development on it since the Recyclerview makes it obsolete and imo is a way better solution.Tun
C
9
animateLayoutChanges adds auto-animation
<ExpandableListView
        android:animateLayoutChanges="true"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
Capriccioso answered 1/9, 2015 at 7:1 Comment(0)
S
6

@idunnololz solution works great. however i would like to add some code to collapse previously expanded group.

private int previousGroup=-1;

    listView.setOnGroupClickListener(new OnGroupClickListener() {

        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            // We call collapseGroupWithAnimation(int) and
            // expandGroupWithAnimation(int) to animate group 
            // expansion/collapse.
            if (listView.isGroupExpanded(groupPosition)) {
                listView.collapseGroupWithAnimation(groupPosition);
                previousGroup=-1;
            } else {
                listView.expandGroupWithAnimation(groupPosition);
                if(previousGroup!=-1){
                    listView.collapseGroupWithAnimation(previousGroup); 
                }
                previousGroup=groupPosition;
            }

            return true;
        }

    });
Sapota answered 12/6, 2014 at 9:47 Comment(1)
Great. Only thing that is missing is to focus currently selected group. For example, to move the list in a way that current header is at the top of the screen.Koestler
F
0

@idunnololz solution is working great, but I experienced weird behavior with my custom layout for group. The expand operation was not executed properly, the collapse however worked perfect. I imported his test project and it worked just fine, so I realized the problem is with my custom layout. However when I was not able to locate the problem after some investigation, I decided to uncomment these lines of code in his AnimatedExpandListView:

       if (lastGroup && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            return expandGroup(groupPos, true);
        }

which caused the problem (my app is aimed for Android 4.0+).

Fovea answered 14/10, 2015 at 12:53 Comment(1)
To expand the last group with animation i had to change ">=" for "<".Contrabass
T
0

Found this snnipet not remebering where here in Stack Overflow. Have two basic static methods: expand(View v) and collapse(View v). You only have to pass the view you want to hide show.

Note: I Don't recomend pass a view having wrap_content as height. May not work fine.

 public class expand {

    public static void expand(View view) {
        view.setVisibility(View.VISIBLE);

        final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(widthSpec, heightSpec);

        ValueAnimator mAnimator = slideAnimator(view, 0, view.getMeasuredHeight());
        mAnimator.start();
    }


    public static void collapse(final View view) {
        int finalHeight = view.getHeight();

        ValueAnimator mAnimator = slideAnimator(view, finalHeight, 0);

        mAnimator.addListener(new Animator.AnimatorListener() {

            @Override
            public void onAnimationEnd(Animator animator) {
                view.setVisibility(View.GONE);
            }


            @Override
            public void onAnimationStart(Animator animation) {

            }


            @Override
            public void onAnimationCancel(Animator animation) {

            }


            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        mAnimator.start();
    }


    private static ValueAnimator slideAnimator(final View v, int start, int end) {

        ValueAnimator animator = ValueAnimator.ofInt(start, end);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {

                int value = (Integer) valueAnimator.getAnimatedValue();
                ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
                layoutParams.height = value;
                v.setLayoutParams(layoutParams);
            }
        });
        return animator;
    }
}
Torietorii answered 5/4, 2020 at 17:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.