Friday, August 29, 2014

Getting Started with Android L Animations

    Among many other new features, Android L has brought a slew of new animations that can be added to your apps. In this post I will go over some of those new animations and how to implement them into an app. All of the code for this post can be found on GitHub.

Ripples and Accents


    Android now supports a handful of predefined style attributes that relate to specific parts of the app, such as the status bar or navigation bar. This new system gives us an easy way to introduce two very simple, yet useful, animations: ripples and accented UI widgets. Ripples are the preferred way of giving users feedback from their actions on UI elements, and their color can be set by applying a color value to the colorControlHighlight attribute in your app theme.

<item name="android:colorControlHighlight">#0000AA</item>



    Just as easily, UI widgets, such as checkboxes, can be set using the colorAccent property in your styles folder to give them a color that fits with your app theme without having to use a set of different  images and state drawables.

<item name="android:colorAccent">#00FF00</item>


Circular Reveal


    One common task in Android is changing the visibility of an element on the screen. Using a ValueAnimator, developers now have an extra option for making this task stand out a bit more: the circular reveal. This can be used by using the ViewAnimationUtil.createCircularReveal method, and then changing the visibility of your view at the appropriate time using an animation listener. Here are the two very similar methods used for making a view visible or gone. Note that the reveal method has a duration set, causing it to take a second to finish the animation, whereas the hide animation is a lot faster. The getX() and getY() methods simply return the center points of the image along its given X and Y axes, and getRadius() simply returns the width of the view.

private void hideImageCircular() {
    int x = getX();
    int y = getY();
    int radius = getRadius();

    ValueAnimator anim =
            ViewAnimationUtils.createCircularReveal(mImageView, x, y, radius, 0);

    anim.addListener(new AnimatorListenerAdapter() {

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            mImageView.setVisibility( View.INVISIBLE );
        }
    });

    anim.start();
}

private void revealImageCircular() {
    int x = getX();
    int y = getY();
    int radius = getRadius();

    ValueAnimator anim =
            ViewAnimationUtils.createCircularReveal(mImageView, x, y, 0, radius);

    anim.setDuration( 1000 );
    anim.addListener( new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            super.onAnimationStart(animation);
            mImageView.setVisibility( View.VISIBLE );
        }
    });

    anim.start();
}


Activity Transitions


    In addition to the previous animations, Android L added a few stock activity transition animations to help make apps look and feel more polished - explode, slide and fade. In order to use these transitions, you must request the content transitions feature in both your entering and exiting activities before setting your content view.

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

    While setting transitions through a style is supported, for this case I will go over how to set transitions in the activities. Simply use the getWindow().setExitTransition( Transition ) and getWindow().setEnterTransition( Transition ) methods to tell your activities how to act when opening and closing. In the case of the explode transition, the code for going from MainActivity and our ListFragment to the second activity will look like this:

ListFragment:
getActivity().getWindow().setExitTransition( new Explode() );
intent = new Intent( getActivity(), ExplodeAnimationActivity.class );
startActivity( intent );

ExplodeAnimationActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    getWindow().setEnterTransition( new Explode() );
    getWindow().setExitTransition( new Explode() );
    setContentView(R.layout.activity_explode_animation);
}

@Override
public void onBackPressed() {
    super.onBackPressed();
    finishAfterTransition();
}

    Notice the onBackPressed method - this is important because it lets the OS know that the animation should finish before it hides the calling second activity. Using these few simple lines of code, we have our three types of simple activity transitions:
Explode Transition

Slide Transition

Fade Transition

    One useful tool that I am not using, but definitely deserves mentioning, is the Transition.TransitionListener. Applying this to your enter or exit transition allows for performing tasks at different points in the animation lifecycle, giving you more control over how your app should act. This listener should also be removed in onDestroy in order to prevent a leak.

getWindow().getEnterTransition().addListener( new Transition.TransitionListener {
    @Override
    public void onTransitionStart(Transition transition) {

    }

    @Override
    public void onTransitionEnd(Transition transition) {

    }

    @Override
    public void onTransitionCancel(Transition transition) {

    }

    @Override
    public void onTransitionPause(Transition transition) {

    }

    @Override
    public void onTransitionResume(Transition transition) {

    }
});


