Wednesday, August 22, 2012

Android: Layout Transition CHANGING animation for API level 11-15

In android, Layout Transition is introduced from API level 11. By enabling layout transition application can get predefine platform animations for following changes.


1. APPEARING - animation that runs on those items that are appearing in the container.
2. CHANGE_APPEARING - animation that runs on those items that are changing due to a new item appearing in the container.
3. CHANGE_DISAPPEARING - animation that runs on those items that are changing due to an item disappearing from the container.
4. DISAPPEARING - animation that runs on those items that are disappearing from the container.


These are fast, tested, easy to incorporate with our applications.

CHANGING animation was not added to API at that time, but it has been added now in API level 16.

CHANGING - animation that runs on those items that are changing due to a layout change not caused by items being added to or removed from the container.

So incase if we want to support devices in API level 11, it is good to have some work around, and it should be able to get platform animation in other decides more than API level 16.

I am trying to make a utility for this to simply incorporate this and here I have two options.

1. Simple One time registration/setup, but this will not support scroll changes or any other property changes. only support left, top, right and bottom.

First we register on layout change listener for both layout and its children. So as soon as there is a layout change in these views the callback will be called.


private static void setupChangeAnimationOneTime(final View view) {
...
final View.OnLayoutChangeListener onLayoutChangeListener = new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
...

}
};
view.addOnLayoutChangeListener(onLayoutChangeListener);
...
}


Here, create a object animator, with old and new values.


PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", oldLeft, left);
PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", oldTop, top);
PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", oldRight, right);
PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", oldBottom, bottom);
ObjectAnimator changeAnimator = ObjectAnimator.ofPropertyValuesHolder((Object)view,
                pvhLeft, pvhTop, pvhRight, pvhBottom);
changeAnimator.setDuration(600);


Start the animator.


changeAnimator.start();


In this option, we don't know other property of the view. so we cannot animate them.

2. Advanced approach, setup in each layout changes, for this the root layout needed to be a custom layout, where the onlayout method should be overridden or need to find a proper action where the layout change is happened (example onClick etc).


First create an animator with properties you wanted animate. Use null for target object and some arbitrary values, which will be changed in setup.



public static ObjectAnimator getDefaultChangeAnimator(){
if(sDefaultChangeAnimator==null){
        PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
        PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
        PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
        PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
        PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
        PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
        sDefaultChangeAnimator = ObjectAnimator.ofPropertyValuesHolder((Object)null,
                pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
        sDefaultChangeAnimator.setDuration(600);
}
return sDefaultChangeAnimator;
}


Now the animator has to be setup just before the layout change of the layout and it's children. First start state will be setup and layout change listener wil be registered.


private static void setupChangeAnimation(final View view) {

changeAnimator.setTarget(view);

changeAnimator.setupStartValues();
final View.OnLayoutChangeListener onLayoutChangeListener = new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
...
changeAnimator.setupEndValues();
changeAnimator.start();

};
changeAnimator.addListener(animatorListener);
view.addOnLayoutChangeListener(onLayoutChangeListener);
...
}

after layout change, inside the on layout change callback the end state of the animator will be setup and start immediately.

Here  we need to make sure setupChangeAnimation called just before the layout change. For this we have two possible place,
1. inside the onlayout call of animation enabled layout.
2. posible action code that make layout changes. (example onClick())


public class DemoLinearLayout extends LinearLayout {
...
protected void onLayout(boolean changed, int l, int t, int r, int b) {
LayoutTransitionUtil.checkAndSetupChangeAnimationForAllChild(this);
super.onLayout(changed, l, t, r, b);
}
}


@Override
public void onClick(View view) {
...
LayoutTransitionUtil.setupChangeAnimation((ViewGroup)findViewById(R.id.frm_linear_04));
}



Here I am uploading the sample source code for reference.

2 comments:

Aaron Sarazan said...

Thanks for writing this post. I just stumbled across it and it's a life-saver.

Well done!

Aaron Sarazan said...
This comment has been removed by the author.