summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wei <markwei@google.com>2013-11-01 15:55:17 -0700
committerMark Wei <markwei@google.com>2013-11-04 10:57:01 -0800
commit5030ae34cd5978a8ab8a06f6c3b69b8645873122 (patch)
treef92417f29c823b422f868eb0c2ae0b9b357e4338
parent2e4d0863dba53435372ec96538f2ef3e1c3675bf (diff)
downloadbitmap-5030ae34cd5978a8ab8a06f6c3b69b8645873122.tar.gz
Modify ExtendedBitmapDrawable to be more flexible for clients.
Introducing ExtendedOptions: Clients can now specify the features they want out of a ExtendedBitmapDrawable: 1) Ordered display (decode aggregator) 2) Parallax 3) State changes (placeholder and/or progress bar) Clients can also specify parameters to those features. These parameters can be modified and changes take effect immediately. Change-Id: I15e4f1dbc20473db6798888b07b4bd34c712db21
-rw-r--r--res/values/colors.xml20
-rw-r--r--sample/res/drawable-hdpi/ic_placeholder.pngbin725 -> 550 bytes
-rw-r--r--sample/res/drawable-mdpi/ic_placeholder.pngbin626 -> 481 bytes
-rw-r--r--sample/res/drawable-xhdpi/ic_placeholder.pngbin840 -> 681 bytes
-rw-r--r--sample/res/drawable-xxhdpi/ic_placeholder.pngbin1382 -> 588 bytes
-rw-r--r--sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java2
-rw-r--r--sample/src/com/example/bitmapsample/MainActivity.java13
-rw-r--r--src/com/android/bitmap/DecodeTask.java3
-rw-r--r--src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java345
-rw-r--r--src/com/android/bitmap/drawable/TileDrawable.java36
10 files changed, 321 insertions, 98 deletions
diff --git a/res/values/colors.xml b/res/values/colors.xml
deleted file mode 100644
index c01dc56..0000000
--- a/res/values/colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2013 The Android Open Source Project
-
- 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.
--->
-<resources>
- <!-- Background color for images while they're loading -->
- <color name="bitmap_placeholder_background_color">#fff1f1f2</color>
-</resources>
diff --git a/sample/res/drawable-hdpi/ic_placeholder.png b/sample/res/drawable-hdpi/ic_placeholder.png
index 7b05b48..9e20328 100644
--- a/sample/res/drawable-hdpi/ic_placeholder.png
+++ b/sample/res/drawable-hdpi/ic_placeholder.png
Binary files differ
diff --git a/sample/res/drawable-mdpi/ic_placeholder.png b/sample/res/drawable-mdpi/ic_placeholder.png
index 752149d..339c316 100644
--- a/sample/res/drawable-mdpi/ic_placeholder.png
+++ b/sample/res/drawable-mdpi/ic_placeholder.png
Binary files differ
diff --git a/sample/res/drawable-xhdpi/ic_placeholder.png b/sample/res/drawable-xhdpi/ic_placeholder.png
index 92ed99b..ca79f26 100644
--- a/sample/res/drawable-xhdpi/ic_placeholder.png
+++ b/sample/res/drawable-xhdpi/ic_placeholder.png
Binary files differ
diff --git a/sample/res/drawable-xxhdpi/ic_placeholder.png b/sample/res/drawable-xxhdpi/ic_placeholder.png
index 18ac128..c2c2877 100644
--- a/sample/res/drawable-xxhdpi/ic_placeholder.png
+++ b/sample/res/drawable-xxhdpi/ic_placeholder.png
Binary files differ
diff --git a/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java b/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java
index eeec78f..dd1696c 100644
--- a/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java
+++ b/sample/src/com/example/bitmapsample/BitmapRequestKeyImpl.java
@@ -78,7 +78,7 @@ public class BitmapRequestKeyImpl implements RequestKey {
// Character difference between shortest and longest uri.
final long spread = 26;
// Maximum amount of time to sleep.
- final long max = 2;
+ final long max = 3;
final long duration = (long) ((float) (mUriString.length() % spread) / spread * max
* 1000);
try {
diff --git a/sample/src/com/example/bitmapsample/MainActivity.java b/sample/src/com/example/bitmapsample/MainActivity.java
index 96e99ec..cd8f6ac 100644
--- a/sample/src/com/example/bitmapsample/MainActivity.java
+++ b/sample/src/com/example/bitmapsample/MainActivity.java
@@ -18,6 +18,7 @@ package com.example.bitmapsample;
import android.app.Activity;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
@@ -29,11 +30,12 @@ import com.android.bitmap.BitmapCache;
import com.android.bitmap.DecodeAggregator;
import com.android.bitmap.UnrefedBitmapCache;
import com.android.bitmap.drawable.ExtendedBitmapDrawable;
+import com.android.bitmap.drawable.ExtendedBitmapDrawable.ExtendedOptions;
public class MainActivity extends Activity {
private ListView mListView;
- private final BitmapCache mCache = new UnrefedBitmapCache(TARGET_CACHE_SIZE_BYTES, 0.1f, 0);
+ private final BitmapCache mCache = new UnrefedBitmapCache(TARGET_CACHE_SIZE_BYTES, 0, 0);
private final DecodeAggregator mDecodeAggregator = new DecodeAggregator();
private static Drawable PLACEHOLDER;
@@ -107,9 +109,14 @@ public class MainActivity extends Activity {
v = (BitmapView) convertView;
} else {
v = new BitmapView(MainActivity.this);
+ ExtendedOptions opts = new ExtendedOptions(
+ ExtendedOptions.FEATURE_ORDERED_DISPLAY | ExtendedOptions.FEATURE_PARALLAX
+ | ExtendedOptions.FEATURE_STATE_CHANGES, PLACEHOLDER, PROGRESS);
+ opts.decodeAggregator = mDecodeAggregator;
+ opts.parallaxSpeedMultiplier = NORMAL_PARALLAX_MULTIPLIER;
+ opts.backgroundColor = Color.LTGRAY;
final ExtendedBitmapDrawable drawable = new ExtendedBitmapDrawable(getResources(),
- mCache, true /* limit density */, mDecodeAggregator, PLACEHOLDER, PROGRESS);
- drawable.setParallaxSpeedMultiplier(NORMAL_PARALLAX_MULTIPLIER);
+ mCache, true /* limit density */, opts);
v.setBasicBitmapDrawable(drawable);
v.setListView(mListView);
diff --git a/src/com/android/bitmap/DecodeTask.java b/src/com/android/bitmap/DecodeTask.java
index 87beac1..0d814c4 100644
--- a/src/com/android/bitmap/DecodeTask.java
+++ b/src/com/android/bitmap/DecodeTask.java
@@ -509,6 +509,9 @@ public class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
}
}
+ /**
+ * Parameters to pass to the DecodeTask.
+ */
public static class DecodeOptions {
/**
diff --git a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
index 6f52061..068fca2 100644
--- a/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
+++ b/src/com/android/bitmap/drawable/ExtendedBitmapDrawable.java
@@ -49,15 +49,13 @@ import com.android.bitmap.util.Trace;
public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
Runnable, Parallaxable, DecodeAggregator.Callback {
- // Ordered display.
- private DecodeAggregator mDecodeAggregator;
-
+ private final ExtendedOptions mOpts;
+
// Parallax.
- private float mParallaxFraction = 0.5f;
- private float mParallaxSpeedMultiplier;
private static final float DECODE_VERTICAL_CENTER = 1f / 3;
+ private float mParallaxFraction = 1f / 2;
- // Placeholder and progress.
+ // State changes.
private static final int LOAD_STATE_UNINITIALIZED = 0;
private static final int LOAD_STATE_NOT_YET_LOADED = 1;
private static final int LOAD_STATE_LOADING = 2;
@@ -73,27 +71,32 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
public static final String TAG = ExtendedBitmapDrawable.class.getSimpleName();
public ExtendedBitmapDrawable(final Resources res, final BitmapCache cache,
- final boolean limitDensity, final DecodeAggregator decodeAggregator,
- final Drawable placeholder, final Drawable progress) {
+ final boolean limitDensity, final ExtendedOptions opts) {
super(res, cache, limitDensity);
- // Ordered display.
- this.mDecodeAggregator = decodeAggregator;
+ opts.validate();
+ mOpts = opts;
// Placeholder and progress.
- final int fadeOutDurationMs = res.getInteger(R.integer.bitmap_fade_animation_duration);
- final int tileColor = res.getColor(R.color.bitmap_placeholder_background_color);
- mProgressDelayMs = res.getInteger(R.integer.bitmap_progress_animation_delay);
-
- int placeholderSize = res.getDimensionPixelSize(R.dimen.placeholder_size);
- mPlaceholder = new Placeholder(placeholder.getConstantState().newDrawable(res), res,
- placeholderSize, placeholderSize, fadeOutDurationMs, tileColor);
- mPlaceholder.setCallback(this);
-
- int progressBarSize = res.getDimensionPixelSize(R.dimen.progress_bar_size);
- mProgress = new Progress(progress.getConstantState().newDrawable(res), res,
- progressBarSize, progressBarSize, fadeOutDurationMs, tileColor);
- mProgress.setCallback(this);
+ if ((opts.features & ExtendedOptions.FEATURE_STATE_CHANGES) != 0) {
+ final int fadeOutDurationMs = res.getInteger(R.integer.bitmap_fade_animation_duration);
+ mProgressDelayMs = res.getInteger(R.integer.bitmap_progress_animation_delay);
+
+ // Placeholder is not optional because of backgroundColor.
+ int placeholderSize = res.getDimensionPixelSize(R.dimen.placeholder_size);
+ mPlaceholder = new Placeholder(
+ opts.placeholder != null ? opts.placeholder.getConstantState().newDrawable(res)
+ : null, res, placeholderSize, placeholderSize, fadeOutDurationMs, opts);
+ mPlaceholder.setCallback(this);
+
+ // Progress bar is optional.
+ if (opts.progressBar != null) {
+ int progressBarSize = res.getDimensionPixelSize(R.dimen.progress_bar_size);
+ mProgress = new Progress(opts.progressBar.getConstantState().newDrawable(res), res,
+ progressBarSize, progressBarSize, fadeOutDurationMs, opts);
+ mProgress.setCallback(this);
+ }
+ }
}
@Override
@@ -102,9 +105,12 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
invalidateSelf();
}
- public void setParallaxSpeedMultiplier(final float parallaxSpeedMultiplier) {
- mParallaxSpeedMultiplier = parallaxSpeedMultiplier;
- invalidateSelf();
+ /**
+ * Get the ExtendedOptions used to instantiate this ExtendedBitmapDrawable. Any changes made to
+ * the parameters inside the options will take effect immediately.
+ */
+ public ExtendedOptions getExtendedOptions() {
+ return mOpts;
}
/**
@@ -121,8 +127,8 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
return;
}
- if (mCurrKey != null && mDecodeAggregator != null) {
- mDecodeAggregator.forget(mCurrKey);
+ if (mCurrKey != null && getDecodeAggregator() != null) {
+ getDecodeAggregator().forget(mCurrKey);
}
mHandler.removeCallbacks(this);
@@ -166,7 +172,7 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
@Override
protected float getDrawVerticalOffsetMultiplier() {
- return mParallaxSpeedMultiplier;
+ return mOpts.parallaxSpeedMultiplier;
}
@Override
@@ -174,28 +180,45 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
return DECODE_VERTICAL_CENTER;
}
+ private DecodeAggregator getDecodeAggregator() {
+ return mOpts.decodeAggregator;
+ }
+
+ /**
+ * Instead of overriding this method, subclasses should override {@link #onDraw(Canvas)}.
+ *
+ * The reason for this is that we need the placeholder and progress bar to be drawn over our
+ * content. Those two drawables fade out, giving the impression that our content is fading in.
+ */
@Override
- public void draw(final Canvas canvas) {
+ public final void draw(final Canvas canvas) {
final Rect bounds = getBounds();
if (bounds.isEmpty()) {
return;
}
- super.draw(canvas);
+ onDraw(canvas);
// Draw the two possible overlay layers in reverse-priority order.
// (each layer will no-op the draw when appropriate)
// This ordering means cross-fade transitions are just fade-outs of each layer.
- mProgress.draw(canvas);
- mPlaceholder.draw(canvas);
+ if (mProgress != null) mProgress.draw(canvas);
+ if (mPlaceholder != null) mPlaceholder.draw(canvas);
+ }
+
+ /**
+ * Overriding this method to add your own custom drawing.
+ */
+ protected void onDraw(final Canvas canvas) {
+ super.draw(canvas);
}
@Override
public void setAlpha(int alpha) {
final int old = mPaint.getAlpha();
super.setAlpha(alpha);
- mPlaceholder.setAlpha(alpha);
- mProgress.setAlpha(alpha);
+ if (mPlaceholder != null) mPlaceholder.setAlpha(alpha);
+ if (mProgress != null) mProgress.setAlpha(alpha);
if (alpha != old) {
invalidateSelf();
}
@@ -204,23 +227,22 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
@Override
public void setColorFilter(ColorFilter cf) {
super.setColorFilter(cf);
- mPlaceholder.setColorFilter(cf);
- mProgress.setColorFilter(cf);
+ if (mPlaceholder != null) mPlaceholder.setColorFilter(cf);
+ if (mProgress != null) mProgress.setColorFilter(cf);
invalidateSelf();
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
-
- mPlaceholder.setBounds(bounds);
- mProgress.setBounds(bounds);
+ if (mPlaceholder != null) mPlaceholder.setBounds(bounds);
+ if (mProgress != null) mProgress.setBounds(bounds);
}
@Override
public void onDecodeBegin(final RequestKey key) {
- if (mDecodeAggregator != null) {
- mDecodeAggregator.expect(key, this);
+ if (getDecodeAggregator() != null) {
+ getDecodeAggregator().expect(key, this);
} else {
onBecomeFirstExpected(key);
}
@@ -246,8 +268,8 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
@Override
public void onDecodeComplete(final RequestKey key, final ReusableBitmap result) {
- if (mDecodeAggregator != null) {
- mDecodeAggregator.execute(key, new Runnable() {
+ if (getDecodeAggregator() != null) {
+ getDecodeAggregator().execute(key, new Runnable() {
@Override
public void run() {
ExtendedBitmapDrawable.super.onDecodeComplete(key, result);
@@ -265,8 +287,8 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
@Override
public void onDecodeCancel(final RequestKey key) {
- if (mDecodeAggregator != null) {
- mDecodeAggregator.forget(key);
+ if (getDecodeAggregator() != null) {
+ getDecodeAggregator().forget(key);
}
super.onDecodeCancel(key);
}
@@ -294,26 +316,34 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
// UNINITIALIZED will not have a fancy transition. This allows list item binds to
// cached data to take immediate effect without unnecessary whizzery.
case LOAD_STATE_UNINITIALIZED:
- mPlaceholder.reset();
- mProgress.reset();
+ if (mPlaceholder != null) mPlaceholder.reset();
+ if (mProgress != null) mProgress.reset();
break;
case LOAD_STATE_NOT_YET_LOADED:
- mPlaceholder.setPulseEnabled(true);
- mPlaceholder.setVisible(true);
- mProgress.setVisible(false);
+ if (mPlaceholder != null) {
+ mPlaceholder.setPulseEnabled(true);
+ mPlaceholder.setVisible(true);
+ }
+ if (mProgress != null) mProgress.setVisible(false);
break;
case LOAD_STATE_LOADING:
- mPlaceholder.setVisible(false);
- mProgress.setVisible(true);
+ if (mProgress == null) {
+ // Stay in same visual state as LOAD_STATE_NOT_YET_LOADED.
+ break;
+ }
+ if (mPlaceholder != null) mPlaceholder.setVisible(false);
+ if (mProgress != null) mProgress.setVisible(true);
break;
case LOAD_STATE_LOADED:
- mPlaceholder.setVisible(false);
- mProgress.setVisible(false);
+ if (mPlaceholder != null) mPlaceholder.setVisible(false);
+ if (mProgress != null) mProgress.setVisible(false);
break;
case LOAD_STATE_FAILED:
- mPlaceholder.setPulseEnabled(false);
- mPlaceholder.setVisible(true);
- mProgress.setVisible(false);
+ if (mPlaceholder != null) {
+ mPlaceholder.setPulseEnabled(false);
+ mPlaceholder.setVisible(true);
+ }
+ if (mProgress != null) mProgress.setVisible(false);
break;
}
Trace.endSection();
@@ -334,10 +364,10 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
private boolean mPulseEnabled = true;
private float mPulseAlphaFraction = 1f;
- public Placeholder(Drawable placeholder, Resources res,
- int placeholderWidth, int placeholderHeight, int fadeOutDurationMs,
- int tileColor) {
- super(placeholder, placeholderWidth, placeholderHeight, tileColor, fadeOutDurationMs);
+ public Placeholder(Drawable placeholder, Resources res, int placeholderWidth,
+ int placeholderHeight, int fadeOutDurationMs, ExtendedOptions opts) {
+ super(placeholder, placeholderWidth, placeholderHeight, fadeOutDurationMs, opts);
+
mPulseAnimator = ValueAnimator.ofInt(55, 255)
.setDuration(res.getInteger(R.integer.bitmap_placeholder_animation_duration));
mPulseAnimator.setRepeatCount(ValueAnimator.INFINITE);
@@ -405,8 +435,8 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
public Progress(Drawable progress, Resources res,
int progressBarWidth, int progressBarHeight, int fadeOutDurationMs,
- int tileColor) {
- super(progress, progressBarWidth, progressBarHeight, tileColor, fadeOutDurationMs);
+ ExtendedOptions opts) {
+ super(progress, progressBarWidth, progressBarHeight, fadeOutDurationMs, opts);
mRotateAnimator = ValueAnimator.ofInt(0, 10000)
.setDuration(res.getInteger(R.integer.bitmap_progress_animation_duration));
@@ -446,6 +476,193 @@ public class ExtendedBitmapDrawable extends BasicBitmapDrawable implements
}
return changed;
}
+ }
+
+ /**
+ * This class contains the features a client can specify, and arguments to those features.
+ * Clients can later retrieve the ExtendedOptions from an ExtendedBitmapDrawable and change the
+ * parameters, which will be reflected immediately.
+ */
+ public static class ExtendedOptions {
+
+ /**
+ * Summary:
+ * This feature enables you to draw decoded bitmap in order on the screen, to give the
+ * visual effect of a single decode thread.
+ *
+ * <p/>
+ * Explanation:
+ * Since DecodeTasks are asynchronous, multiple tasks may finish decoding at different
+ * times. To have a smooth user experience, provide a shared {@link DecodeAggregator} to all
+ * the ExtendedBitmapDrawables, and the decode aggregator will hold finished decodes so they
+ * come back in order.
+ *
+ * <p/>
+ * Pros:
+ * Visual consistency. Images are not popping up randomly all over the place.
+ *
+ * <p/>
+ * Cons:
+ * Artificial delay. Images are not drawn as soon as they are decoded. They must wait
+ * for their turn.
+ *
+ * <p/>
+ * Requirements:
+ * Set {@link #decodeAggregator} to a shared {@link DecodeAggregator}.
+ */
+ public static final int FEATURE_ORDERED_DISPLAY = 1;
+
+ /**
+ * Summary:
+ * This feature enables the image to move in parallax as the user scrolls, to give visual
+ * flair to your images.
+ *
+ * <p/>
+ * Explanation:
+ * When the user scrolls D pixels in the vertical direction, this ExtendedBitmapDrawable
+ * shifts its Bitmap f(D) pixels in the vertical direction before drawing to the screen.
+ * Depending on the function f, the parallax effect can give varying interesting results.
+ *
+ * <p/>
+ * Pros:
+ * Visual pop and playfulness. Feeling of movement. Pleasantly surprise your users.
+ *
+ * <p/>
+ * Cons:
+ * Some users report motion sickness with certain speed multiplier values. Decode height
+ * must be greater than visual bounds to account for the parallax. This uses more memory and
+ * decoding time.
+ *
+ * <p/>
+ * Requirements:
+ * Set {@link #parallaxSpeedMultiplier} to the ratio between the decoded height and the
+ * visual bound height. Call {@link ExtendedBitmapDrawable#setParallaxFraction(float)} when
+ * the user scrolls, usually accomplished in your view's
+ * {@link android.view.View#onDraw(android.graphics.Canvas)} method.
+ */
+ public static final int FEATURE_PARALLAX = 1 << 1;
+
+ /**
+ * Summary:
+ * This feature enables fading in between multiple decode states, to give smooth transitions
+ * to and from the placeholder, progress bars, and decoded image.
+ *
+ * <p/>
+ * Explanation:
+ * The states are: {@link ExtendedBitmapDrawable#LOAD_STATE_UNINITIALIZED},
+ * {@link ExtendedBitmapDrawable#LOAD_STATE_NOT_YET_LOADED},
+ * {@link ExtendedBitmapDrawable#LOAD_STATE_LOADING},
+ * {@link ExtendedBitmapDrawable#LOAD_STATE_LOADED}, and
+ * {@link ExtendedBitmapDrawable#LOAD_STATE_FAILED}. These states affect whether the
+ * placeholder and/or the progress bar is showing and animating. We first show the
+ * pulsating placeholder when an image begins decoding. After 2 seconds, we fade in a
+ * spinning progress bar. When the decode completes, we fade in the image.
+ *
+ * <p/>
+ * Pros:
+ * Smooth, beautiful transitions avoid perceived jank. Progress indicator informs users that
+ * work is being done and the app is not stalled.
+ *
+ * <p/>
+ * Cons:
+ * Very fast decodes' short decode time would be eclipsed by the animation duration. Static
+ * placeholder could be accomplished by {@link BasicBitmapDrawable} without the added
+ * complexity of states.
+ *
+ * <p/>
+ * Requirements:
+ * Set {@link #backgroundColor} to the color used for the background of the placeholder and
+ * progress bar. Use the alternative constructor to populate {@link #placeholder} and
+ * {@link #progressBar}.
+ */
+ public static final int FEATURE_STATE_CHANGES = 1 << 2;
+
+ /**
+ * Non-changeable bit field describing the features you want the
+ * {@link ExtendedBitmapDrawable} to support.
+ *
+ * <p/>
+ * Example:
+ * <code>
+ * opts.features = FEATURE_ORDERED_DISPLAY | FEATURE_PARALLAX | FEATURE_STATE_CHANGES;
+ * </code>
+ */
+ public final int features;
+
+ /**
+ * Required field if {@link #FEATURE_ORDERED_DISPLAY} is supported.
+ */
+ public DecodeAggregator decodeAggregator = null;
+
+ /**
+ * Required field if {@link #FEATURE_PARALLAX} is supported.
+ *
+ * A value of 1.5f gives a subtle parallax, and is a good value to
+ * start with. 2.0f gives a more obvious parallax, arguably exaggerated. Some users report
+ * motion sickness with 2.0f. A value of 1.0f is synonymous with no parallax. Be careful not
+ * to set too high a value, since we will start cropping the widths if the image's height is
+ * not sufficient.
+ */
+ public float parallaxSpeedMultiplier = 1;
+
+ /**
+ * Optional field if {@link #FEATURE_STATE_CHANGES} is supported.
+ *
+ * See {@link android.graphics.Color}.
+ */
+ public int backgroundColor = 0;
+
+ /**
+ * Optional non-changeable field if {@link #FEATURE_STATE_CHANGES} is supported.
+ */
+ public final Drawable placeholder;
+
+ /**
+ * Optional non-changeable field if {@link #FEATURE_STATE_CHANGES} is supported.
+ */
+ public final Drawable progressBar;
+
+ /**
+ * Use this constructor when all the feature parameters are changeable.
+ */
+ public ExtendedOptions(final int features) {
+ this(features, null, null);
+ }
+
+ /**
+ * Use this constructor when you have to specify non-changeable feature parameters.
+ */
+ public ExtendedOptions(final int features, final Drawable placeholder,
+ final Drawable progressBar) {
+ this.features = features;
+ this.placeholder = placeholder;
+ this.progressBar = progressBar;
+ }
+ /**
+ * Validate this ExtendedOptions instance to make sure that all the required fields are set
+ * for the requested features.
+ *
+ * This will throw an IllegalStateException if validation fails.
+ */
+ private void validate()
+ throws IllegalStateException {
+ if ((features & FEATURE_ORDERED_DISPLAY) != 0 && decodeAggregator == null) {
+ throw new IllegalStateException(
+ "ExtendedOptions: To support FEATURE_ORDERED_DISPLAY, "
+ + "decodeAggregator must be set.");
+ }
+ if ((features & FEATURE_PARALLAX) != 0 && parallaxSpeedMultiplier == 0) {
+ throw new IllegalStateException(
+ "ExtendedOptions: To support FEATURE_PARALLAX, "
+ + "parallaxSpeedMultiplier must be set.");
+ }
+ if ((features & FEATURE_STATE_CHANGES) != 0 && backgroundColor == 0
+ && placeholder == null) {
+ throw new IllegalStateException(
+ "ExtendedOptions: To support FEATURE_STATE_CHANGES, "
+ + "either backgroundColor or placeholder must be set.");
+ }
+ }
}
}
diff --git a/src/com/android/bitmap/drawable/TileDrawable.java b/src/com/android/bitmap/drawable/TileDrawable.java
index 40d3e16..c756422 100644
--- a/src/com/android/bitmap/drawable/TileDrawable.java
+++ b/src/com/android/bitmap/drawable/TileDrawable.java
@@ -24,6 +24,8 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import com.android.bitmap.drawable.ExtendedBitmapDrawable.ExtendedOptions;
+
/**
* A drawable that wraps another drawable and places it in the center of this space. This drawable
* allows a background color for the "tile", and has a fade-out transition when
@@ -31,6 +33,7 @@ import android.graphics.drawable.Drawable;
*/
public class TileDrawable extends Drawable implements Drawable.Callback {
+ private final ExtendedOptions mOpts;
private final Paint mPaint = new Paint();
private final Drawable mInner;
private final int mInnerWidth;
@@ -38,13 +41,15 @@ public class TileDrawable extends Drawable implements Drawable.Callback {
protected final ValueAnimator mFadeOutAnimator;
- public TileDrawable(Drawable inner, int innerWidth, int innerHeight,
- int backgroundColor, int fadeOutDurationMs) {
- mInner = inner.mutate();
+ public TileDrawable(Drawable inner, int innerWidth, int innerHeight, int fadeOutDurationMs,
+ ExtendedOptions opts) {
+ mOpts = opts;
+ mInner = inner != null ? inner.mutate() : null;
mInnerWidth = innerWidth;
mInnerHeight = innerHeight;
- mPaint.setColor(backgroundColor);
- mInner.setCallback(this);
+ if (inner != null) {
+ mInner.setCallback(this);
+ }
mFadeOutAnimator = ValueAnimator.ofInt(255, 0)
.setDuration(fadeOutDurationMs);
@@ -67,6 +72,10 @@ public class TileDrawable extends Drawable implements Drawable.Callback {
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
+ if (mInner == null) {
+ return;
+ }
+
if (bounds.isEmpty()) {
mInner.setBounds(0, 0, 0, 0);
} else {
@@ -81,8 +90,11 @@ public class TileDrawable extends Drawable implements Drawable.Callback {
if (!isVisible() && mPaint.getAlpha() == 0) {
return;
}
+ final int alpha = mPaint.getAlpha();
+ mPaint.setColor(mOpts.backgroundColor);
+ mPaint.setAlpha(alpha);
canvas.drawRect(getBounds(), mPaint);
- mInner.draw(canvas);
+ if (mInner != null) mInner.draw(canvas);
}
@Override
@@ -98,7 +110,7 @@ public class TileDrawable extends Drawable implements Drawable.Callback {
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
- mInner.setColorFilter(cf);
+ if (mInner != null) mInner.setColorFilter(cf);
}
@Override
@@ -116,7 +128,7 @@ public class TileDrawable extends Drawable implements Drawable.Callback {
@Override
public boolean setVisible(boolean visible, boolean restart) {
- mInner.setVisible(visible, restart);
+ if (mInner != null) mInner.setVisible(visible, restart);
final boolean changed = super.setVisible(visible, restart);
if (changed) {
if (isVisible()) {
@@ -136,14 +148,18 @@ public class TileDrawable extends Drawable implements Drawable.Callback {
@Override
protected boolean onLevelChange(int level) {
- return mInner.setLevel(level);
+ if (mInner != null)
+ return mInner.setLevel(level);
+ else {
+ return super.onLevelChange(level);
+ }
}
/**
* Changes the alpha on just the inner wrapped drawable.
*/
public void setInnerAlpha(int alpha) {
- mInner.setAlpha(alpha);
+ if (mInner != null) mInner.setAlpha(alpha);
}
@Override