If you ever wrote an Android app, there’s a 90% chance that you used the ListView component at least once.
In the app that I wrote, I needed to give the user some options he can perform on each one of the list items. I thought about sliding a toolbar from the side, which will replace the list item, like they do on twitter, but it’s less convenient due to the fact that you hide the data that list item presents. A better approach, for me at least, was to slide out a toolbar. In that way – you can see the list item and get the option to perform some commands on it. And basically getting from this:
To this:
The problem with that approach is that the ListView component doesn’t have any built-in option for that, nor an easy way to add new views to the list, and not to mention adding new views with a cool animation! To do that, I had to pull some tricks up my sleeves.
First, I’ve added another view for the list item’s layout, just below the one handling the data itself (In this case – the text item), and I gave it a negative bottom margin as the size of its height. That’s a neat trick to hide the toolbar view completely!
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/title" ... android:layout_height="wrap_content"/> <!--***********************--> <!--*** TOOLBAR LAYOUT ****--> <!--***********************--> <LinearLayout android:id="@+id/toolbar" android:layout_marginBottom="-50dip" android:visibility="gone" android:layout_height="50dip" android:layout_width="fill_parent"> <Button android:id="@+id/doSomething1" android:layout_height="50dip" ... android:text="Harder"/> <Button android:id="@+id/doSomething2" android:layout_height="50dip" ... android:text="Better"/> <Button android:id="@+id/doSomething3" android:layout_height="50dip" ... android:text="Faster"/> <Button android:id="@+id/doSomething4" android:layout_height="50dip" ... android:text="Stronger"/> </LinearLayout> </LinearLayout>
Secondly, I wrote an animation class that changes the value of that bottom margin, getting the toolbar revealed/hidden.
public class ExpandAnimation extends Animation { ... @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); if (interpolatedTime < 1.0f) { // Calculating the new bottom margin, and setting it mViewLayoutParams.bottomMargin = mMarginStart + (int) ((mMarginEnd - mMarginStart) * interpolatedTime); // Invalidating the layout, making us seeing the changes we made mAnimatedView.requestLayout(); } ... } }
And finally, I got the ListView’s OnItemClickListener to toggle the toolbar state, using the above animation class.
// Creating an item click listener, to open/close our toolbar for each item list.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, final View view, int position, long id) { View toolbar = view.findViewById(R.id.toolbar); // Creating the expand animation for the item ExpandAnimation expandAni = new ExpandAnimation(toolbar, 500); // Start the animation on the toolbar toolbar.startAnimation(expandAni); } });
Combining all together, got me a cool ListView, with a sweet animation for opening/closing the toolbar!
This is just a demo for my ExpandAnimation, There’s a lot more you can do. For instance – if the list item is on the bottom of the list, we can scroll the list and then show the toolbar. It’ll give a better user experience than having to scroll down manually. You can also use it for any view, not only ListView items. For instance, animating the visibility of some Activity’s title.
As always, the full source code is on my GitHub.
If you have any ideas how to improve this method – please send me feedback here!
I have used this code. Thanks a lot. But I want to do the following:
When the user click the second item to expand. The first item that have been clicked will close. How do I do that?
Ok I did it like this:
if(expanded != null)
{
View close = expanded.findViewById(R.id.toolbar);
// Creating the expand animation for the item
ExpandAnimation expandAni_close = new ExpandAnimation(close, 500);
// Start the animation on the toolbar
close.startAnimation(expandAni_close);
}
View toolbar = v.findViewById(R.id.toolbar);
// Creating the expand animation for the item
ExpandAnimation expandAni = new ExpandAnimation(toolbar, 500);
// Start the animation on the toolbar
toolbar.startAnimation(expandAni);
expanded = v;
Hey,
There are several ways to accomplish that. I’m not quite sure how did you implement it, could you please elaborate on your solution?
Udi.
I think you would need something like this:
if (PreviousToolbar!=null) {
ExpandAnimation tempExpandAni = new ExpandAnimation(PreviousToolbar, 500);
PreviousToolbar.startAnimation(tempExpandAni);
}
View toolbar = view.findViewById(R.id.toolbar);
// Creating the expand animation for the item
ExpandAnimation expandAni = new ExpandAnimation(toolbar, 500);
// Start the animation on the toolbar
toolbar.startAnimation(expandAni);
PreviousToolbar = toolbar;
Fix on my previous post (bug when the same item was clicked):
View toolbar = view.findViewById(R.id.toolbar);
boolean SameItemClicked = false;
if ((PreviousToolbar==toolbar)) {
PreviousToolbar = null;
SameItemClicked = true;
}
if (PreviousToolbar!=null){
ExpandAnimation tempExpandAni = new ExpandAnimation(PreviousToolbar, 500);
PreviousToolbar.startAnimation(tempExpandAni);
}
// Creating the expand animation for the item
ExpandAnimation expandAni = new ExpandAnimation(toolbar, 500);
// Start the animation on the toolbar
toolbar.startAnimation(expandAni);
if (SameItemClicked) {
PreviousToolbar = null;
}
else {
PreviousToolbar = toolbar;
}
How to define the PreiviousToolbar?
But when I click quickly.The previous tool bar will no close. If you free,thanks for you give some tips.
Ok. This is simple and great. I was trying to use this in my other app. But the ToolBar size is not always the same. You use 50dp and -50dp for bottomMargin. Can we set height=”wrap_content” and still do something like this?
Have you or someone else solved this problem?
There’s a problem with supporting unfixed sizes. I’ve tried some methods in the past to overcome this, but couldn’t. If you’ll find an elegant way – please let us know, or even fix and send a pull request.
Awsome, thanks for the tutorial. Also jsmit thanks for the Autoclose previous item code 😉
I noticed one improvement for you (in getView()):
Change:
: ((LinearLayout.LayoutParams) toolbar.getLayoutParams()).bottomMargin = -50;
To something like this:
: LinearLayout.LayoutParams params = ((LinearLayout.LayoutParams) toolbar.getLayoutParams());
: if(!mIsOriginalBottomMarginStored)
: {
: mOriginalBottomMargin = params.bottomMargin; // in px
: mIsOriginalBottomMarginStored= true;
: }
: params.bottomMargin = mOriginalBottomMargin; // in px
bottomMargin value is in pixels and -50px is not equal to -50dip which was defined in the xml 😉
This fixes noticeable animation jump (depending on the device pixel density) when an item is expanded AT THE 1ST TIME.
Little bit more hackish code for you 😉
Hi! I just use your demo.Thanks a lot.But there’s a bug.When I scroll the listview,the opened item will close. If you free, could you tell me some tips? Thanks.
Hi,
great tutorial.
@jsmit – I am trying to use somewhat modified your code, in a way when you click on the same item, the toolbar should close.
But, with this modifications, if I click on the same (opened toolbar) item = the code inside the onItemClick does not executes at all. Only if I click on different ListItem row.
public void onItemClick(AdapterView parent, final View view, int position, long id) {
Log.e(“my”, “entered onClick listItem”);
View toolbar = view.findViewById(R.id.kursna_lista_toolbar);
if ((previousToolbar==toolbar)) {
previousToolbar = null;
sameItemClicked = true;
}
if (previousToolbar!=null){
Log.e(“my”, “closing some toolbar”);
novotek.android.helper.ExpandAnimation tempExpandAni = new novotek.android.helper.ExpandAnimation(previousToolbar, 500);
previousToolbar.startAnimation(tempExpandAni);
}
if (sameItemClicked) {
previousToolbar = null;
}
else {
previousToolbar = toolbar;
}
if (!sameItemClicked){
Log.e(“my”, “start OPENING new”);
novotek.android.helper.ExpandAnimation expandAni2 = new novotek.android.helper.ExpandAnimation(toolbar, 200);
toolbar.startAnimation(expandAni2);
previousToolbar = toolbar;
}
else {
Log.e(“my”, “no need to open new – was closing”);
previousToolbar = null;
}
}
Any ideas?
Just one correction to be precise:
if ((previousToolbar==toolbar)) {
//previousToolbar = null;
sameItemClicked = true;
}
Anyway, this does not effect on the problem explained…
Hi everybody,
i’ve been reading the post for a while, hoping someone faces the same problem, but no one has posted the solution for this issue until now.
Does someone has the solution for this?
Thanks in advance.
Hi Udinic, I wanted to solve this problem too. Your blog helped me getting started. Since i needed it for two projects, i decided to make a separate project to provide this functionality.
I open sources that project on github so anybody could use it in their android app.
https://github.com/Udinic/SmallExamples/tree/master/ExpandAnimationExample
I still have some open issues like the ones your mentioning (animate down when opening the last item)
Thanks a lot for your blog item! I mentioned you in the acknowledgements!
oh sorry, the link is wrong, i meant this link: https://github.com/tjerkw/Android-SlideExpandableListView
Its based on this initial article. So you are a father of this project too 🙂
So, should we continue posting questions or issues on the github project website?
Questions or issues can be posted here. If you have a fix/addition to the code – you can fork the project on Github and send me a pull request.
Sure, but, maybe @jsmit is not following here any more… And I somehow feel he can help resolve the issue 🙂
i used cursor adapter and included View binder in your animation… but having click issue(gets clicked in reverse order) help me……..
Man, have you solved the problem of clicking the last item, and let the expanded toolbar show?
The solution if not good, while it works.
final int temi=pos;
getListView().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
final float densityMultiplier = getResources().getDisplayMetrics().density;
float si_=(getListView().getBottom()-getListView().getTop())/densityMultiplier;
// Log.d(“hi”, Integer.toString((int)si_));
getListView().setSelectionFromTop(temi-1,(int)si_-100);
// getListView().scrollTo(0, 100);
}
},350);
I don’t understand how this solution is applied, but need help with the problem of the last item going of the screen. Can you please advise on where this code is applied?
Wow! This code is very helpful.
Thanks!!!
Wow….this code is a lifesaver. I am having two problems with it so far though:
1. When tapping on an item twice quickly, the item will begin to open, but then close again and refuses to fully open afterwards.
2. I need to find a solution for keeping the items open after they scroll off the screen.
Any help is greatly appreciated.
this works great except I haven’t figured out how to prevent the buttons from flashing when the tool bar is collapsed. The buttons momentarily flash it’s own orange color when the toolbar is collapsed. I can’t seem to resolve this issue. Any help?
Pingback: Explanation of the getView() method of an ArrayAdapter | Jisku.com - Developers Network
To help those in need, I solved the double click issue I noted by adding the following to my onItemClick method:
view.setClickable(true); //Disables click on the view again (until the animation is finished)
View toolbar = view.findViewById(R.id.toolbar);
if(toolbar != null){
// Creating the expand animation for the item
ExpandAnimation expandAni = new ExpandAnimation(toolbar, 500);
expandAni.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
view.setClickable(false); //Re-enables the view after animation is complete
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
}
});
toolbar.startAnimation(expandAni);
Hope this helps others.
Josh
This is awesome thanks man keep up the good work it worked for me
Excuse me for my english, I’m from Brazil and my English is not strong yet, I’m looking at your code but was wondering if anyone managed to solve the problem of keeping the item visible after scroll the listview, and if the issue focus Button clicked when the item is collected. I appreciate any help and I want to thank the excellent work.
Pingback: Expandable Toolbar in Android ListView – Kristian Dorland
Hey..thanks for the animation source! Is it possible by any chance to make the animation smoother? Right now what I’m getting is a kind-of jittery step-by-step animation.
Pingback: TextView setText does not work in LinearLayout : Android Community - For Application Development
I solved some of the problems mentioned in the comments the following way:
1. Keeping the item expanded when not visible
2. Noticeable clip on first animation run because of px | dp difference
– removing all code from getView() in the BaseAdapter so no initialization is done when view is inflated or recycled
– this will cause a positive margin problem and the animation will look bad on first run, therefore
– use the following code after you get the Layout Params in the ExpandableAnimation consturctor
if(mViewLayoutParams.bottomMargin > 0)
mViewLayoutParams.bottomMargin = mViewLayoutParams.bottomMargin*(-1);
Now you should have a smooth running animation each time you press an item.
Use with previous comments for collapsing all expanded views on new click and disabling the click while the animation is still running and you should have pretty decent code.
Hi,
Please apply all the bug fixes to existing code.
Thanks
Hey,
A more organized way will be to fork my repository on GitHub and send me a pull request with all the suggested fixes.
Hi,
Need to add close button when expand the item and it should close when click on the same button. Please give some idea to do that.
Thanks
hi
Thanks for your blog.This is very useful for me.But there is a problem.Actually i can’t view all the listed items when expanding an item.Suppose,if i need to view 10 items while expanding a single item,here i can only view 7 items.What i need to do?
Hi,
I found your library today and it seems very helpful. But I have one question, the real ExpandableListView opens a list for each parent, so I tried to do the same with the SlideExpandableListView. When the Button for epanding the list it pressed, it should display a ListView. But I am confronted with two problems:
1) It seems that the measured height is always 48px for the ListView, so that only the first child can be displayed, only when I set the height fixed on e.g. 150 dp some items will be displayed, but there comes the second problem
2) I tried to scroll in the list view but I have no chance to do this
Did I something wrong or do I have to set a fixed size for all childrens? I hope you have a solution for my little problem 🙂
Pingback: Explanation of the getView() method of an ArrayAdapter - Tech Forum Network
Pingback: Issue trying to make a list expandable in android | BlogoSfera
Hi,
this animation lib is great and helpful for my project..
i’ve added an arrow(both up and down arrows) to one imageview for each listitem, so i should make ‘up’ arrow visible when dropped down. similarly other case..
how to acheive this?? please suggest any way..
Hi,
This animation looks great and very helpful for my project.Thanks for your valuable post at the same time ,shared the code.Rocks…..
Hi Udinic,
Thanks for this excellent post, your code is very useful for me. I need a clarification for this… I need to close the previous opened layout when the second item click(as per Said Tahsin Dane said). i go through this entire discussion unfortunately i can’t get clear, can you please tell me which code and where i place the code to achieve my need. So far you discussed about previousToolbar, frankly don’t know where i need to initialize. Please send the reply as soon as possible.
create a variable to store the currently selected item. On click, check to see if a new item is being selected and, if so, run the animation on the selected item and the previous item. if you have any questions about the code below, respond to my comment.
@Override
public void onClick(View arg0) {
View friendsOptions = arg0.findViewById(R.id.friends_options_container);
ExpandAnimation expandAni = new ExpandAnimation(friendsOptions, 500);
friendsOptions.startAnimation(expandAni);
if (mSelected != null) {
if (mSelected == arg0) {
friendsOptions.startAnimation(expandAni);
mSelected = null;
}
else {
View friendsOptionsOld = mSelected.findViewById(R.id.friends_options_container);
ExpandAnimation expandAniOld = new ExpandAnimation(friendsOptionsOld, 500);
friendsOptionsOld.startAnimation(expandAniOld);
friendsOptions.startAnimation(expandAni);
mSelected = arg0;
}
}
else {
mSelected = arg0;
friendsOptions.startAnimation(expandAni);
}
Hey,
I was hoping you might be able to point me into the right direction for this issue that I am running into.
When I click an item in my list view, I can see the 4 buttons appear, but they sort of appear over the list view item, sort of like there is no expanding animation happening.
I am using a linear layout for the toolbar like you have outlined, but in my custom list view item xml file, I am using a relative layout if that is of any use.
Thanks!
Also, if it is of any importance, I am displaying my list inside of a fragment
I am using your solution in a slightly adapted manner: as some commenters above, i have implemented a “one-item-only” thing and the toolbar ist also completely replacing the upper view. But one thing is annoying: The opened list item closes itself automatically if it gets out of sight. Can i prevent this behaviour?
I need a help from you regarding Calendar App.In Cal App they did a calendar in a wonderful way.By default the current week is displaying in the calendar, when a user drag a finger it will expand into the month view like vice versa. Please have a look and help me out.
Hi..
I have one issue when i open the any row and scroll listview through the end and coming back to the opened row then that row is gets closed.i want the row to be open if i scroll through the item how to fix this. and also i want only one item to be open at a time.
Hi,
Thanks for your code, it helped me a lot!! I’d like to do something like that but with custom listview item, do you have any idea on how to process?
i have a question, im trying to add buttom on runtime not static whoe can i do that?
Pingback: Animation for expandableListView – w3toppers.com