android


How to properly create a customized animated drawable?


Background
I've been searching in plenty of places to find out how to animate a drawable without animating the view and without using the built in drawables.
The reason is that I will need to prepare a customized animation within the drawable, and I might have different requirements for it later.
For now, I'm making a basic animated drawable that just spins a given bitmap inside it.
I've set it on an imageView, but I wish to be able to use it on any kind of view, even customized views that have overridden the onDraw function.
The problem
I can't find out how to show the drawable without being cut, no matter what the size of the view is. Here's what I see:
The code
Here's the code:
private class CircularAnimatedDrawable extends Drawable implements Animatable {
private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator();
private static final int ANGLE_ANIMATOR_DURATION = 2000;
private final RectF fBounds = new RectF();
private float angle = 0;
private ObjectAnimator mObjectAnimatorAngle;
private final Paint mPaint;
private boolean mRunning;
private final Bitmap mBitmap;
public CircularAnimatedDrawable(final Bitmap bitmap) {
this.mBitmap = bitmap;
mPaint = new Paint();
setupAnimations();
}
public float getAngle() {
return this.angle;
}
public void setAngle(final float angle) {
this.angle = angle;
invalidateSelf();
}
#Override
public Callback getCallback() {
return mCallback;
}
#Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.restore();
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
fBounds.left = bounds.left;
fBounds.right = bounds.right;
fBounds.top = bounds.top;
fBounds.bottom = bounds.bottom;
}
private void setupAnimations() {
mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, "angle", 360f);
mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);
mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mObjectAnimatorAngle.start();
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
mObjectAnimatorAngle.cancel();
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
}
and the usage :
final ImageView imageView = (ImageView) findViewById(R.id.imageView);
final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.spinner_76_inner_holo);
final CircularAnimatedDrawable circularAnimatedDrawable = new CircularAnimatedDrawable(bitmap);
circularAnimatedDrawable.setCallback(imageView);
circularAnimatedDrawable.start();
imageView.setImageDrawable(circularAnimatedDrawable);
The question
How can I set it to make the drawable fit the view?
Should I use the bitmap size? the fBounds? both? Or maybe something else?
try this modified version of your Drawable:
class CircularAnimatedDrawable extends Drawable implements Animatable, TimeAnimator.TimeListener {
private static final float TURNS_PER_SECOND = 0.5f;
private Bitmap mBitmap;
private boolean mRunning;
private TimeAnimator mTimeAnimator = new TimeAnimator();
private Paint mPaint = new Paint();
private Matrix mMatrix = new Matrix();
public CircularAnimatedDrawable(final Bitmap bitmap) {
mBitmap = bitmap;
mTimeAnimator.setTimeListener(this);
}
#Override
public void draw(final Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
Log.d(TAG, "onBoundsChange " + bounds);
mMatrix.setRectToRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()),
new RectF(bounds),
Matrix.ScaleToFit.CENTER);
}
#Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
Rect b = getBounds();
mMatrix.postRotate(360 * TURNS_PER_SECOND * deltaTime / 1000, b.centerX(), b.centerY());
invalidateSelf();
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mTimeAnimator.start();
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
mTimeAnimator.cancel();
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
}
EDIT: version without Animator stuff (uses [un]scheduleSelf), NOTE it uses View's Drawable.Callback mechanism so it usually cannot be started directly from onCreate where View doesn't have attached Handler yet
class CircularAnimatedDrawable extends Drawable implements Animatable, Runnable {
private static final float TURNS_PER_SECOND = 0.5f;
private static final long DELAY = 50;
private Bitmap mBitmap;
private long mLastTime;
private boolean mRunning;
private Paint mPaint = new Paint();
private Matrix mMatrix = new Matrix();
public CircularAnimatedDrawable(final Bitmap bitmap) {
mBitmap = bitmap;
}
#Override
public void draw(final Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
Log.d(TAG, "onBoundsChange " + bounds);
mMatrix.setRectToRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()),
new RectF(bounds),
Matrix.ScaleToFit.CENTER);
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mLastTime = SystemClock.uptimeMillis();
scheduleSelf(this, 0);
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
unscheduleSelf(this);
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
#Override
public void run() {
long now = SystemClock.uptimeMillis();
Rect b = getBounds();
long deltaTime = now - mLastTime;
mLastTime = now;
mMatrix.postRotate(360 * TURNS_PER_SECOND * deltaTime / 1000, b.centerX(), b.centerY());
scheduleSelf(this, now + DELAY);
invalidateSelf();
}
}
ok, the fix is:
#Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle, fBounds.width() / 2 + fBounds.left, fBounds.height() / 2 + fBounds.top);
canvas.translate(fBounds.left, fBounds.top);
canvas.drawBitmap(mBitmap, null, new Rect(0, 0, (int) fBounds.width(), (int) fBounds.height()), mPaint);
canvas.restore();
}
#Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
#Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
It works fine. I hope it will be enough for the future changes.
EDIT: here's an optimization to the above, including all changes:
class CircularAnimatedDrawable extends Drawable implements Animatable {
private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator();
private static final int ANGLE_ANIMATOR_DURATION = 2000;
private float angle = 0;
private ObjectAnimator mObjectAnimatorAngle;
private final Paint mPaint;
private boolean mRunning;
private final Bitmap mBitmap;
private final Matrix mMatrix = new Matrix();
public CircularAnimatedDrawable(final Bitmap bitmap) {
this.mBitmap = bitmap;
mPaint = new Paint();
mPaint.setAntiAlias(true);
setupAnimations();
}
#SuppressWarnings("unused")
public float getAngle() {
return this.angle;
}
#SuppressWarnings("unused")
public void setAngle(final float angle) {
this.angle = angle;
invalidateSelf();
}
#Override
public void draw(final Canvas canvas) {
final Rect b = getBounds();
canvas.save();
canvas.rotate(angle, b.centerX(), b.centerY());
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
canvas.restore();
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
mMatrix.setRectToRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()), new RectF(bounds),
Matrix.ScaleToFit.CENTER);
}
#Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
#Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
private void setupAnimations() {
mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, "angle", 360f);
mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);
mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mObjectAnimatorAngle.start();
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
mObjectAnimatorAngle.cancel();
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
}

