可扩展列表 ExpandableListView 的下拉效果

32

当展开/折叠可扩展列表项时,是否可以有一个漂亮的上下滑动效果?

如果可以,该如何实现?

提前感谢。


也许有一种简单的方法可以减缓ExpandableListView上展开效果的速度,难道有吗??? - thomaus
1
我是唯一一个想知道如何实现这个的人吗? - thomaus
2
不,我没有。我猜我们得等待Android实现一个漂亮的小部件。;-) - thomaus
1
你有没有找到可扩展列表视图的解决方案? - Alex Chengalan
6个回答

29
这是this的完全副本。简而言之,我使用了常规列表,创建了自己的下拉视图,使用了自定义的下拉动画,成功了(请查看链接获取更多描述)。
编辑:步骤指南:
首先,我创建了XML列表行:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/row_parent"
    android:orientation="vertical">
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/row_simple_parent"
        >
    <View
        android:id="@+id/row_simple_parent_invis_cover"
        android:visibility="gone"
        android:layout_height="something that fills out your content"
        android:layout_width="match_parent"
        android:background="@android:color/transparent"/>
    </RelativeLayout>

    <!-- Dropdown -->
    <RelativeLayout 
        android:id="@+id/row_dropdown"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">
    </RelativeLayout>
</LinearLayout>

下拉菜单的动画如下:
import android.app.Activity;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * Class for handling collapse and expand animations.
 * @author Esben Gaarsmand
 *
 */
public class ExpandCollapseAnimation extends Animation {
    private View mAnimatedView;
    private int mEndHeight;
    private int mStartVisibility;

    /**
     * Initializes expand collapse animation. If the passed view is invisible/gone the animation will be a drop down, 
     * if it is visible the animation will be collapse from bottom
     * @param view The view to animate
     * @param duration
     */ 
    public ExpandCollapseAnimation(View view, int duration) {
        setDuration(duration);
        mAnimatedView = view;
        mEndHeight = mAnimatedView.getLayoutParams().height;
        mStartVisibility = mAnimatedView.getVisibility();
        if(mStartVisibility == View.GONE || mStartVisibility == View.INVISIBLE) {
            mAnimatedView.setVisibility(View.VISIBLE);
            mAnimatedView.getLayoutParams().height = 0;
        }
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        if (interpolatedTime < 1.0f) {
            if(mStartVisibility == View.GONE || mStartVisibility == View.INVISIBLE) {
                mAnimatedView.getLayoutParams().height = (int) (mEndHeight * interpolatedTime);
            } else {
                mAnimatedView.getLayoutParams().height = mEndHeight - (int) (mEndHeight * interpolatedTime);
            }
            mAnimatedView.requestLayout();
        } else {
            if(mStartVisibility == View.GONE || mStartVisibility == View.INVISIBLE) {
                mAnimatedView.getLayoutParams().height = mEndHeight;
                mAnimatedView.requestLayout();
            } else {
                mAnimatedView.getLayoutParams().height = 0;
                mAnimatedView.setVisibility(View.GONE);
                mAnimatedView.requestLayout();
                mAnimatedView.getLayoutParams().height = mEndHeight;
            }
        }
    }

    /**
     * This methode can be used to calculate the height and set itm for views with wrap_content as height. 
     * This should be done before ExpandCollapseAnimation is created.
     * @param activity
     * @param view
     */
    public static void setHeightForWrapContent(Activity activity, View view) {
        DisplayMetrics metrics = new DisplayMetrics();
        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

        int screenWidth = metrics.widthPixels;

        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);

        view.measure(widthMeasureSpec, heightMeasureSpec);
        int height = view.getMeasuredHeight();
        view.getLayoutParams().height = height;
    }
}

然后在我的适配器中(您当然可以添加更多的语法,如果您希望下拉菜单在列表中不可见时不关闭,则还需要在holder中使用某种参数来记住此设置):
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;
    if(convertView == null) {
        // setup holder
        holder = new ViewHolder();
        convertView = mInflater.inflate(R.layout.list_row, null);
        holder.mDropDown = convertView.findViewById(R.id.row_dropdown);
        convertView.setTag(holder);
    } else {
        // get existing row view
        holder = (ViewHolder) convertView.getTag();
    }
    holder.mDropDown.setVisibility(View.GONE);
    return convertView;
}

然后,在您的列表 onItemClick 中发生了魔法:

@Override
public void onListItemClick(ListView list, View view, int position, long id) {
    final ListItem item = (ListItem) list.getAdapter().getItem(position);
    // set dropdown data
    ViewHolder holder = (ViewHolder) view.getTag();
    final View dropDown = holder.mDropDown;

    // set click close on top part of view, this is so you can click the view
    // and it can close or whatever, if you start to add buttons etc. you'll loose
    // the ability to click the view until you set the dropdown view to gone again.
    final View simpleView = view.findViewById(R.id.row_simple_parent_invis_cover);
    simpleView.setVisibility(View.VISIBLE);

    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == 0) {
                // first we measure height, we need to do this because we used wrap_content
                // if we use a fixed height we could just pass that in px.
                ExpandCollapseAnimation.setHeightForWrapContent(getActivity(), dropDown);
                ExpandCollapseAnimation expandAni = new ExpandCollapseAnimation(dropDown, DROP_DOWN_TIME);
                dropDown.startAnimation(expandAni);

                Message newMsg = new Message();

            } else if(msg.what == 1) {
                ExpandCollapseAnimation expandAni = new ExpandCollapseAnimation(dropDown, DROP_DOWN_TIME);
                dropDown.startAnimation(expandAni);

                simpleView.setOnClickListener(null);
                simpleView.setVisibility(View.GONE);
            }
        }
    };

    simpleView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            handler.sendEmptyMessage(1);
        }
    });

    // start drop down animation
    handler.sendEmptyMessage(0);
}

