Animate Me! if you don't do it for me, do it for Chet - DroidconLondon2015

Preview:

Citation preview

Animate me !

#AnimateMeby(Mathias Seguy == Android2EE){

French Android Trainer}

Animate me ! If you don't do it for me,

do it for chet !

Demonstration !

The Tutorial

Animation is lifeAnimation is comprehensionAnimation is engagements

Animation is delighted your UXAnimation is necessary

Animation is simple

Why animations?

Make Animations simple

Animations framework's goal ?

Because we want you to animate your

application !Chris Banes Bruno

OliveiraRoman Nurick

Adam Powell

Chet Haase Reto Meier Taylor Ling

Romain Guy

Animations framework's goal ?

Animations Elementary Principles

Alpha Rotate

TranslateScale

And the plan transformations 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

Only change pixels not the state of the view

What we know: Tween Animations

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

What we know: ObjectAnimator

Uses Handler and RunnableSimple but dangerous (memory leak, runs in UI thread,

can generates frames drops)Not optimized

Animation is changing the view/object state by dropping changes in the UI thread

Handler clipDrawableHandler=new Handler();Runnable clipDrawableRunnable=new Runnable() { @Override

public void run() {myView.changeSomething(level);clipDrawableHandler.postDelayed(clipDrawableRunnable,32);

}};

V1

Animation: Elementary principles

Uses Handler and RunnableSimple but dangerous (memory leak, runs in UI thread,

can generates frames drops)Not optimized

V11

Animation is changing the view/object state by dropping changes in the UI thread

Animation: Elementary principles

Handler clipDrawableHandler=new Handler();Runnable clipDrawableRunnable=new Runnable() { @Override

public void run() {myView.changeSomething(level);clipDrawableHandler.postDelayed(clipDrawableRunnable,32);

}};

Have to be replaced by ObjectAnimator when

Gingerbread is out of scope

Make movement real :Interpolators are simple

Don't be scared, it's simple. How do you go from the point from to the point to ?

V1

t1t0v0

v1

from

to?i(t)=v,

where i(t0)=v0 and i(t1)=v1

float

time

Interpolators

It can be straight

V1

t1t0v0

v1

from

to

linear

Interpolators

t1t0v0

v1

You can use the system's ones

V1

t1t0v0

v1

from

to

deceleration acceleration

to

fromt1t0v0

v1to

bouncing

from

<set android:interpolator="@android:anim/accelerate_interpolator">

<set android:interpolator="@android:anim/decelerate_interpolator">

<set android:interpolator="@android:anim/

bounce_interpolator">

Interpolators

Or build your own.V1

t1t0v0

v1

from

to

