This is the slide of the conference gave at the DroidCon Paris 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 :)
8. Alpha
Rotate
Translate
Scale
And the plantransformations are yours
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 pixels notthe state of the view
9. Makes animations generic
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
10. Uses Handler and Runnable
Simplebut dangerous(memory leak, runsin UIthread, can generates frames drops)
Not optimized
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
11. Uses Handler and Runnable
Simplebut dangerous(memory leak, runsin UIthread, can generates frames drops)
Not optimized
V11
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);
}
};
12.
13. Don't be scared, it's simple.
How doyougofrom the pointfromto the pointto?
V1
t1t0
v0
v1
from
to
?
i(t)=v,
where i(t0)=v0
and i(t1)=v1
float
time
14. It can be straight
V1
t1t0
v0
v1
from
to
linear
15. t1t0
v0
v1
Youcan use the system's ones
V1
t1t0
v0
v1
from
to
deceleration acceleration
to
from
t1t0
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"
>
24. <?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
TransitionDrawable
StateListDrawable
AnimatedStateListDraw
able
AnimatedVectorDrawabl
e
26. <?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/apk/res/a
ndroid">
<!-- 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
us
e
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"/>
animatedVectorDrawable.start();
V21
TransitionDrawable
StateListDrawable
AnimatedStateListDrawable
AnimatedVectorDrawable
27. <?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
us
e
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"/>
animatedVectorDrawable.start();
V21
TransitionDrawable
StateListDrawable
AnimatedStateListDrawable
AnimatedVectorDrawable
28. The constraints that killfor path transformation:
"Note thatthe paths must be compatiblefor morphing.In more details, the pathsshouldhave
exact same lengthof commands , and exact same lengthof parameters for each commands."
It means: youwon't use it expectfor so simple trick (arrow to hamburger).
=>Waiting for tools !
V21
29. And here it is !!!
https://github.com/bonnyfone/vectalign
V21
30. To create Svg and/orsimplify them
https://inkscape.org/
To convert Svg intoVectorDrawable
http://inloop.github.io/svg2android/
A goodpractice :
Define your path in aString file
(resvaluesmy_path_string.xml)
V21
But even with that it's the hell on earth
=>working ona Github project
31.
32. Make it simple and magic:
Youcan animate any view
translationX ,translationY, rotationX, rotationY, rotation, scaleX, scaleY, pivotX,pivotY,
x,y,alphaand more
with one lineof code!
myView.animate().setDuration(300).x(20).rotationY(60).start();
V13
35. First extends what youwant
(Object or View or what ever)
public class BlueDot extends View {
V13
36. Define the propertyto animate
usingset/*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)*/
V13
37. 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();
V13
38. ClipDrawable
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" />
It was before
Works with every think !
V13
39. 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();
}
No moreHandler neither Runnable!!!
yes thanks Chet !
V13
48. Youhave the choicebetween:
Custom animation
ScalingComponent
Scaling bitmap
You need to reverse :
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);
}}
v16v8
49. Awesome
First manage your theme
<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>
v21
50. Set the android:transitionName to your components
<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
v21
51. Make your pairs and launch the new Activity
if (isPostLollipop) {
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
new Pair<View, String>(imvSprites, getString(R.string.imvSprite_transition)),
);
}
ActivityCompat.startActivity(MainActivity.this, intent, options.toBundle());
}
v21
56. 56
Its the natural evolution of the ListView, the ViewHolder is the one responsible of the view management
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myView=inflater.inflate(R.layout.recyclerview,container,false);
recyclerView= (RecyclerView) myView.findViewById(R.id.my_recycler_view);
// use a layout manager
recyclerViewLayoutManager = getLayoutManager();
recyclerView.setLayoutManager(recyclerViewLayoutManager);
// specify an adapter (see also next example)
recyclerViewAdapter = new RecyclerViewAdapter(humans,getActivity());
recyclerView.setAdapter(recyclerViewAdapter);
return myView ;
}
find the View
set the LayoutManager
set the Adapter
V7
57. 57
This is the same code as for the ListView (ViewHolder next slide)
public class RecyclerViewAdapter extends
RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
/****Attributes (t for temp)**/
private ArrayList<Human> humans;
private LayoutInflater inflater;
private View tNewView;
private ViewHolder tViewHolder;
private Human tHuman;
/****** Constructor**/
public RecyclerViewAdapter(ArrayList<Human>
dataSet,Context ctx){
humans=dataSet;
inflater=LayoutInflater.from(ctx);
}
@Override
public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
tNewView=inflater.inflate(R.layout.simple_item,parent,false);
tViewHolder=new ViewHolder(tNewView);
return tViewHolder;
}
@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder holder, int position) {
tHuman=humans.get(position);
holder.getTxvName().setText(tHuman.getName());
holder.getTxvFirstName().setText(tHuman.getFirstName());
holder.getTxvMessage().setText(tHuman.getMessage());
}
@Override
public int getItemCount() {
return humans.size();
}
V7
58. 58
This is the same code as for the ListView (ViewHolder next slide)
public class RecyclerViewAdapter extends
RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
/****Attributes (t for temp)**/
private ArrayList<Human> humans;
private LayoutInflater inflater;
private View tNewView;
private ViewHolder tViewHolder;
private Human tHuman;
/****** Constructor**/
public RecyclerViewAdapter(ArrayList<Human>
dataSet,Context ctx){
humans=dataSet;
inflater=LayoutInflater.from(ctx);
}
@Override
public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
tNewView=inflater.inflate(R.layout.simple_item,parent,false);
tViewHolder=new ViewHolder(tNewView);
return tViewHolder;
}
@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder holder, int position) {
tHuman=humans.get(position);
holder.getTxvName().setText(tHuman.getName());
holder.getTxvFirstName().setText(tHuman.getFirstName());
holder.getTxvMessage().setText(tHuman.getMessage());
}
@Override
public int getItemCount() {
return humans.size();
}
Inflate the view and its viewHolder
Return the ViewHolder
Update the View using the ViewHolder
Set your DataSet and your LayoutInflater
V7
59. 59
Le ViewHolder gère la vue qu'il encapsule
public class ViewHolder extends RecyclerView.ViewHolder{
TextView txvName=null;
TextView txvFirstName=null;
TextView txvMessage=null;
View.OnClickListener clickListener;
int position;
public ViewHolder(View itemView) {
super(itemView);
txvName= (TextView) itemView.findViewById(R.id.txvName);
txvFirstName= (TextView) itemView.findViewById(R.id.txvFirstName);
txvMessage= (TextView) itemView.findViewById(R.id.txvMessage);
clickListener=new View.OnClickListener() {
public void onClick(View v) {changeTxvMessageVisibilityState(); }
};
itemView.setOnClickListener(clickListener);
}
public TextView getTxvFirstName() {return txvFirstName;}
public TextView getTxvMessage() {return txvMessage;}
public TextView getTxvName() {return txvName;}
public void changeTxvMessageVisibilityState(){
//Do the stuff }
}
V7
61. 61
Le StaggeredLayoutManager
public RecyclerView.LayoutManager getLayoutManager() {
StaggeredGridLayoutManager stagLayoutManager=new StaggeredGridLayoutManager(2,GridLayoutManager.VERTICAL);
stagLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
return stagLayoutManager;
}
V7
62. 62
Le GridLayoutManager
public RecyclerView.LayoutManager getLayoutManager() {
GridLayoutManager gridLayoutManager=new GridLayoutManager(getContext(),2,GridLayoutManager.VERTICAL,false);
//define specific span of specific cells according to a rule
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int arg0) {
return (arg0 % 3) == 0 ? 2 : 1;
}
});
return gridLayoutManager;
}
V7
75. 76
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(); }
V13
76. 77
public class MainActivity extends ActionBarActivity {
@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);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){
viewPager.setPageTransformer(true, new PageTransformer(this));
}
V13
77. 78
public class MyPageTransformer implements ViewPager.PageTransformer{
RecyclerView myRecyclerView;
public void transformPage(View view, float position) {
//Only the main layout is passed here/
myRecyclerView= (RecyclerView) view.findViewById(R.id.my_recycler_view);
if (position < -1) { // [-Infinity,-1)This page is way off-screen to the left.
view.setAlpha(0); }
else if (position < 1) { //in the visible range [-1,1]
myRecyclerView.setAlpha(1-Math.abs(position));
view.setAlpha(1);
if (position < 0) {//coming from left
myRecyclerView.setRotationX((position * 360));
} else {//coming from right
myRecyclerView.setRotationX(-1*position *360);
}
}
else { // (1,+Infinity] // This page is way off-screen to the right.
view.setAlpha(0); }
}}
V13
78.
79. First: Simplifyyour layout !!!
if not enoughyoucan also:
User LayerType Hardware accelerated
btnDoNotPress.setLayerType(View.LAYER_TYPE_HARDWARE,null);
new Animator.AnimatorListener() {
public void onAnimationEnd(Animator animation) {
btnDoNotPress.setLayerType(View.LAYER_TYPE_NONE, null);
}
V1
80. Use Interface andFactory
/** * The animation */
MainActivityAnimMother anim;
//the factory for the animations (you can
create a Class to do that if you want) :
if(isPostLollipop){
anim=new MainActivityAnimLLP();
}else if(postICS){
anim=new MainActivityAnimICS();
}else{
anim=new MainActivityAnimGinger();
}
V1
MainActivity
MainActivity
MainActivityAnimGinger
MainActivityAnimICS
MainActivityAnimLLP
MainActivity
MotherAnim
MainActivityAnimIntf
Animation code Animation
attributes
Animation methods
declaration
v21