Est-il possible d'avoir un bel effet de glissement vers le haut/bas lors de l'expansion/réduction d'un élément d'une ExpandableListView?
Si oui, comment?
Merci d'avance.
Il s'agit donc d'un double complet de this . En bref, j'ai utilisé une liste régulière, créé ma propre vue déroulante, utilisé une animation déroulante personnalisée et voila le succès (regardez le lien pour plus de description).
Edit: Guide étape par étape:
Je crée d'abord le list_row 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>
L'animation de la liste déroulante est la suivante:
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;
}
}
Puis à l'intérieur de mon adaptateur (vous ajouterez bien sûr plus de syntaxe, et si vous souhaitez que la liste déroulante ne se ferme pas lorsqu'elle n'est pas visible dans la liste, vous devez vous en souvenir dans le support avec une sorte de paramètre également):
@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;
}
Ensuite, la magie opère dans vos listes surItemClick:
@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);
}
Dernier commentaire: je ne suis pas sûr que ce soit la meilleure façon de le faire, mais c'est ce qui a fonctionné pour moi.
Edit : Il existe une solution différente de DevBytes sur youtube qui peut être consultée ici .
La réponse signalée par Warpzit est correcte. J'ai utilisé cette approche pour fournir une bibliothèque que vous pouvez facilement intégrer dans votre application sans avoir à savoir comment cela fonctionne réellement:
https://github.com/tjerkw/Android-SlideExpandableListView
Plus d'informations à ce sujet peuvent être lues dans cet article de blog: http://tjerktech.wordpress.com/2012/06/23/an-emerging-Android-ui-pattern-for-contextual-actions/
L'implémentation par Warpzit fonctionne certainement, mais elle est inutilisable si vous devez soutenir des groupes avec beaucoup d'enfants (disons 100) car vous n'utiliserez pas la structure optimisée de ListView (c'est-à-dire la réutilisation/recyclage des vues enfants ). Au lieu de cela, j'ai fini par étendre ExpandableListView pour créer un AnimatedExpandableListView qui utilise la technique que j'ai décrite ici . Ce faisant, AnimatedExpandableListView peut animer l'expansion de groupe tout en offrant des performances optimales. Jetez un oeil.
Le développement/réduction ne fonctionne pas avec le code d'ici: https://github.com/tjerkw/Android-SlideExpandableListView parce que OnItemExpandCollapseListener expandCollapseListener
De AbstractSlideExpandableListAdapter
est null
; la méthode notifiyExpandCollapseListener
est appelée au démarrage de l'animation, mais l'écouteur est null
car: vous avez le ActionSlideExpandableListView
:
ActionSlideExpandableListView lv = (ActionSlideExpandableListView) findViewById(R.id.list_view);
SlideExpandableListAdapter slideAdapter = new SlideExpandableListAdapter(adapter,R.id.expandable_toggle_button, R.id.expandable);
et vous définissez l'adaptateur: lv.setAdapter(slideAdapter);
qui appelle la méthode setAdapter
de SlideExpandableListView
et là une nouvelle instance de SlideExpandableListAdapter
est créée.
J'ai changé comme ceci: setAdapter
méthode de ActionSlideExpandableListView
prend comme paramètre également AbstractSlideExpandableListAdapter.OnItemExpandCollapseListener
Qui est passé à la méthode setAdapter
de SlideExpandableListView
. Là quand je crée SlideExpandableListAdapter
je passe aussi cet écouteur:
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);
}
Une solution simple consiste à utiliser la classe AnimatedExpandableListView
créée par Gary Guo, disponible ici , si vous utilisez déjà la BaseExpandableListAdapter
, qui étend ExpandableListAdapter
. De cette façon, il n'est pas nécessaire d'utiliser une modification d'un ListView
.
Il vous suffit de sous-classer AnimatedExpandableListAdapter
au lieu de BaseExpandableListAdapter
et AnimatedExpandableListView
à la place de ExpandableListView
.
Au lieu de @Override getChildrenCount
utilisez simplement @Override getRealChildrenCount
. Faites de même pour @Override getChildView,
en utilisant @Override getRealChildView
à sa place.
Ensuite, vous utilisez l'animation comme suit:
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;
}
});
Un autre détail est que dans votre fichier xml de mise en page, vous devez référencer AnimatedExpandableListView
et non ExpandableListView
:
<com.your.package.project.class.location.AnimatedExpandableListView
Android:id="@+id/listView"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
L'exemple de code dans le projet et les commentaires sur la classe AnimatedExpandableListView
sont très utiles si vous avez besoin d'aide.