alcoholicpublic class MyInterpolator implements Interpolator { float v; @Override public float getInterpolation(float input) { //v=i(input) return v; }...}

Interpolators

Drawables are our best friends

myDrawable= (ImageView)findViewById(R.id.imvToto).getDrawable();

<ImageView android:id="@+id/imvToto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:src="@drawable/my_drawable"/>

V1

Think Drawables

ClipDrawableRotateDrawableScaleDrawable

AnimationDrawableTransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawableHandler 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

Think Drawables

RotateDrawable rotateDrawableWheel;Handler rotateDrawableHandler=new Handler();Runnable rotateDrawableRunnable=new Runnable() {public void run() {level++;

rotateDrawableWheel.setLevel(level);rotateDrawableHandler.postDelayed(rotateDrawableRunnable,32);

}};

<?xml version="1.0" encoding="utf-8"?><rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_android2ee" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:toDegrees="360" />

V1

ClipDrawableRotateDrawable

ScaleDrawableAnimationDrawableTransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawable

Think Drawables

ScaleDrawable scaleDrawable;Handler scaleDrawableHandler=new Handler();Runnable scaleDrawableRunnable=new Runnable() {public void run() {level++;

scaleDrawable.setLevel(level);scaleDrawableHandler.postDelayed(scaleeDrawableRunnable,32);

}};

<?xml version="1.0" encoding="utf-8"?><scale xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@mipmap/ic_edit" android:scaleGravity="center" android:scaleHeight="100%" android:scaleWidth="100%" />

V1

ClipDrawableRotateDrawableScaleDrawable

AnimationDrawableTransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawable

Think Drawables

animationDrawable.start();

<animation-list android:id="@+id/selected" android:oneshot="false"> <item android:drawable="@drawable/attack_magic1" android:duration="100" /> <item android:drawable="@drawable/attack_magic2" android:duration="100" /> <item android:drawable="@drawable/attack_magic3" android:duration="100" /> <item android:drawable="@drawable/attack_magic4" android:duration="100" /></animation-list>

ClipDrawableRotateDrawableScaleDrawable

AnimationDrawableTransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawable

V1

Think Drawables

transitionDrawable.startTransition(3000); transitionDrawable.reverseTransition(3000);

<?xml version="1.0" encoding="utf-8"?><transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@mipmap/ic_ok" /> <item android:drawable="@mipmap/ic_nok" /></transition>

ClipDrawableRotateDrawableScaleDrawable

AnimationDrawableTransitionDrawable

StateListDrawableAnimatedStateListDrawa

bleAnimatedVectorDrawable

V1

Think Drawables

<?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

Normal

Pressed

Normal

View state

Display

V1

ClipDrawableRotateDrawableScaleDrawable

AnimationDrawableTransitionDrawable

StateListDrawableAnimatedStateListDrawa

bleAnimatedVectorDrawable

Think Drawables

<?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>

V21

Normal

Pressed

Normal

View state

Display

ClipDrawableRotateDrawableScaleDrawable

AnimationDrawableTransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawable

Think Drawables

<?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/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"/>

use

animatedVectorDrawable.start();

V21

AnimatedVectorDrawable: Animate Shapes

TransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawable

<?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"/>

use

animatedVectorDrawable.start();

V21

AnimatedVectorDrawable: Morph Shapes

TransitionDrawableStateListDrawable

AnimatedStateListDrawable

AnimatedVectorDrawable

The constraints that kill for path transformation: "Note that the paths must be compatible for morphing.

In more details, the paths should have exact same length of commands , and exact same length of

parameters for each commands."

It means: you won't use it expect for so simple trick (arrow to hamburger).=>Waiting for tools !

AnimatedVectorDrawable: Morph Shapes constraintV21

And here it is !!!https://github.com/bonnyfone/vectalign

V21

AnimatedVectorDrawable: Morph Shapes tool

To create Svg and/or simplify themhttps://inkscape.org/

To convert Svg into VectorDrawablehttp://inloop.github.io/svg2android/

A good practice :Define your path in a String file (res\values\my_path_string.xml)

V21

AnimatedVectorDrawable: others needed tools

But even with that it's the hell on earth=>working on a Github project

Others best friends

Animation is simple

Make it simple and magic: You can animate any view

translationX , translationY, rotationX, rotationY, rotation, scaleX, scaleY, pivotX,pivotY, x,y,alpha and more

with one line of code !

myView.animate().setDuration(300).x(20).rotationY(60).start();

V13

ViewPropertyAnimator

myView.animate().rotation(360);is equivalent to

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f).start();

ViewPropertyAnimator is based on ObjectAnimatorV13

Makes animations simple, magic and generic

V13

ObjectAnimator Demystify

First extends what you want (Object or View or what ever)

public class BlueDot extends View {

V13

ObjectAnimator Demystify

Define the property to 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)*/

V13

ObjectAnimator Demystify

Then animatepublic 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

ObjectAnimator Demystify

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

ObjectAnimator Demystify

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 more Handler neither Runnable !!!yes thanks Chet !

V13

ObjectAnimator Demystify

Scene Transition

Demonstration !

Scene and Transitionv16

One xml line to add<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/llSceneRoot" android:animateLayoutChanges="true">

v16Scene and Transition

One xml line to add<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/llSceneRoot" android:animateLayoutChanges="true">

One line of code to addif(postICS){ LayoutTransition transition = ((ViewGroup)findViewById(R.id.llSceneRoot)).getLayoutTransition(); // New capability as of Jellybean; monitor the container for *all* layout changes // (not just add/remove/visibility changes) and animate these changes as well.(==size) transition.enableTransitionType(LayoutTransition.CHANGING);}

v16Scene and Transition

v16

Activities transitions

v8

You have the choice between Custom animation

Intent slidingActivity = new Intent(this, SlidingActivity.class);ActivityCompat.startActivity(this, slidingActivity, translationBundle);}

v16Activities transitions

v8

Bundle translationBundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.anim_push_left_in_a2ee,R.anim.anim_push_left_out_a2ee).toBundle();

You have the choice between Custom animation

Scaling Component

Intent slidingActivity = new Intent(this, SlidingActivity.class);ActivityCompat.startActivity(this, slidingActivity, translationBundle);}

v16Activities transitions

v8

Bundle translationBundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.anim_push_left_in_a2ee,R.anim.anim_push_left_out_a2ee).toBundle();

Bundle translationBundle = ActivityOptionsCompat.makeScaleUpAnimation(btnScaling,0,0,btnScaling.getWidth(),btnScaling.getHeight() ).toBundle();

You have the choice between: Custom animation

Scaling Component

Scaling bitmap

Intent slidingActivity = new Intent(this, SlidingActivity.class);ActivityCompat.startActivity(this, slidingActivity, translationBundle);}

Bundle translationBundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.anim_push_left_in_a2ee,R.anim.anim_push_left_out_a2ee).toBundle();

Bundle translationBundle = ActivityOptionsCompat.makeScaleUpAnimation(btnScaling,0,0,btnScaling.getWidth(),btnScaling.getHeight() ).toBundle();

Bundle translationBundle = ActivityOptionsCompat.makeThumbnailScaleUpAnimation(imvSmiley,bitmap,0,0).toBundle();

v16Activities transitions

v8

You have the choice between: Custom animation

Scaling Component

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);}}

v16Activities transitions

v8

AwesomeFirst manage your theme

<resources> <!-- Thanks to :--> <!-- http://code.tutsplus.com/tutorials/introduction-to-the-new-lollipop-activity-transitions&#45;&#45;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>

v21New Activities transitions

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

v21New Activities transitions

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());}

v21New Activities transitions

The best for the end !

53

RecyclerView

54

BuildGradle: Add the library

dependencies { ... compile 'com.android.support:recyclerview-v7:23.0.1'

55

Principle

RecyclerView

Adapter

ViewHolder

LayoutManager

ItemAnimator ItemDecoratorItemView management

(ClickListener,Animation...)

dataSet

56

RecyclerView: codeIts 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

Same principles as for ListView

57

RecyclerView: The AdapterCodeThis 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(); }

58

RecyclerView: The AdapterCodeThis 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 viewHolderReturn the ViewHolder

Update the View using the ViewHolder

Set your DataSet and your LayoutInflater

59

RecyclerView: Le code de l'AdapterLe 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 }}

60

RecyclerView: Les LayoutManagerLe LinearLayoutManager

public RecyclerView.LayoutManager getLayoutManager() { return new LinearLayoutManager(getContext());}

61

RecyclerView: Les LayoutManagerLe StaggeredLayoutManager

public RecyclerView.LayoutManager getLayoutManager() {

StaggeredGridLayoutManager stagLayoutManager=new StaggeredGridLayoutManager(2,GridLayoutManager.VERTICAL);

stagLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);

return stagLayoutManager;}

62

RecyclerView: Les LayoutManagerLe 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;}

63

CoordinatorLayout

Tutoriaux:RecyclerView

64

BuildGradle: Add the library

