Friday, July 8, 2011

How to perform entry animations for listview.

From my work related learnings, i would like to share the three ways of doing entry animation for listview elements.


1 - LayoutAnimation Controller
This controller is viewgroup animation controller means, it will apply animation to each of the child added to a view group. When this controller is set to the listview
it animates each listviews items. So that you can have entry animation for listview.

But the drawback is this controller will apply the animation to the listview items only for very first time this layout is drawn/shown. The next time when you do
hide / show the layout, you cannot expect the listview to have its element animated.

2 - So the second approach i tried is over riding the onvisibilitychanged() api of Layout class where your listview is added.check for visibility,if someone made the layout visible then this the right place to perform the animation on each views of listview. Use getChildAt() api to get the listview items and start a animation on each view. So you get a entry animation for listview whenever, this layout is made visible. You can also write a exit animation and start when visibility == INVISIBLE


protected void onVisibilityChanged (View changedView, int visibility)
{
super.onVisibilityChanged(changedView,visibility);
if(visibility == View.VISIBLE) {
startEnterAnimation();
}
}

public void startEnterAnimation() {

Animation animation;
int offsetTime=0;
for(int i=0;i View view = list.getChildAt(i);
animation = new AlphaAnimation(0.0f,1.0f);
animation.setFillAfter(true);
animation.setDuration(100);
animation.setStartOffset( i * 100);

if(view != null)
{
view.startAnimation(animation);
}
}
}


There is also a limitation in the second approach. Scenario is, what happens before u perform the animation on the listview,there is a change in the data, where in you need to call the notifydatasetchanged api, then u wanted to start the entry animation.
But by the time you start performing the entry animation, notifydatasetchagned api would have layouted out the listview elements.

So we need to start the animation as and when listview items are drawn on the screen. how do we do this ??

Third approach solves this issue.

3 - In the adapter's getview() api, as and when we return the convertView to the framework, start the animation that instance by checiking for a flag,
which will be set to true for the first time listview is shown. Approach looks good, but how do u know when the listview has finished calling all its getview
and drawn its child so that you can make the flag false ??. Here is the solution i have tried, as soon as notifydatasetchanged api is called,

UI thread will have tasks in its messagequeue to perform getview for the count returned by getCount() adapter.so after notifydatasetchanged api is called,
get the handler of listview(which is nothing but UI thread handler) and post a runnable to make the boolean flag false, So that this runnable will be
executed after all the getview() calls are finished for all its child elements. So We know when to make the boolean false at the right instance.


protected void onVisibilityChanged (View changedView, int visibility)
{
super.onVisibilityChanged(changedView,visibility);
if(visibility == View.VISIBLE) {
// Suppose your data is upadted.
mListAdapter.notifyDataSetChanged();
isInitialLayout = true;
listview.getHandler().post(new Runnable() {
public void run() {
isInitialLayout = false;
}
});
}
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listview_item, null);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.title);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

holder.title.setText(mData.get(position));

/* Start the animation for this item for the position */
if(isInitialLayout == true ) {
Animation animation = new AlphaAnimation(0.0f,1.0f);
animation.setFillAfter(true);
animation.setDuration(100);
animation.setStartOffset(position * 100);
convertView.startAnimation(animation);
}

return convertView;
}


If there are any other ways of performing the entry animation for a listview in a layout, please let me/everybody know :)