Подтвердить что ты не робот

Эффект сдвига вниз на ExpandableListView

Возможно ли иметь хороший эффект скольжения вверх/вниз при расширении/свертывании элемента ExpandableListView?

Если да, то как?

Спасибо заранее.

4b9b3361

Ответ 1

Итак, это полный дубликат этого. Короче говоря, я использовал обычный список, сделал свой собственный раскрывающийся список, использовал пользовательскую анимацию выпадающего списка и успех воилы (посмотрите на ссылку для большего описания).

Изменить: пошаговое руководство:

Сначала создаю xml list_row:

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

Затем внутри моего адаптера (вы, конечно, добавите больше синтаксиса, и если вы хотите, чтобы раскрывающийся список не закрывался, когда скрылся из списка, вам нужно запомнить это в держателе с каким-то параметром):

@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, которое можно просмотреть здесь.

Ответ 2

Андерсер, отмеченный Варпзитом, верен. Я использовал этот подход для предоставления библиотеки, которую вы можете легко внедрить в свое приложение, не зная, как это работает:

https://github.com/tjerkw/Android-SlideExpandableListView

Подробнее об этом можно прочитать в этом блоге: http://tjerktech.wordpress.com/2012/06/23/an-emerging-android-ui-pattern-for-contextual-actions/

Ответ 3

Реализация Warpzit определенно работает, однако она непригодна, если вам нужно поддерживать группы с большим количеством детей (скажем, 100), поскольку вы не будете использовать оптимизированную структуру ListView (то есть повторное использование/повторное использование детских взглядов). Вместо этого я закончил расширение ExpandableListView для создания AnimatedExpandableListView, в котором используется техника, описанная здесь здесь. Таким образом, AnimatedExpandableListView может оживлять расширение группы, сохраняя при этом максимальную производительность. Посмотрите.

Ответ 4

Развернуть/свернуть с кодом здесь не работает: https://github.com/tjerkw/Android-SlideExpandableListView, потому что OnItemExpandCollapseListener expandCollapseListener from AbstractSlideExpandableListAdapter есть null; при запуске анимации вызывается метод 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);, который вызывает метод setAdapter из SlideExpandableListView, и там создается новый экземпляр SlideExpandableListAdapter.

Я изменил вот так: setAdapter метод из ActionSlideExpandableListView принимает в качестве параметра также AbstractSlideExpandableListAdapter.OnItemExpandCollapseListener, который передается методу setAdapter из SlideExpandableListView. Там когда я создаю 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);
    }

Ответ 5

Простым решением является использование класса AnimatedExpandableListView созданного Гэри Го, доступного здесь, если вы уже используете BaseExpandableListAdapter, который расширяет ExpandableListAdapter. Таким образом, нет необходимости использовать модификацию ListView.

Вам нужно только BaseExpandableListAdapter подкласс AnimatedExpandableListAdapter вместо BaseExpandableListAdapter и AnimatedExpandableListView вместо ExpandableListView.

Вместо @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 очень полезны, если требуется дополнительная помощь.

Ответ 6

Попробуйте это в классе 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;
        }
    });