Related Links

How to get zoom and erase an imageview in android
Nested Json parsing with GSon
Unable to retrieve the access token. Status: 401ccess token on twitter android
Saving & restoring with Bundle
How to make zoomable LinearLayout? [closed]
call activity from a library project
Moving the image according to the Button click
What was the reason of “access denied” exception while executing Runtime.exec() on Android?
Add the traffic information to google navigation app
java.lang.RuntimeException :Unable to start activity ComponentInfo
Andengine: FontException Not enough space for letter [closed]
Why am I getting errors about API version?
andengine sprite move PathModifier and body
Bizarre exceptions being thrown in android activity
Data folder location in multi-user environment in Android 4.2
How to fetch the E Mail ID of A contact

Categories

HOME
oracle12c
polymer
ecmascript-6
graphics
spring-data-redis
replace
access
aws-cli
permissions
3d-reconstruction
iis-6
gluon-mobile
title
ycsb
naivebayes
gkturnbasedmatch
zerobrane
aws-sdk
smartclient
soci
shortest-path
google-maps-autocomplete
user-experience
undertow
string-concatenation
game-theory
bulletphysics
subclass
iis-express
panoramas
powershell-ise
kodi
linkedhashmap
reactjs.net
html-encode
batching
getlasterror
numpy-broadcasting
openerp-7
docfx
xcode7.3
zoomify
metawidget
urlsession
dcmtk
glm-math
optimizely
dynamic-linq
dtsearch
trojan
shinobi
sandcastle
embedding
cassia
httpruntime.cache
opl
computer-architecture
jdi
ons-api
iced-coffeescript
finalbuilder
viola-jones
haskell-warp
liquid-layout
grouping-sets
apache-spark-1.3
pseudo-class
lambda-architecture
lnk
dct
ios8-extension
twitter-bootstrap-rails
sbcl
android-audiorecord
accesscontrolexception
notorm
c++builder-xe5
away3d
model-driven-development
jeromq
kogrid
poker
octal
superscrollorama
swfobject
panda3d
qi4j
vfs
correlated-subquery
jdb
isa-swizzling
box2d-iphone
google-maps-mobile
codebase
squishit
undefined-index
aspmenu
web-garden
zend-rest
request-headers
perl5.12

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App