Posted by Ben Weiss, Developer Programs Engineer
Material design is a new system for visual, interaction and motion design. We originally launched the Topeka web app as an Open Source example of material design on the web.
Today, we’re publishing a new material design example: The Android version of Topeka. It demonstrates that the same branding and material design principles can be used to create a consistent experience across platforms. Grab the code today on GitHub.The juicy bits
While the project demonstrates a lot of different aspects of material design, let’s take a quick look at some of the most interesting bits.
Transitions
Topeka for Android features several possibilities for transition implementation. For starters the Transitions API within ActivityOptions provides an easy, yet effective way to make great transitions between Activities.
To achieve this, we register the shared string in a resources file like this:
<resources> <string name="transition_avatar">AvatarTransition</string> </resources>
Then we use it within the source’s and target’s view as transitionName
<ImageView android:id="@+id/avatar" android:layout_width="@dimen/avatar_size" android:layout_height="@dimen/avatar_size" android:layout_marginEnd="@dimen/keyline_16" android:transitionName="@string/transition_avatar"/>
And then make the actual transition happen within SignInFragment.
private void performSignInWithTransition(View v) { Activity activity = getActivity(); ActivityOptions activityOptions = ActivityOptions .makeSceneTransitionAnimation(activity, v, activity.getString(R.string.transition_avatar)); CategorySelectionActivity.start(activity, mPlayer, activityOptions); activity.finishAfterTransition(); }
For multiple transition participants with ActivityOptions you can take a look at the CategorySelectionFragment.
Animations
When it comes to more complex animations you can orchestrate your own animations as we did for scoring.
To get this right it is important to make sure all elements are carefully choreographed. The AbsQuizView class performs a handful of carefully crafted animations when a question has been answered:
The animation starts with a color change for the floating action button, depending on the provided answer. After this has finished, the button shrinks out of view with a scale animation. The view holding the question itself also moves offscreen. We scale this view to a small green square before sliding it up behind the app bar. During the scaling the foreground of the view changes color to match the color of the fab that just disappeared. This establishes continuity across the various quiz question states.
All this takes place in less than a second’s time. We introduced a number of minor pauses (start delays) to keep the animation from being too overwhelming, while ensuring it’s still fast.
The code responsible for this exists within AbsQuizView’s performScoreAnimation
method.
FAB placement
The recently announced Floating Action Buttons are great for executing promoted actions. In the case of Topeka, we use it to submit an answer. The FAB also straddles two surfaces with variable heights; like this:
To achieve this we query the height of the top view (R.id.question_view) and then set padding on the FloatingActionButton once the view hierarchy has been laid out:
private void addFloatingActionButton() { final int fabSize = getResources().getDimensionPixelSize(R.dimen.fab_size); int bottomOfQuestionView = findViewById(R.id.question_view).getBottom(); final LayoutParams fabLayoutParams = new LayoutParams(fabSize, fabSize, Gravity.END | Gravity.TOP); final int fabPadding = getResources().getDimensionPixelSize(R.dimen.padding_fab); final int halfAFab = fabSize / 2; fabLayoutParams.setMargins(0, // left bottomOfQuestionView - halfAFab, //top 0, // right fabPadding); // bottom addView(mSubmitAnswer, fabLayoutParams); }
To make sure that this only happens after the initial layout, we use an OnLayoutChangeListener in the AbsQuizView’s constructor:
addOnLayoutChangeListener(new OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int l, int t, int r, int b, int oldLeft, int oldTop, int oldRight, int oldBottom) { removeOnLayoutChangeListener(this); addFloatingActionButton(); } });
Round OutlineProvider
Creating circular masks on API 21 onward is now really simple. Just extend the ViewOutlineProvider class and override the getOutline() method like this:
@Override public final void getOutline(View view, Outline outline) { final int size = view.getResources(). getDimensionPixelSize(R.id.view_size); outline.setOval(0, 0, size, size); }
and setClipToOutline(true)
on the target view in order to get the right shadow shape.
Check out more details within the outlineprovider package within Topeka for Android.
Vector Drawables
We use vector drawables to display icons in several places throughout the app. You might be aware of our collection of Material Design Icons on GitHub which contains about 750 icons for you to use. The best thing for Android developers: As of Lollipop you can use these VectorDrawables within your apps so they will look crisp no matter what density the device’s screen. For example, the back arrow ic_arrow_back from the icons repository has been adapted to Android’s vector drawable format.
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="48" android:viewportHeight="48"> <path android:pathData="M40 22H15.66l11.17-11.17L24 8 8 24l16 16 2.83-2.83L15.66 26H40v-4z" android:fillColor="?android:attr/textColorPrimary" /> </vector>
The vector drawable only has to be stored once within the res/drawable folder. This means less disk space is being used for drawable assets.
Property Animations
Did you know that you can easily animate any property of a View beyond the standard transformations offered by the ViewPropertyAnimator class (and it’s handy View#animate syntax)? For example in AbsQuizView we define a property for animating the view’s foreground color.
// Property for animating the foreground public static final PropertyFOREGROUND_COLOR = new IntProperty ("foregroundColor") { @Override public void setValue(FrameLayout layout, int value) { if (layout.getForeground() instanceof ColorDrawable) { ((ColorDrawable) layout.getForeground()).setColor(value); } else { layout.setForeground(new ColorDrawable(value)); } } @Override public Integer get(FrameLayout layout) { return ((ColorDrawable) layout.getForeground()).getColor(); } };
This can later be used to animate changes to said foreground color from one value to another like this:
final ObjectAnimator foregroundAnimator = ObjectAnimator .ofArgb(this, FOREGROUND_COLOR, Color.WHITE, backgroundColor);
This is not particularly new, as it has been added with API 12, but still can come in quite handy when you want to animate color changes in an easy fashion.
Tests
In addition to exemplifying material design components, Topeka for Android also features a set of unit and instrumentation tests that utilize the new testing APIs, namely “Gradle Unit Test Support” and the “Android Testing Support Library.” The implemented tests make the app resilient against changes to the data model. This catches breakages early, gives you more confidence in your code and allows for easy refactoring. Take a look at the androidTest and test folders for more details on how these tests are implemented within Topeka. For a deeper dive into Testing on Android, start reading about the Testing Tools.
What’s next?
With Topeka for Android, you can see how material design lets you create a more consistent experience across Android and the web. The project also highlights some of the best material design features of the Android 5.0 SDK and the new Android Design Library.
While the project currently only supports API 21+, there’s already a feature request open to support earlier versions, using tools like AppCompat and the new Android Design Support Library.
Have a look at the project and let us know in the project issue tracker if you’d like to contribute, or on Google+ or Twitter if you have questions.