Shared Element Activity Transitions


    On top of the standard activity transitions, it is now possible to support animating shared elements across two activities. The first thing that has to be done to support this is setting your activity to use content transitions and allow overlapping transitions. This can be done through the style applied to your activities:

<item name="android:windowContentTransitions">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item>
<item name="android:windowAllowExitTransitionOverlap">true</item>

    While shared element transitions can be applied in Java, for this example I will use styles to apply the shared element transitions:

<item name="android:windowSharedElementEnterTransition">@transition/changebounds</item>
<item name="android:windowSharedElementExitTransition">@transition/changebounds</item>

    where the changebounds transition is defined as an xml file in our resources folder. Notice that I added two additional properties, duration and interpolator, in order to make the animation a little more fun.

changebounds.xml:
<changeBounds
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:interpolator/bounce" />

    The next step is to make sure you have a comparable view type (in this case we'll use ImageView) in both of your activity layouts, and that they both must have the same viewName attribute value. In our first activity, the view that will have the shared element will look like this:

<ImageView
    android:id="@+id/image"
    android:viewName="image"
    android:layout_width="match_parent"
    android:layout_height="250dp" />

    and the view in the second activity will look like this:

<ImageView
    android:id="@+id/image"
    android:layout_alignParentBottom="true"
    android:viewName="image"
    android:layout_width="match_parent"
    android:layout_height="250dp" />

    Now that all of the xml setup is ready to go, we can start coding in the transition from our activities. In the first activity, we set the transition group for the parent container view to false, and then take the drawable from the imageview that we want to share and compress and convert it into a byte stream that can be stored in an intent bundle. We then create ActivityOptions with a scene transition animation for our imageview and start the next activity.

Intent intent = new Intent( this, SharedElementSecondAnimationActivity.class );

((ViewGroup) mImageView.getParent()).setTransitionGroup( false );

ByteArrayOutputStream stream = new ByteArrayOutputStream();
( (BitmapDrawable) mImageView.getDrawable() ).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream);
intent.putExtra( "image", stream.toByteArray() );

ActivityOptions options;

try {
    options = ActivityOptions.makeSceneTransitionAnimation( this, mImageView, "image" );
} catch( NullPointerException e ) {
    Log.e( "SharedElementAnimationChangeBoundsActivity", "Did you set your ViewNames in the layout file?" );
    return;
}

if( options == null ) {
    Log.e("sharedelementanimation", "Options is null. Something broke. Good luck!");
} else {
    startActivity(intent, options.toBundle());
}

    In the second activity, we read the byte array from the intent bundle and decode it into a bitmap. We then apply that bitmap to an imageview. This second activity also has the onBackPressed method with finishAfterTransition in order to apply the transition when hitting the back button.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shared_element_second_animation);
    mImageView = (ImageView) findViewById( R.id.image );

    byte[] byteArray = getIntent().getByteArrayExtra("image");
    Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
    mImageView.setImageBitmap(bitmap);
}

@Override
public void onBackPressed() {
    super.onBackPressed();
    finishAfterTransition();
}

    And with that we now have a shared element transition that moves our shared element to its new location in the second activity, and has an extra bounce effect:


    These few examples of animations only begin to scratch the surface of what's possible in Android L, not to mention the scene transitions that were introduced in Kit Kat. I hope this tutorial helps other developers get started with the new possibilities, as applying animations makes for beautiful apps that are also fun to work on.

Friday, August 22, 2014

Programmatically Coloring Drawables

    With the introduction of Android L, developers now have an easy way to change the color of drawables to match the theme of their app. Despite this, many developers will need to support earlier Android versions in their apps, so I have decided to share a technique for changing drawable asset colors using Picasso for both local and remote assets. The source code for this example can be found on GitHub.

    The key to this technique is using the Transform interface provided in the Picasso library, which provides a transform method to take a bitmap and return an altered bitmap for use in the Picasso image loading method. This interface can be useful for things like changing the size of an image, or in this case changing the image color. The ColorTransformation class that we will use in this example implements the Transform interface and can either accept a color value in its constructor, or have a color set from a resource value.

