使用动画的 Android ExpandableListView

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20627113/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:10:20  来源:igfitidea点击:

Android ExpandableListView using animation

androidanimationexpandablelistview

提问by Lê Minh Quy?n

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 ?

我想在 onclick parent 时为孩子添加动画幻灯片。那我该怎么办?

回答by idunnololz

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 RecyclerViewthat 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.

自从我写这个答案以来已经有一段时间了。从那时起,发生了很多变化。最大的变化是引入RecyclerView动画列表或网格变得容易。RecyclerView如果可以,我强烈建议切换到s。对于那些不能解决问题的人,我会看看我能做些什么来修复我的库的错误。



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.

我实际上不喜欢动画 ExpandableListView 的流行实现,它只是使用带有展开动画的 ListView,因为在我的用例中,我的每个组都有很多孩子,因此使用普通 ListView 作为孩子是不可行的视图不会被回收,内存使用量会很大,性能很差。相反,我采用了一种更困难但更具可扩展性和灵活性的方法。

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:

我扩展了 ExpandableListView 类并覆盖了 onCollapse 和 onExpand 函数,我还创建了一个名为 AnimatedExpandableListAdapter 的 BaseExpandableListAdapter 的子类。在适配器内部,我覆盖了 getChildView 函数并将该函数设为 final,以便该函数不能再次被覆盖。相反,我为子类提供了另一个名为 getRealChildView 的函数来覆盖以提供真正的子视图。然后我向类添加了一个动画标志,如果设置了动画标志,则让 getChildView 返回一个虚拟视图,如果未设置标志,则返回真实视图。现在使用舞台布景,我对 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.
  1. 在适配器中设置动画标志并告诉适配器哪个组正在扩展。
  2. 调用notifyDataSetChanged()(强制适配器调用getChildView()屏幕上的所有视图)。
  3. 然后适配器(在动画模式下)将为初始高度为 0 的扩展组创建一个虚拟视图。然后适配器将获取真正的子视图并将这些视图传递给虚拟视图。
  4. 然后虚拟视图将开始在它自己的onDraw()函数中绘制真正的子视图。
  5. 适配器将启动一个动画循环,该循环将扩展虚拟视图直到其大小合适。它还将设置一个动画侦听器,以便在动画完成后清除动画标志并调用notifyDataSetChanged()

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.

最后,完成所有这些后,我不仅能够获得所需的动画效果,还能够获得所需的性能,因为这种方法适用于拥有 100 多个孩子的组。

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.

对于折叠动画,需要做更多的工作来设置和运行这一切。特别是,当您覆盖 onCollapse 时,您不想调用父级的函数,因为它会立即折叠组,让您没有机会播放动画。相反,您希望在折叠动画结束时调用 super.onCollapse。

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/

这个周末我花了一些时间来重写我的这个 AnimatedExpandableListView 的实现,我在这里发布了一个示例用法的源代码:https: //github.com/idunnololz/AnimatedExpandableListView/

回答by Hiren Dabhi

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

@idunnololz 解决方案效果很好。但是我想添加一些代码来折叠以前扩展的组。

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;
        }

    });

回答by KursikS

animateLayoutChanges adds auto-animation
<ExpandableListView
        android:animateLayoutChanges="true"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>

回答by vanomart

@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:

@idunnololz 解决方案效果很好,但我的自定义组布局遇到了奇怪的行为。展开操作没有正确执行,但折叠工作完美。我导入了他的测试项目,效果很好,所以我意识到问题出在我的自定义布局上。然而,当我在经过一番调查后无法找到问题时,我决定在他的 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+).

这导致了问题(我的应用程序针对 Android 4.0+)。

回答by Jorge A

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.

发现这个 snipet 不记得在 Stack Overflow 的什么地方。有两个基本的静态方法:expand(View v)collapse(View v)。你只需要通过你想隐藏的视图显示。

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

注意:我不建议传递具有 wrap_content 作为高度的视图。可能无法正常工作。

 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;
    }
}