11

Is there a way to add animation when opening an expandable list in Android? I want it so that when the user clicks on the expandable list, it has an animation/effect like I'm opening a sliding drawer.

It moves slow until it is completely opened.

JDJ
  • 4,298
  • 3
  • 25
  • 44
user430926
  • 4,017
  • 13
  • 53
  • 77

5 Answers5

4

I've spent a lot of time searching with no luck. The existing solutions are just not smooth enough - if your layout is something more complex than just 2 buttons, it becomes laggy.

So, I've created my own ListAdapter that caches the whole view into a Bitmap and then performs the animation on the cached view instead of the view itself. It works much faster.

Here it is: https://github.com/dmitry-zaitsev/ExpandableAdapter

The good news is that you don't need to rewrite a bunch of code - just wrap my ExpandableAdapter around your adapter and provide the id of the view that will act like a toggle button and the id of the view that holds the content of the second level:

new ExpandableAdapter(context, yourAdapter, R.id.switch, R.id.holder);

And that is all.

JDJ
  • 4,298
  • 3
  • 25
  • 44
Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
2

I had this same exact problem. And I fixed it once and for all. I open-sourced it to github. https://github.com/tjerkw/Android-SlideExpandableListView

Basically, you include this project dependency with your Android project. And then wrap your ListAdapter into a SlideExpandableListAdapter. The wrapper will then add the slide functionality with animation to your ListView.

Hope it helps you, I'm already using it in two projects.

JDJ
  • 4,298
  • 3
  • 25
  • 44
TjerkW
  • 2,086
  • 21
  • 26
  • I've started to use your library but found that once i start adding complex views, it starts to get sluggish pretty quick. I think it might be because you are animating margins. Any suggestions for getting around the performance issue? – Kent Andersen Mar 11 '13 at 15:33
  • @KentAndersen Actually a pull request was already accepted which should fix this issue. Are you using the latest version? If you are still having problems please it explain it in detail in a github ticket. – TjerkW Mar 11 '13 at 15:37
  • But you have not used ExpandableListView.I want to animate child views of expandable List view not ListView. – anshul Mar 19 '13 at 12:45
  • @TjerkW I'm trying to use your library and it seems to be firing on touch but with no visible change (the item does not expand) log entries looks like: `anim height -48` or `0`; any ideas? – amcc Jun 15 '13 at 11:43
  • This is for expanding a listView cell , not for expanding a group in ExpandableListView – havchr Aug 22 '13 at 11:29
2

I have tried to make this work as well. I have found one solution that works for the childViews. It does not animate the actual expanding of the group though, but animates the child cells as they fill the space the expansion leaves behind.

Edit: There is a bug in collapsing, which will make some cells that should not be hidden, become hidden. This is probably related to View-recycling in the listView. I will will update when I have a solution to this.

Animating with layoutAnimation in setOnGroupClickListener

        mResultList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            if(mResultList.isGroupExpanded(groupPosition)){
                mProgAdap.prepareToCollapseGroup(groupPosition);
                setupLayoutAnimationClose(groupPosition);
                mResultList.requestLayout();
            }else{
                boolean autoScrollToExpandedGroup = false;
                mResultList.expandGroup(groupPosition,autoScrollToExpandedGroup);
                setupLayoutAnimation();
                //*/
            }
            //telling the listView we have handled the group click, and don't want the default actions.
            return true;
        }

        private void setupLayoutAnimation() {
            AnimationSet set = new AnimationSet(true);
            Animation animation = new AlphaAnimation(0.0f, 1.0f);
            animation.setDuration(50);
            set.addAnimation(animation);

            animation = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f, 0.5f, 1.0f);
            animation.setDuration(50);
            set.addAnimation(animation);

            LayoutAnimationController controller = new LayoutAnimationController(set, 0.75f);
            mResultList.setLayoutAnimationListener(null);
            mResultList.setLayoutAnimation(controller);
        }

        private void setupLayoutAnimationClose(final int groupPosition) {
            AnimationSet set = new AnimationSet(true);
            Animation animation = new AlphaAnimation(1.0f, 0.0f);
            animation.setDuration(50);
            animation.setFillAfter(true);
            animation.setFillEnabled(true);
            set.addAnimation(animation);
            animation = new ScaleAnimation(1.0f, 1.0f, 1.0f, 0.0f, 0.5f, 0.0f);
            animation.setDuration(50);
            animation.setFillAfter(true);
            animation.setFillEnabled(true);
            set.addAnimation(animation);
            set.setFillAfter(true);
            set.setFillEnabled(true);
            LayoutAnimationController controller = new LayoutAnimationController(set, 0.75f);
            controller.setOrder(LayoutAnimationController.ORDER_REVERSE);
            mResultList.setLayoutAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    mResultList.collapseGroup(groupPosition);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            mResultList.setLayoutAnimation(controller);
        }
    });

We need more tweaks to make the animation only apply to the actual children of the expanded/collapsed group. Because we can't overload the correct part in the LayoutAnimationController, we need to create a special ViewGroup class. This is the same technique as in , "Can LayoutAnimationController animate only specified Views".

In the ExpandableListViewAdapter, we now need some state handling to allow or ignore animation on items in the list.

    @Override
public void onGroupExpanded(int groupPos){
    super.onGroupExpanded(groupPos);

    int childCount = getChildrenCount(groupPos);
    Log.d("EXPLIST","setting children to be expanded:" + childCount);

    for(int j=0; j < getGroupCount(); j++){
        for(int k=0; k < getChildrenCount(j); k++){
            GoalServiceCell cell =  (GoalServiceCell)getChild(j,k);
            cell.expandAnimState = GoalServiceCell.ExpandAnimState.SHOULD_NOT_ANIMATE;
        }
    }

    for(int i=0; i < childCount; i++){
        GoalServiceCell cell =  (GoalServiceCell)getChild(groupPos,i);
        cell.expandAnimState = GoalServiceCell.ExpandAnimState.SHOULD_START_EXPAND;

    }

}

public void prepareToCollapseGroup(int groupPos){
    int childCount = getChildrenCount(groupPos);
    for(int j=0; j < getGroupCount(); j++){
        for(int k=0; k < getChildrenCount(j); k++){
            GoalServiceCell cell =  (GoalServiceCell)getChild(j,k);
            cell.expandAnimState = GoalServiceCell.ExpandAnimState.SHOULD_NOT_ANIMATE;
        }
    }

    for(int i=0; i < childCount; i++){
        GoalServiceCell cell =  (GoalServiceCell)getChild(groupPos,i);
        cell.expandAnimState = GoalServiceCell.ExpandAnimState.SHOULD_START_COLLAPSIN;

    }
}

@Override
public void onGroupCollapsed(int groupPos){
    super.onGroupCollapsed(groupPos);
    int childCount = getChildrenCount(groupPos);
    for(int i=0; i < childCount; i++){
        GoalServiceCell cell =  (GoalServiceCell)getChild(groupPos,i);
        cell.expandAnimState = GoalServiceCell.ExpandAnimState.SHOULD_NOT_ANIMATE;
    }

}

And in the ViewHolder of the children.

       void expandOrCollapse(GoalServiceCell cell,int position){

        AnimationAverseRelativeLayout hack = (AnimationAverseRelativeLayout)master;
        boolean shouldAnim = cell.expandAnimState == GoalServiceCell.ExpandAnimState.SHOULD_START_EXPAND ||
                             cell.expandAnimState == GoalServiceCell.ExpandAnimState.SHOULD_START_COLLAPSIN;
        hack.setIfShouldAnimate(shouldAnim);

    }

The GroupViews are also contained in a AnimationAverseRelativeLayout. Since I have set "shouldAnimate" to default to false, I don't need to touch them.

Community
  • 1
  • 1
havchr
  • 1,132
  • 1
  • 10
  • 25
  • I had to disable the "collapse" animation, because I never got it working. Is it hooligan when you are only doing the expand animation as well? – havchr Sep 11 '13 at 12:41
  • are you sure this has been set controller.setOrder(LayoutAnimationController.ORDER_REVERSE); and not ORDER_RANDOM ? – havchr Sep 11 '13 at 12:57
  • Did you change `if(mResultList.isGroupExpanded(groupPosition)){ mProgAdap.prepareToCollapseGroup(groupPosition); setupLayoutAnimationClose(groupPosition); mResultList.requestLayout(); }` to `if(mResultList.isGroupExpanded(groupPosition)){ mResultList.collapseGroup(groupPosition); }` to properly disable the collapse animation ? – havchr Sep 12 '13 at 21:04
1

So what I've done is use a regular ListView and then perform the animation in onListItemClick. The animation is similar to what I do at this link: Android animate drop down/up view proper

But only for a portion of the row view. The row view is implemented in following way in xml:

<somelayout>
    <normal>
    </normal>
    <expanded>
    </expanded>
</somelayout>

The normal is used without expand. When expand is activated the expanded is set to visible instead of gone. You need to keep control of setting it to gone again when closing (remember this is set at your convertviews which are recycled).

I can clarify further if needed, it's just quite a lot of code to put in.

Community
  • 1
  • 1
Warpzit
  • 27,966
  • 19
  • 103
  • 155
  • This is for expanding a listView cell , not for expanding a group in ExpandableListView – havchr Aug 22 '13 at 11:29
  • 1
    @havchr correct, but sometimes the content of an expanded item can be put inside a regular item and this solution could be used. – Warpzit Aug 22 '13 at 11:50
0

That's what i did in this situations.

ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(list1, PropertyValuesHolder.ofInt("bottom", currentlistHeight,currentlistHeight*2 ));

What this would do is that the height of the listView will get doubled and it would be animated. If you set the current list height ZERO. this would act like a drawer.

IntelliJ Amiya
  • 74,896
  • 15
  • 165
  • 198
Fahad Ishaque
  • 1,916
  • 1
  • 17
  • 21