public ColorTransformation( int color ) {
    setColor( color );
}

public void setColor( int color ) {
    this.color = color;
}

public void setColorFromRes( Context context, int colorResId ) {
    setColor( context.getResources().getColor( colorResId ) );
}

    The transform method is where the magic happens. We provide a source bitmap (which is passed to the transform by Picasso automagically in its drawable building process) and return an altered version. In this example we create a new drawable from the source bitmap in order to get the dimensions of the image, then create a new bitmap and canvas for that bitmap. We then set the bounds for the drawable and apply a color filter using the SRC_IN version of the PorterDuff algorithm - applying the color on top of the visible portions of the drawable ( if you haven't read up on PorterDuff, it's definitely a worth while and interesting topic ) and then draw the altered drawable onto the canvas of the new bitmap before returning it. We also recycle the source bitmap once it is no longer needed per best practices for managing memory with Bitmaps in 2.3 and lower Android devices.

@Override
public Bitmap transform(Bitmap source) {
    if( color == 0 ) {
        return source;
    }

    BitmapDrawable drawable = new BitmapDrawable(Resources.getSystem(), source );
    Bitmap result = Bitmap.createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888 );
    Canvas canvas = new Canvas( result );
    drawable.setBounds( 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight() );
    drawable.setColorFilter( color, PorterDuff.Mode.SRC_IN );
    drawable.draw(canvas);
    drawable.setColorFilter(null);
    drawable.setCallback(null);

    if( result != source ) {
        source.recycle();
    }

    return result;
}

    One additional point to notice in the ColorTransformation class is that there is a key method provided. This is used by Picasso for retaining transformed drawables in memory, allowing for quicker loading when a drawable is transformed and displayed multiple times, such as in a listview.

@Override
public String key() {
    return "DrawableColor:" + color;
}

    Now that the transform class is built, we can use it to load drawables in our app with specific colors. In MainActivity I have four image views: one that uses an unaltered local drawable, one that loads in and transforms a local drawable, one that loads in an unaltered asset through a URL and one that loads in the same asset via URL, but applies a transform to change its color. The latter three imageviews have their drawables populated like so:

Picasso.with( this )
        .load( R.drawable.ic_star )
        .transform( new ColorTransformation(getResources().getColor( R.color.local_drawable_color ) ) )
        .into( mDrawableTransformedImage );

Picasso.with( this )
        .load( getString( R.string.image_url ) )
        .into( mDrawableUrlImage );

Picasso.with( this )
        .load( getString( R.string.image_url ) )
        .transform( new ColorTransformation( getResources().getColor( R.color.remote_image_color ) ) )
        .into( mDrawableUrlTransformedImage );

    As you can see, the ColorTransform class is created for each drawable and a color is passed to it, and Picasso handles the rest by calling Transform and dealing with memory management/caching behind the scenes. The last part of MainActivity uses a Picasso Target to load a drawable into an object that has callbacks associated with it. This technique is used for colorizing and placing an image into the action bar, as the Picasso.into method does not work with the action bar icon directly. First we declare our Target and the actions it should perform with its drawable once it has been loaded or if it fails:

private Target ActionBarIconTarget = new Target()
{
    @Override
    public void onBitmapLoaded( Bitmap bitmap, Picasso.LoadedFrom from )
    {
        getSupportActionBar().setIcon( new BitmapDrawable( getResources(), bitmap ) );
    }

    @Override
    public void onBitmapFailed( Drawable errorDrawable )
    {
        getSupportActionBar().setIcon( R.drawable.ic_launcher );
    }

    @Override
    public void onPrepareLoad( Drawable placeHolderDrawable )
    {

    }
};

    Once we have our target and what it should do defined, we can load our altered drawable into the target so that the image can be loaded into the action bar.

