Showing posts with label lollipop. Show all posts
Showing posts with label lollipop. Show all posts

Tuesday, November 18, 2014

Android Stacked Fractions

I read about a small new feature on TextView in Android Lollipop:

I never heard of CSS font-feature-settings, but a quick internet search led me to a sandbox, where I can play with OpenType font features such as kerning and ligature. Not particularly exciting, but then I scrolled down the list and saw fractions. I experimented with different settings and came up with this:

Stacked fractions! Let's put that in a TextView.

First, I need a font that supports afrc. I chose Nutso2, an Apache 2 licensed font.

I first tried to set font settings on the whole TextView:

Typeface typeface = Typeface.createFromAsset(
    getAssets(), "Nutso2.otf");
textView.setTypeface(typeface);
textView.setText("1/2 2/5");
textView.setFontFeatureSettings("afrc");

Looks good. But then I want to display one and a half. If I set text as "1 1/2" there is a space, and if I use "11/2" it shows eleven over two. What to do?

I went back to the Nutso project for their demo page:

1<span class="afrc">1/2</span>

HTML with span, eh? Why yes, I can do that in Android! We'll need a TagHandler with a custom Span.

1<afrc>1/2</afrc>

The TagHandler is called twice: at the start of the span, again at the end. We mark the beginning of the tag with a Spannable.SPAN_MARK_MARK so we can retrieve it at the end of the span. With that we have the start and end positions of our stacked fraction, and we can apply our FractionSpan to activate the afrc font feature settings.

Now we have mixed fractions, no space. Yay!

Source: https://github.com/chiuki/advanced-textview (FractionActivity)

To hear about this and other cool TextView tricks, come to my Advanced Android TextView talk at AnDevCon this Friday!

Monday, October 20, 2014

First look at AnimatedVectorDrawable

Android Lollipop introduced a lot of sweet new classes. The one that caught my eye is AnimatedVectorDrawable, and I decided to check it out right away.

Example from Documentation

The documentation included an example, so I created the files as instructed: res/drawable/vectordrawable.xml, res/drawable/avd.xml, res/anim/rotation.xml and res/anim/path_morph.xml

Now what? There are many ways to use a Drawable. How about in a TextView?

<TextView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="@string/example_from_documentation"
  android:drawableBottom="@drawable/avd"/>

No animation yet. Let's start it.

for (Drawable drawable : textView.getCompoundDrawables()) {
  if (drawable instanceof Animatable) {
    ((Animatable) drawable).start();
  }
}

And voilĂ ! Animation. But what is it? Let's step it through.

res/drawable/vectordrawable.xml

<vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:height="64dp"
  android:width="64dp"
  android:viewportHeight="600"
  android:viewportWidth="600" >
  <group
    android:name="rotationGroup"
    android:pivotX="300.0"
    android:pivotY="300.0"
    android:rotation="45.0" >
    <path
      android:name="v"
      android:fillColor="#000000"
      android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
  </group>
</vector>

This is the VectorDrawable. The path defines a triangle, and the group rotates it by 45 degrees.

res/drawable/avd.xml

<animated-vector
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:drawable="@drawable/vectordrawable" >
  <target
    android:name="rotationGroup"
    android:animation="@anim/rotation" />
  <target
    android:name="v"
    android:animation="@anim/path_morph" />
</animated-vector>

Next we have avd.xml, which rotates the group and morphs the path.

res/anim/rotation.xml

<objectAnimator
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:duration="6000"
  android:propertyName="rotation"
  android:valueFrom="0"
  android:valueTo="360" />

res/anim/path_morph.xml

<set
  xmlns:android="http://schemas.android.com/apk/res/android">
  <objectAnimator
    android:duration="3000"
    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>

The rotation starts from 0 degrees. But since our drawable is initially rotated at 45 degrees, there is a sudden jump when the animation starts. It takes 6000ms to reach 360 degrees. Meanwhile, the path morphs from a triangle to a rectangle in 3000ms, half the time. So, at 180 degrees, the morph is complete.

Phew, that was not obvious at all. To understand what was happening, I split the rotation and the path morph to observe them separately.

Clock

After trying the example, I wanted to make my own, to see if I can come up with some simple animations that makes sense. I like the idea of animating different parts of the VectorDrawable, and made a clock.

The hours arm rotates from 9 o'clock to 5 o'clock while the minutes arm goes around from 0 to 60 minutes. The duration of rotations are set differently to give them different speeds.

Smiling face

Next I played with path morph. What is a path morph that makes sense? Let's make a sad face into a happy face!

Points to note

  1. The <vector> tag must have android:height and android:width to define the intrinsic width and height of the VectorDrawable. Your app will crash if you skip them.
  2. If your VectorDrawable has a smaller android:height or android:width than when you use them say in an ImageView, your graphic will look pixelated.
  3. To morph from one path to another, the paths must be compatible. They need to have the exact same length of commands, and exact same length of parameters for each command.

I am very excited about VectorDrawable and AnimatedVectorDrawable. Scalable graphics FTW!

Source code: https://github.com/chiuki/animated-vector-drawable