dependencies { ... compile 'com.android.support:design:23.0.1'

65

All is done in the Layout file

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".CoordinatorLayout">

The parent Layout of all yours Layouts: The CoordinatorLayout

66

Everything is done in the layout

<android.support.design.widget.CoordinatorLayout >

<android.support.design.widget.AppBarLayout ... android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

<android.support.v7.widget.Toolbar ... app:layout_scrollFlags="scroll|enterAlways"/>

<android.support.design.widget.TabLayout ... android:fillViewport="true"/>

</android.support.design.widget.AppBarLayout>

<android.support.v4.view.ViewPager //Your content ... app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android.support.design.widget.CoordinatorLayout >

67

Going further with the CollapsingToolBarLayout<android.support.design.widget.AppBarLayout ... > <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed" android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary" app:expandedTitleMarginStart="48dp" app:expandedTitleMarginEnd="64dp">

<ImageView ... android:fitsSystemWindows="true" app:layout_collapseMode="parallax" />

<android.support.v7.widget.Toolbar ... app:layout_scrollFlags="scroll|enterAlways"/>

<android.support.design.widget.TabLayout ... android:fillViewport="true"/>

</android.support.design.widget.CollapsingToolbarLayout></android.support.design.widget.AppBarLayout>

68

Plus complexe avec CollapsingToolBarLayout

<android.support.design.widget.AppBarLayout...>

<android.support.design.widget.CollapsingToolbarLayout...>

<ImageView.../>

<android.support.design.widget.TabLayout .../>

<android.support.v7.widget.Toolbar.../>

</android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

Set the title of the ActionBar on the CollapsingToolbarLayout not

on the ToolBar!

69

Plus complexe avec CollapsingToolBarLayout<android.support.design.widget.AppBarLayout...>

<android.support.design.widget.CollapsingToolbarLayout...>

<ImageView.../> <android.support.v7.widget.Toolbar.../>

<android.support.design.widget.TabLayout .../>

</android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

switch

71

The one I prefer

<android.support.design.widget.AppBarLayout ... >

<android.support.design.widget.CollapsingToolbarLayout ... >

<ImageView ... />

<android.support.v7.widget.Toolbar ... />

</android.support.design.widget.CollapsingToolbarLayout>

<android.support.design.widget.TabLayout />

</android.support.design.widget.AppBarLayout>

collapsingToolbar.setContentScrimResource(R.drawable.cardview_background_toolbar);

An old trick : PageTransformer

73

V13

ViewPager

Demonstration !

V13

ViewPager

75

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

ViewPager

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

ViewPager

77

public class MainActivity extends ActionBarActivity {

@Overrideprotected void onCreate(Bundle savedInstanceState) {...//instanciate the PageAdapterpagerAdapter=new MyPagerAdapter(this);//Find the viewPagerviewPager = (ViewPager) super.findViewById(R.id.viewpager);// Affectation de l'adapter au ViewPagerviewPager.setAdapter(pagerAdapter);if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB){ viewPager.setPageTransformer(true, new PageTransformer(this));}

V13

ViewPager

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

ViewPager

Tips

How to get the 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

Get screen size

When customizing you view, you have to overwrite the following method:

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

Get custom view size

Use the ViewTreeObserver

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

Get widget view size

Use the LayoutParameter of the ViewGroup

private void changeImvSprite1Size(){ //Change the LayoutParameter to change the size of view if(isImvSprite1Expended){ imvSprite1.setLayoutParams(imvSpritesLayoutParamNormal); }else{ imvSprite1.setLayoutParams(imvSpritesLayoutParamExpanded); } isImvSprite1Expended=!isImvSprite1Expended;}

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 ;

V1

Change components size

Invalidate and dirtyArea or layoutRequest

V1

Redraw

Make your own 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);

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) };}

V1

Make rainbow paint

First: Simplify your layout !!!if not enough you can 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

Optimize your Animation

#android2eemathias.seguy@android2ee.com

www.android2ee.com

Android by passionPassionate by Training

Expert by technical love

Questions ?

Code:https://github.com/MathiasSeguy-

Android2EE

Android2EE@android2ee

Thank you!

Mathias Seguy

Slides:http://fr.slideshare.net/Android2EE