Picasso.with( this )
    .load( R.drawable.ic_star )
    .transform( new ColorTransformation( getResources().getColor( R.color.action_bar_icon_color ) ) )
    .into( ActionBarIconTarget );

    And with that we now have an easy way to load recolored drawables across our app without generating new assets in each color. This is particularly useful when applying techniques like my last post on using Gradle to reskin the same code base for multiple applications. Below I have provided an image from the demo app showing the same icons in various colors after being run through this code. Hopefully it will help you other developers out there save a fair bit of time.

The unaltered URL drawable may be a bit hard to see, but it's originally a black icon of the Android logo

Sunday, August 3, 2014

Build Variations using Gradle and Resource Folders

    The Android framework allows for customization of apps based on different attributes of a physical device through the structuring of an apps resource folders, letting a developer change things such as layouts, integers and string values in order to fit their apps needs for a given device. Using this, developers can easily change configurations between phones, small and large tablets, and devices in landscape or portrait mode. With the introduction of Gradle and Android Studio, the ability to use folder structures has been expanded to allow for overriding resource folders and values per different product flavors and build types, such as debug and release. In this tutorial I will go over using Gradle to reskin a base application using product flavors. All source code can be found on GitHub.

    To start, let's touch on using resource values in an application. If you append a quantifying value such as -xxhdpi, -land or -sw600dp to a resource folder, then the resource values in those folders will override the values in their main counterpart folder for devices that fit the appended values criteria. This means that if a device is in landscape mode, it will use resources from values-land over those in values if available, and if the device is at least a small tablet, then items in values-sw600dp will take precedence.

    The differences here can be seen in the following example screen shot, where one line of text is changed depending on the default value (for portrait devices) and the values-land string value. Without any code changes, the device will choose which string value to use based on the orientation of the phone.
default orientation string - portrait
landscape orientation string on a Nexus 4

    Now that we have a base understanding of resource hierarchies in the Android framework, let's move on to the overall goal of this tutorial - reskinning an app using product flavors. This is done almost entirely in the build.gradle file, though there will be some additions in the project folder structure. A default Android application in Android Studio will already come with the build type node populated for debug and release.

buildTypes {
    release {
        //This is where the signing cert would be referenced for the release build
    }
    debug {
        //This is where the signing cert would be referenced for the debug build
    }
}

    Under the buildTypes node, we can define the main categories of product flavors that we will want to use. When generating a project, one type of each of these flavors will be used, as well as one of the build types.

flavorDimensions "color", "number"

    Once the types of flavor dimensions are defined, we can start declaring types of product flavors and assigning them to a flavor dimension.

productFlavors {
    blue {
        applicationId "com.ptrprograms.gradleproductflavorsblue"
        flavorDimension "color"
    }
    green {
        applicationId "com.ptrprograms.gradleproductflavorsgreen"
        flavorDimension "color"
    }
    orange {
        applicationId "com.ptrprograms.gradleproductflavorsorange"
        flavorDimension "color"
    }
    purple {
        applicationId "com.ptrprograms.gradleproductflavorspurple"
        flavorDimension "color"
    }
    red {
        applicationId "com.ptrprograms.gradleproductflavorsred"
        flavorDimension "color"
    }
    yellow {
        applicationId "com.ptrprograms.gradleproductflavorsyellow"
        flavorDimension "color"
    }

    one {
        flavorDimension "number"
    }

    two {
        flavorDimension "number"
    }
}

    Note that each of the color flavors has an applicationId value set. This allows us to differentiate between our products, getting multiple reskinned applications onto the Play Store with the same code base. With the product flavors defined, we can now use the Build Variants window in Android Studio to pick a mix of build types and product flavors to install or build.


    Similar to overriding values within one application by device size or orientation, we can also override values by product flavor. By creating directories in the same folder as 'main' titled after their product flavors, the resources in those directories will take precedence over the main resource values. In this example I override all of the launcher icons, the background color resource and a few strings matching the product flavor in each of the color flavors. Likewise I override a string in the number product flavor directories.



    With these values filled out, we can quickly generate similar apps with cosmetic changes, as can be seen from the blue version of the app at the beginning of this post and this green product running on a Nexus 7.

    And with that we are able to generate many different applications using a similar code base with different values per product for colors, images, text and any other available type of resource, so that multiple apps can be built quickly with just some planning and organization.