最终评论:我不确定这是否是最佳方法,但这是对我有效的方法。
编辑:DevBytes在YouTube上有一个不同的解决方案,可以在这里查看。

1
@Kermia 我稍后会这样做。不确定是在这里/其他帖子中发布还是两者都要 =/。既然你要求了,我想我会在这里发布。 - Warpzit
1
这并不能帮助我们在可扩展列表视图中实现组的展开动画。这个解决方案很好,但是它回答了一个不同的问题。 - hadi
我尝试了一下,但什么也没发生,最后我找到了这个不可思议的例子 https://github.com/Udinic/SmallExamples/tree/master/ExpandAnimationExample ,解决了我的问题...简短而简单...顺便说一句,非常感谢@Warpzit - Rishabh Srivastava
有没有想法如何使“下拉菜单不会在视线之外关闭”? - user755
@user2439755 我已经解释过了:然后在我的适配器内部(当然,您需要添加更多的语法,如果您希望下拉列表在列表中不可见时不关闭,则还需要在holder中使用某种参数来记住这一点)。 - Warpzit
显示剩余8条评论

5

3
Warpzit的实现确实可行,但如果需要支持具有大量子项(比如100个)的组,则无法使用ListView的优化结构(即子视图的重用/回收)。相反,我扩展了ExpandableListView以创建AnimatedExpandableListView,该列表使用我在这里描述的技术。通过这样做,AnimatedExpandableListView可以在保持顶级性能的同时动画展开组。请查看一下。

1

这里的代码https://github.com/tjerkw/Android-SlideExpandableListView中,展开/折叠不起作用,因为AbstractSlideExpandableListAdapter中的OnItemExpandCollapseListener expandCollapseListenernull。当动画开始时调用了方法notifiyExpandCollapseListener,但监听器是null,因为你有ActionSlideExpandableListView

ActionSlideExpandableListView lv = (ActionSlideExpandableListView) findViewById(R.id.list_view);
SlideExpandableListAdapter slideAdapter = new SlideExpandableListAdapter(adapter,R.id.expandable_toggle_button, R.id.expandable);

你需要设置适配器: lv.setAdapter(slideAdapter); 这会调用SlideExpandableListViewsetAdapter方法并创建一个新的SlideExpandableListAdapter实例。

我做了以下更改: ActionSlideExpandableListViewsetAdapter方法也接受AbstractSlideExpandableListAdapter.OnItemExpandCollapseListener作为参数,该参数会传递给SlideExpandableListViewsetAdapter方法。在这里我创建了SlideExpandableListAdapter时也传递了这个监听器:

 public void setAdapter(ListAdapter adapter, AbstractSlideExpandableListAdapter.OnItemExpandCollapseListener expandCollapseListener) {
        this.adapter = new SlideExpandableListAdapter(adapter, expandCollapseListener);
        super.setAdapter(this.adapter);
    }

    public SlideExpandableListAdapter(ListAdapter wrapped, OnItemExpandCollapseListener expandCollapseListener) {
        this(wrapped, R.id.expandable_toggle_button, R.id.expandable);
        setItemExpandCollapseListener(expandCollapseListener);
    }

0
一个简单的解决方案是使用Gary Guo创建的类AnimatedExpandableListView,如果您已经在使用扩展BaseExpandableListAdapter,它继承了ExpandableListAdapter,则可以在这里找到该类。这样就不需要使用ListView的修改版本。
您只需要子类化AnimatedExpandableListAdapter而不是BaseExpandableListAdapter,并在ExpandableListView的位置上使用AnimatedExpandableListView
不要再使用@Override getChildrenCount,而是使用@Override getRealChildrenCount。对于@Override getChildView,请使用@Override getRealChildView
然后您可以像这样使用动画:
    expandableListView.setOnGroupClickListener(new AnimatedExpandableListView.OnGroupClickListener() {
        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            if (expandableListView.isGroupExpanded(groupPosition))
                expandableListView.collapseGroupWithAnimation(groupPosition);
            else
                expandableListView.expandGroupWithAnimation(groupPosition);
            return true;
        }

    });

另一个细节是,在您的布局xml文件中,您需要引用AnimatedExpandableListView而不是ExpandableListView:

<com.your.package.project.class.location.AnimatedExpandableListView
    android:id="@+id/listView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

如果需要进一步帮助,项目中的示例代码和对AnimatedExpandableListView类的注释非常有用。


-3

在Java类中尝试这个

       expListView.setAdapter(listAdapter);
    expListView.setOnGroupExpandListener(new OnGroupExpandListener() {
        int previousGroup = -1;

        @Override
        public void onGroupExpand(int groupPosition) {
            if(groupPosition != previousGroup)
                expListView.collapseGroup(previousGroup);
            previousGroup = groupPosition;
        }
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接