This is the slide of the conference gave at the DroidCon Greece 2015 by Mathias Seguy.
The pitch:
Animation is a key point when building your application….
But, “Animation is complicated with Android”, is your feeling.
Well, in fact, it’s not. I will show you how simples they are, how magic they could be and how fun your application can become.
I will explains which animation to use for which version of the system, what are your choices and what is your strategy. We will have a complete overview on the Android Animation framework.
So I hope to see you in the room for this conference, because at the end, your application will be enhanced :)
5. Animation is life
Animationis comprehension
Animation is engagements
Animation is delightedyour UX
Animationis necessary
Animation is simple
Why Animation ?
7. Make Animations simple
Make Animation frameworks simple
Make Animation more simplethan that
Make Animation frameworks so simplethat a 6 years oldkids can use it
It's still not simplest enough,keep simplifyingit.
Animations frameworks's goals?
8. Because we wantyou to animate your
application !
Animations frameworks's goals?
10. AlphaRotate
Translate
Scale
And the plantransformations are yours
What we know: Tween Animations V1
<set
android:interpolator="@[package:]anim/interpolator_resource">
<alpha
android:duration="float"
android:fromAlpha="float" android:toAlpha="float" />
<scale
android:duration="float"
android:fromXScale="float" android:toXScale="float"
android:fromYScale="float" android:toYScale="float"
android:pivotX="float" android:pivotY="float" />
<translate
android:duration="float"
android:fromXDelta="float" android:toXDelta="float"
android:fromYDelta="float" android:toYDelta="float" />
<rotate
android:duration="float"
android:fromDegrees="float" android:toDegrees="float"
android:pivotX="float" android:pivotY="float" />
<set>
...
</set>
</set>
Animation animIn= AnimationUtils.loadAnimation(ctx, R.anim.generic_anim);
edtMessage.startAnimation(animIn);
animation/generic_anim.xml
Onlychange the pixels notthe state of the view
11. Makes animations generic
What we know:ObjectAnimator V11
/** * The in Animation for after HC */
AnimatorSet animInHC;
animInHC = (AnimatorSet) AnimatorInflater.loadAnimator(ctx, R.animator.generic_anim);
animInHC.setTarget(edtMessage);
animInHC.setTarget(btnAdd);
animInHC.start();
<set >
<objectAnimator
android:duration="1000"
android:propertyName="translationX"
android:valueFrom="-250"
android:valueTo="0"
android:valueType="floatType" />
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="0.0"
android:valueTo="1.0"
android:valueType="floatType" />
</set>
animator-v11/generic_anim.xml
Changes the state of the object
12. Uses Handler and Runnable
Simplebut dangerous(memory leak, runsin UIthread, can generates frames drops)
Not optimized
Animation: Elementary Principles
Animationis changingthe view/objectstate by droppingchanges inthe UI thread
Handler clipDrawableHandler=new Handler();
Runnable clipDrawableRunnable=new Runnable() {
@Override
public void run() {
myView.changeSomething(level);
clipDrawableHandler.postDelayed(clipDrawableRunnable,32);
}
};
V1
13. Handler clipDrawableHandler=new Handler();
Runnable clipDrawableRunnable=new Runnable() {
@Override
public void run() {
myView.changeSomething(level);
clipDrawableHandler.postDelayed(clipDrawableRunnable,32);
}
};
Uses Handler and Runnable
Simplebut dangerous(memory leak, runsin UIthread, can generates frames drops)
Not optimized
Animation: Elementary Principles V11
Animationis changingthe view/objectstate by droppingchanges inthe UI thread
14. Handler clipDrawableHandler=new Handler();
Runnable clipDrawableRunnable=new Runnable() {
@Override
public void run() {
myView.changeSomething(level);
clipDrawableHandler.postDelayed(clipDrawableRunnable,32);
}
};
Uses Handler and Runnable
Simplebut dangerous(memory leak, runsin UIthread, can generates frames drops)
Not optimized
Animation: Elementary Principles V11
Animationis changingthe view/objectstate by droppingchanges inthe UI thread
16. Don't be scared, it's simple.
How do yougofrom the pointAto the pointB?
Interpolator V1
t1
t0
v0
v1
A
B
?
i(t)=v,
where i(t0)=v0
and i(t1)=v1
float
time
17. It can be straight
Interpolartor V1
t1
t0
v0
v1
from
to
linear
18. Youcan use the system's ones
Interpolartor V1
t1
t0
v0
v1
from
to
deceleration
t1
t0
v0
v1
acceleration
to
from
t1
t0
v0
v1
to
bouncing
from
<set android:interpolator=
"@android:anim/accelerate_interpolator">
<set android:interpolator=
"@android:anim/decelerate_interpolator">
<set android:interpolator=
"@android:anim/bounce_interpolator">
19. Or buildyour own.
Interpolartor V1
t1
t0
v0
v1
from
to
alcoholic
public class MyInterpolator implements Interpolator {
float v;
@Override
public float getInterpolation(float input) {
//v=i(input)
return v;
}
}
26. Think Drawable
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:enterFadeDuration="300"
android:exitFadeDuration="300">
<!--Sorry below v21 there is no animated selector
you can just fade in and fade out-->
<item android:id="@+id/item_pressed"
android:state_pressed="true">
<bitmap android:src="@drawable/ic_android2ee"/></item>
<item android:id="@+id/item_normal">
<bitmap android:src="@drawable/ic_nut"/> </item>
</selector>
fade fade
Norma
l
Press
ed
Norma
l
View
state
Displ
ay
V
1
ClipDrawable
RotateDrawable
ScaleDrawable
AnimationDrawable
StateListDrawable
AnimatedStateListDraw
able
TransitionDrawable
AnimatedVectorDrawabl
e
27. <?xml version="1.0" encoding="utf-8"?>
<animated-selector >
<item android:id="@+id/item_pressed"
android:state_pressed="true">
<bitmap android:src="@drawable/ic_android2ee"/></item>
<item android:id="@+id/item_normal">
<bitmap android:src="@drawable/ic_nut"/> </item>
<transition android:fromId="@+id/item_pressed"
android:toId="@+id/item_normal">
<animation-list android:id="@+id/selected"
android:oneshot="true">
<item android:drawable="@drawable/attack_magic1"
android:duration="100" />
<item android:drawable="@drawable/attack_magic2"
android:duration="100" />
</animation-list></transition></animated-selector>
Think Drawable V21
Norma
l
Press
ed
Norma
l
View
state
Displ
ay
ClipDrawable
RotateDrawable
ScaleDrawable
AnimationDrawable
StateListDrawable
AnimatedStateListDraw
able
TransitionDrawable
AnimatedVectorDrawabl
e
29. AnimatedVectorDrawable: Animate Shape
<?xml version="1.0" encoding="utf-8"?>
<vector
android:viewportWidth="500"
android:viewportHeight="500"
android:width="500px"
android:height="500px">
<!--Make group to animate them
separately using ObjectAnimator-->
<!--Define the pivot in the group they
will be used by ObjectAnimator-->
<group android:name="tete"
android:pivotX="250.0"
android:pivotY="100.0">
<path
android:name="head"
android:fillColor="#9FBF3B"
android:pathData="..." />
</group>...</vector>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
android:drawable="@drawable/my_svg" >
<target
android:name="tete"
android:animation="@anim/anim_svg"
/>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/ap
k/res/android">
<!-- res/anim/rotation.xml -->
<objectAnimator
android:duration="6000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
</set>
drawable/my_svg drawable/my_svg_animated anim/anim_svg
use
use
layout/my_view
use
<ImageView
android:id="@+id/imvAnimatedVector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/my_svg_animated"/>
V21
RotateDrawable
ScaleDrawable
AnimationDrawable
StateListDrawable
AnimatedStateListDraw
able
TransitionDrawable
animatedVectorDrawable.start();
30. AnimatedVectorDrawable: Morph between
shapes
<?xml version="1.0" encoding="utf-8"?>
<vector
android:viewportWidth="500"
android:viewportHeight="500"
android:width="500px"
android:height="500px">
<!--Make group to animate them
separately using ObjectAnimator-->
<!--Define the pivot in the group they
will be used by ObjectAnimator-->
<group android:name="tete"
android:pivotX="250.0"
android:pivotY="100.0">
<path
android:name="head"
android:fillColor="#9FBF3B"
android:pathData="..." />
</group>...</vector>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
android:drawable="@drawable/my_svg" >
<target
android:name="tete"
android:animation="@anim/animpath_svg"
/>
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<set >
<!-- res/anim/rotation.xml -->
<objectAnimator
android:duration="6000"
android:propertyName="pathData"
android:valueFrom="M300,70 l 0,-70
70,70 0,0 -70,70z"
android:valueTo="M300,70 l 0,-70
70,0 0,140 -70,0 z"
android:valueType="pathType"/>
</set>
drawable/my_svg drawable/my_svg_animated anim/animpath_svg
use
use
layout/my_view
use
<ImageView
android:id="@+id/imvAnimatedVector2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/my_svg_animated"/>
V21
RotateDrawable
ScaleDrawable
AnimationDrawable
StateListDrawable
AnimatedStateListDraw
able
TransitionDrawable
animatedVectorDrawable.start();
31. The constraints that killfor path transformation:
"Note that the paths must be compatible for morphing.In more details, the paths shouldhave
exact same lengthof commands , and exact same lengthof parameters for each commands."
It means: youwon't use it expect for so simpletrick (arrow to hamburger).
=>Waiting for tools !
AnimatedVectorDrawable: constrained V21
32. And here it is !!!
https://github.com/bonnyfone/vectalign
AnimatedVectorDrawable: constrained V21
34. Make it simple and magic:
Youcan animate any view
translationX ,translationY, rotationX, rotationY, rotation, scaleX, scaleY, pivotX,pivotY,
x,y,alphaand more
with onelineof code!
myView.animate().setDuration(300).x(20).rotationY(60).start();
ViewPropertyAnimator V13
37. First extends what youwant
(Objector View or what ever)
public class BlueDot extends View {
ObjectAnimator Demistify V13
38. Define the propertyto animate
using set/*MyProperty*/
public class BlueDot extends View {
/** * The property to animate *
* @param parameter value of the state to calculate the animation of the object
*/
private void setToto(int parameter) {
/*Do your stuff,
(call invalidate to redraw view)*/
ObjectAnimator Demystify V13
39. Then animate
public class BlueDot extends View {
/** * The property to animate *
@param parameter value of the state to calculate the animation of the object */
private void setToto(int parameter) {/*Do your stuff,(call invalidate to redraw view)*/
ObjectAnimator.ofInt(blueDot, "toto", 0, 110)
.start();
ObjectAnimator Demystify V13
40. ClipDrawable
ObjectAnimator Demystify
Handler clipDrawableHandler=new Handler();
Runnable clipDrawableRunnable=new Runnable() {
@Override
public void run() {level++;
clipDrawableHorizontal.setLevel(level);
clipDrawableHandler.postDelayed(clipDrawableRunnable,32);
}
};
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_android2ee"
android:clipOrientation="horizontal"
android:gravity="left" />
V1
It was before
Works with every think !
41. Works with every think !
public class MyActivity extends Activity{
private void setMoveDrawable(int level){
clipDrawableHorizontal.setLevel(level); }
private void animateDrawable() {
ObjectAnimator.ofInt(this, "MoveDrawable", 0, 10000)
.start(); }
ObjectAnimator Demystify V13
42. Works with every think !
public class MyActivity extends Activity{
private void setMoveDrawable(int level){
clipDrawableHorizontal.setLevel(level); }
private void animateDrawable() {
ObjectAnimator.ofInt(this, "MoveDrawable", 0, 10000)
.start(); }
ObjectAnimator Demystify V13
46. Animation: ViewFlipper (basics)
4
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/viewpager"
android:background="#FF00F0F0">
</android.support.v4.view.ViewPager>
public class MainActivity extends ActionBarActivity {
private MyPagerAdapter pagerAdapter;
private ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//instanciate the PageAdapter
pagerAdapter=new MyPagerAdapter(this);
//Find the viewPager
viewPager = (ViewPager) super.findViewById(R.id.viewpager);
// Affectation de l'adapter au ViewPager
viewPager.setAdapter(pagerAdapter);
}
V13
47. Animation: ViewFlipper (basics)
4
public class MyPagerAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> fragments;
public MyPagerAdapter(ActionBarActivity ctx) {
super(ctx.getSupportFragmentManager());
fragments = new ArrayList<Fragment>();
//A stuff I never did before, instanciate my fragment
Fragment frag =new MyFragment1();
fragments.add(frag);...
}
public Fragment getItem(int position) { return fragments.get(position); }
public int getCount() {return fragments.size(); }
public float getPageWidth (int position) {
//this is used to disaplyed the view a little bit smaller than
// it is really so you can see the right and the left view around the central view
return 0.93f;
}
}
V13
48. Animation: ViewPager adding Animation
4
//instanciate the PageAdapter
pagerAdapter=new MyPagerAdapter(this);
//Find the viewPager
viewPager = (ViewPager) super.findViewById(R.id.viewpager);
// Affectation de l'adapter au ViewPager
viewPager.setAdapter(pagerAdapter);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){
viewPager.setPageTransformer(true, new PageTransformer(this));
}
V13
49. Animation: ViewPager
4
public class PageTransformer implements ViewPager.PageTransformer {
public void transformPage(View view, float position) {
//Only the main layout is passed here
pageWidth = view.getWidth();
if (position < -1) // This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { //this page is displayed on the screen
if (position < 0) {
left(view, position);
} else {
right(view, position);
}
} else // This page is way off-screen to the right.
view.setAlpha(0);
}
}
V13
59. Youhave the choicebetween:
Custom animation
Scaling Component
Scaling bitmap
You needto reverse:
Animation: Activities/Fragments Transition v16v8
public class otherActivity extends Activity {
public void finish() {
super.finish();
//this work for all version superior to level 5
overridePendingTransition(R.anim.anim_push_right_in_a2ee,
R.anim.anim_push_right_out_a2ee);
}}
60. Awesome
First manage your theme
Animation: new Activity Transition v21
<resources>
<!-- Thanks to :-->
<!-- http://code.tutsplus.com/tutorials/introduction-to-the-new-lollipop-activity-
transitions--cms-23711-->
<!-- Base application theme. -->
<style name="AppTheme" parent="BaseTheme">
<!-- Set the transition between activities effective -->
<item name="android:windowContentTransitions">true</item>
<item name="android:windowEnterTransition">@android:transition/slide_bottom</item>
<item name="android:windowExitTransition">@android:transition/slide_bottom</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
</style>
</resources>
61. Set the android:transitionName to your components
Animation: new Activity Transition v21
<ImageButton
android:id="@+id/ibtnSprite"
android:transitionName="@string/imvSprite_transition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_gravity="center"
android:src="@drawable/attack_magic_animation"
/>
<ImageView
android:id="@+id/imvSprite"
android:transitionName="@string/imvSprite_transition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/attack_magic_animation"
/>
layout/main_activity layout/other_activity
62. Make yourpairs and launchthe new Activity
Animation: new Activity Transition v21
if (isPostLollipop) {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
new Pair<View, String>(imvSprites, getString(R.string.imvSprite_transition)),
);
}
ActivityCompat.startActivity(MainActivity.this, intent, options.toBundle());
}
64. How to get the screen size ?
Get screen size
@SuppressLint("NewApi")
private void getViewSize() {
//this is an usual trick when we want to know the dimension of our view
//initialize dimensions of the view
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
if (postICS) {
Point size = new Point();
display.getSize(size);
width = size.x;
height = size.y;
} else {
width = display.getWidth(); // deprecated
height = display.getHeight(); // deprecated
}
}
V1
65. Whencustomizing youview, youhave to overwrite the followingmethod:
Get custom view size
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.w = w;
this.h = h;
centerX = w / 2;
centerY = h / 2;
//...}
V1
66. Use the ViewTreeObserver
Get view size
private void getEditButtonWidth() {
//this is an usual trick when we want to know the dimension of the elements of our view
//find the dimension of the EditButton
ViewTreeObserver vto = btnEdit.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
btnEdit.getViewTreeObserver().removeGlobalOnLayoutListener(this);
btnEditWidth = btnEdit.getMeasuredWidth();
}
});
}
V1
67. Use the LayoutParameter of the ViewGroup
Changecomponents size
private void changeImvSprite1Size(){
//Change the LayoutParameter to change the size of view
if(isImvSprite1Expended){
imvSprite1.setLayoutParams(imvSpritesLayoutParamNormal);
}else{
imvSprite1.setLayoutParams(imvSpritesLayoutParamExpanded);
}
isImvSprite1Expended=!isImvSprite1Expended;
}
V1
private void initializeImvSpriteSize() {
//get the real size of the components
imvSprite1Height = imvSprite1.getMeasuredHeight();
//initialize the layout parameter for the normal size
imvSpritesLayoutParamNormal = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, imvSprite1Height);
//initialize the layout parameter for the expanded size
imvSpritesLayoutParamExpanded= new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 2*imvSprite1Height);}
private LinearLayout.LayoutParams imvSpritesLayoutParamNormal, imvSpritesLayoutParamExpanded ;
69. Make yourownpaint :
Make rainbow paint
Paint dotPaint= new Paint();
//initialize the shader (stuff that make the color of the paint depending on
// the location in the screen and a set of colors)
//@chiuki at droidcon london
int[] rainbow = getRainbowColors();
Shader shader = new LinearGradient(0, 0, 0, w, rainbow,
null, Shader.TileMode.MIRROR);
Matrix matrix = new Matrix();
matrix.setRotate(90);
shader.setLocalMatrix(matrix);
dotPaint.setShader(shader);
V1
private int[] getRainbowColors() {
return new int[]{
getResources().getColor(R.color.rainbow_red),
getResources().getColor(R.color.rainbow_yellow),
getResources().getColor(R.color.rainbow_green),
getResources().getColor(R.color.rainbow_turquoise),
getResources().getColor(R.color.rainbow_blue),
getResources().getColor(R.color.rainbow_purple)
};}