Skip to content

Instantly share code, notes, and snippets.

@nickbutcher
Last active June 16, 2020 19:28
Show Gist options
  • Star 68 You must be signed in to star a gist
  • Fork 13 You must be signed in to fork a gist
  • Save nickbutcher/e36ee3b3d6580ddb7a0a to your computer and use it in GitHub Desktop.
Save nickbutcher/e36ee3b3d6580ddb7a0a to your computer and use it in GitHub Desktop.
Animated Stroke. The google I/O website this year (https://google.com/io) has some funky animated lettering. I especially liked the animated stroke around the letters and wondered how you might implement that on Android. Turns out that AnimatedVectorDrawable makes this very easy! Here's how it looks: https://twitter.com/crafty/status/71077957997…
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="360dp"
android:height="200dp"
android:viewportWidth="360"
android:viewportHeight="200">
<path
android:name="hash"
android:pathData="M39,45L39,80 M57,45L57,80 M66,54L31,54 M66,71L31,71"
android:strokeColor="@color/indigo_400"
android:strokeLineCap="round"
android:strokeWidth="@dimen/hashtag_stroke_width" />
<path
android:name="i_body"
android:pathData="M83,82L107,82A2,2 0,0 1,109 84L109,155A2,2 0,0 1,107 157L83,157A2,2 0,0 1,81 155L81,84A2,2 0,0 1,83 82z"
android:strokeColor="@color/indigo_400"
android:strokeWidth="@dimen/hashtag_stroke_width" />
<path
android:name="i_dot"
android:pathData="M94,59m-14,0a14,14 0,1 1,28 0a14,14 0,1 1,-28 0"
android:strokeColor="@color/indigo_400"
android:strokeWidth="@dimen/hashtag_stroke_width" />
<path
android:name="o"
android:pathData="M159.5,119.5m-37.5,0a37.5,37.5 0,1 1,75 0a37.5,37.5 0,1 1,-75 0"
android:strokeColor="@color/indigo_400"
android:strokeWidth="@dimen/hashtag_stroke_width" />
<!-- Here's the trick, we place four copies of the path for the number one on top of each other.
We then set different trim values to show only a quarter of each path. We can then animate
the trimPathOffset property to make the shown segment describe the whole path. -->
<path
android:name="one_1"
android:pathData="@string/path_one"
android:strokeColor="@color/cyan_a100"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0"
android:trimPathEnd="0.25" />
<path
android:name="one_2"
android:pathData="@string/path_one"
android:strokeColor="@color/pink_500"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0.25"
android:trimPathEnd="0.5" />
<path
android:name="one_3"
android:pathData="@string/path_one"
android:strokeColor="@color/indigo_400"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0.5"
android:trimPathEnd="0.75" />
<path
android:name="one_4"
android:pathData="@string/path_one"
android:strokeColor="@color/cyan_300"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0.75"
android:trimPathEnd="0" />
<!-- we do the same for the number six. -->
<path
android:name="six_1"
android:pathData="@string/path_funky_six"
android:strokeColor="@color/cyan_a100"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0"
android:trimPathEnd="0.25" />
<path
android:name="six_2"
android:pathData="@string/path_funky_six"
android:strokeColor="@color/pink_500"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0.25"
android:trimPathEnd="0.5" />
<path
android:name="six_3"
android:pathData="@string/path_funky_six"
android:strokeColor="@color/indigo_400"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0.5"
android:trimPathEnd="0.75" />
<path
android:name="six_4"
android:pathData="@string/path_funky_six"
android:strokeColor="@color/cyan_300"
android:strokeWidth="@dimen/hashtag_stroke_width"
android:trimPathStart="0.75"
android:trimPathEnd="0" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_hashtag">
<target
android:name="one_1"
android:animation="@animator/offset_trim_one" />
<target
android:name="one_2"
android:animation="@animator/offset_trim_one" />
<target
android:name="one_3"
android:animation="@animator/offset_trim_one" />
<target
android:name="one_4"
android:animation="@animator/offset_trim_one" />
<target
android:name="six_1"
android:animation="@animator/offset_trim_six" />
<target
android:name="six_2"
android:animation="@animator/offset_trim_six" />
<target
android:name="six_3"
android:animation="@animator/offset_trim_six" />
<target
android:name="six_4"
android:animation="@animator/offset_trim_six" />
</animated-vector>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Define elements that are repeated here and reference them so that we only have to update them
in one place. -->
<resources>
<string name="path_funky_six">M302.14,60.72C302.29,61.46 276.46,97.06 270.1,112.55C260.87,138.44 278.6,149.83 284.3,152.76C299.15,160.38 316.85,150.27 323.08,141.43C329.3,132.59 333.05,109.99 316.85,100.57C306.85,94.75 290.54,97.32 290.2,97.06C289.85,96.79 276.32,81.31 276.46,80.88C276.6,80.45 294.73,77.62 302.88,84.28C315.76,92.99 315.62,114.99 306.84,127.42C298.06,139.85 276.46,144.38 260.54,130.73C238.46,111.79 259.06,85.64 260.87,83.1C260.87,83.1 286.23,46.19 286.83,46.03C287.43,45.87 301.99,59.99 302.14,60.72Z</string>
<string name="path_one">M211,45L235,45A2,2 0,0 1,237 47L237,155A2,2 0,0 1,235 157L211,157A2,2 0,0 1,209 155L209,47A2,2 0,0 1,211 45z</string>
<item name="hashtag_stroke_width" type="dimen" format="float">5</item>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- we use separate anims for the 1/6 to allow different durations (as the path lengths are
different, using the same duration produces different speeds). -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="trimPathOffset"
android:valueFrom="0"
android:valueTo="1"
android:duration="4000"
android:interpolator="@android:interpolator/linear"
android:repeatCount="infinite"
android:repeatMode="restart" />
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- we use separate anims for the 1/6 to allow different durations (as the path lengths are
different, using the same duration produces different speeds). -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="trimPathOffset"
android:valueFrom="0"
android:valueTo="1"
android:duration="5000"
android:interpolator="@android:interpolator/linear"
android:repeatCount="infinite"
android:repeatMode="restart" />
@eneim
Copy link

eneim commented Mar 21, 2016

Very beautiful implementation. Could you share the .svg file for this? I would like to try this: https://codecrafted.net/svgtoandroid to have java code to draw those items. Thanks in advance.

@alexjlockwood
Copy link

Two small things:

The following color attrs are missing:

<color name="indigo_400">#5C6BC0</color>
<color name="cyan_a100">#84FFFF</color>
<color name="pink_500">#E91E63</color>
<color name="cyan_300">#4DD0E1</color>

And the @drawable/ic_hashtag name in the animated vector drawable is also incorrect.

Other than that works great! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment