Возможно ли иметь приятный эффект скольжения вверх/вниз при развертывании/сворачивании элемента ExpandableListView?
Если да, то как?
Заранее спасибо.
Возможно ли иметь приятный эффект скольжения вверх/вниз при развертывании/сворачивании элемента ExpandableListView?
Если да, то как?
Заранее спасибо.
Изменить: пошаговое руководство:
Сначала я создаю 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);
}
Заключительный комментарий: я не уверен, что это лучший способ сделать это, но это сработало для меня.
Изменить. На YouTube есть другое решение от DevBytes, которое можно просмотреть здесь.
postDropHandler
, и не знаю, как это реализовано, если это возможно. Спасибо
- person Alvin Baena; 13.05.2012
onListItemClick
и getView
предназначены для ListView, но здесь мы используем ExpandableListView. Так зачем эти методы ListView? Кроме того, какой у вас ViewHolder
класс?
- person gosr; 23.06.2012
Ответ, указанный Warpzit, верен. Я использовал этот подход, чтобы предоставить библиотеку, которую вы можете легко встроить в свое приложение, не зная, как она на самом деле работает:
https://github.com/tjerkw/Android-SlideExpandableListView
Подробнее об этом можно прочитать в этом сообщении блога: http://tjerktech.wordpress.com/2012/06/23/an-emerging-android-ui-pattern-for-contextual-actions/
Реализация Warpzit определенно работает, однако ее нельзя использовать, если вам нужно поддерживать группы с большим количеством дочерних элементов (скажем, 100), поскольку вы не будете использовать оптимизированную структуру ListView (т. е. повторное использование/переработка дочерних представлений). ). Вместо этого я расширил ExpandableListView, чтобы создать AnimatedExpandableListView, использующий метод, который я описал здесь. Таким образом, AnimatedExpandableListView может анимировать расширение группы, сохраняя при этом максимальную производительность. Посмотрите.
Развертывание/свертывание не работает с кодом отсюда: https://github.com/tjerkw/Android-SlideExpandableListView, потому что OnItemExpandCollapseListener expandCollapseListener
из 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);
}
Если вы уже используя BaseExpandableListAdapter
, который расширяет ExpandableListAdapter
. Таким образом, нет необходимости использовать модификацию ListView
.
Вам нужно только создать подкласс 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
очень полезны, если вам нужна дополнительная помощь.
Попробуйте это в классе 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;
}
});