How to implement "Loading images" pattern (Opacity, Exposure and Saturation) from Google's new Material design guidelines

android, material-design

Solution

Thanks to @mttmllns! Previous Answer.

Since the previous answer shows an example written in C# and I was curious, I ported it to java. Complete GitHub Example

It outlines a 3-steps process where a combination of opacity, contrast/luminosity and saturation is used in concert to help salvage our poor users eyesight.

For a detailed explanation read this article.

EDIT:

See, the excellent answer provided by @DavidCrawford.

BTW: I fixed the linked GitHubProject to support pre-Lollipop devices. (Since API Level 11)

The Code

`AlphaSatColorMatrixEvaluator.java`

import android.animation.TypeEvaluator;
import android.graphics.ColorMatrix;

public class AlphaSatColorMatrixEvaluator implements TypeEvaluator {
    private ColorMatrix colorMatrix;
    float[] elements = new float[20];

    public AlphaSatColorMatrixEvaluator() {
        colorMatrix = new ColorMatrix ();
    }

    public ColorMatrix getColorMatrix() {
        return colorMatrix;
    }

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        // There are 3 phases so we multiply fraction by that amount
        float phase = fraction * 3;

        // Compute the alpha change over period [0, 2]
        float alpha = Math.min(phase, 2f) / 2f;
        // elements [19] = (float)Math.round(alpha * 255);
        elements [18] = alpha;

        // We substract to make the picture look darker, it will automatically clamp
        // This is spread over period [0, 2.5]
        final int MaxBlacker = 100;
        float blackening = (float)Math.round((1 - Math.min(phase, 2.5f) / 2.5f) * MaxBlacker);
        elements [4] = elements [9] = elements [14] = -blackening;

        // Finally we desaturate over [0, 3], taken from ColorMatrix.SetSaturation
        float invSat = 1 - Math.max(0.2f, fraction);
        float R = 0.213f * invSat;
        float G = 0.715f * invSat;
        float B = 0.072f * invSat;

        elements[0] = R + fraction; elements[1] = G;            elements[2] = B;
        elements[5] = R;            elements[6] = G + fraction; elements[7] = B;
        elements[10] = R;           elements[11] = G;           elements[12] = B + fraction;

        colorMatrix.set(elements);
        return colorMatrix;
    }
}

Here is how you can set it up:

ImageView imageView = (ImageView)findViewById(R.id.imageView);
final BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.image);
imageView.setImageDrawable(drawable);
AlphaSatColorMatrixEvaluator evaluator = new AlphaSatColorMatrixEvaluator ();
final ColorMatrixColorFilter filter = new ColorMatrixColorFilter(evaluator.getColorMatrix());
drawable.setColorFilter(filter);

ObjectAnimator animator = ObjectAnimator.ofObject(filter, "colorMatrix", evaluator, evaluator.getColorMatrix());

animator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        drawable.setColorFilter (filter);
    }
});
animator.setDuration(1500);
animator.start();

And here is the result:

Problem

Has anyone looked into implementing the Loading images pattern from Google's latest Material Design guide. It's a recommended way that "illustrations and photographs may load and transition in three phases at staggered durations". Those being Opacity, Exposure and Saturation: I'm currently using the Volley NetworkImageView (actually a derived class from this). I'm sure it's got to be some variant of the answer to this question. I'm just not sure which classes/code to use for both the saturation and animation curves that are described.

Original source

Related problems