AnimatedRotateDrawable.java revision c2974809373697147cbe5754835cc871fb93aef1
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.graphics.drawable; 18 19import android.graphics.Canvas; 20import android.graphics.Rect; 21import android.graphics.ColorFilter; 22import android.content.res.Resources; 23import android.content.res.TypedArray; 24import android.util.AttributeSet; 25import android.util.TypedValue; 26import android.util.Log; 27import android.os.SystemClock; 28import org.xmlpull.v1.XmlPullParser; 29import org.xmlpull.v1.XmlPullParserException; 30 31import java.io.IOException; 32 33import com.android.internal.R; 34 35/** 36 * @hide 37 */ 38public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable, 39 Animatable { 40 41 private AnimatedRotateState mState; 42 private boolean mMutated; 43 private float mCurrentDegrees; 44 private float mIncrement; 45 private boolean mRunning; 46 47 public AnimatedRotateDrawable() { 48 this(null, null); 49 } 50 51 private AnimatedRotateDrawable(AnimatedRotateState rotateState, Resources res) { 52 mState = new AnimatedRotateState(rotateState, this, res); 53 init(); 54 } 55 56 private void init() { 57 final AnimatedRotateState state = mState; 58 mIncrement = 360.0f / (float) state.mFramesCount; 59 final Drawable drawable = state.mDrawable; 60 if (drawable != null) { 61 drawable.setFilterBitmap(true); 62 if (drawable instanceof BitmapDrawable) { 63 ((BitmapDrawable) drawable).setAntiAlias(true); 64 } 65 } 66 } 67 68 public void draw(Canvas canvas) { 69 int saveCount = canvas.save(); 70 71 final AnimatedRotateState st = mState; 72 final Drawable drawable = st.mDrawable; 73 final Rect bounds = drawable.getBounds(); 74 75 int w = bounds.right - bounds.left; 76 int h = bounds.bottom - bounds.top; 77 78 float px = st.mPivotXRel ? (w * st.mPivotX) : st.mPivotX; 79 float py = st.mPivotYRel ? (h * st.mPivotY) : st.mPivotY; 80 81 canvas.rotate(mCurrentDegrees, px, py); 82 83 drawable.draw(canvas); 84 85 canvas.restoreToCount(saveCount); 86 } 87 88 public void start() { 89 if (!mRunning) { 90 mRunning = true; 91 nextFrame(); 92 } 93 } 94 95 public void stop() { 96 mRunning = false; 97 unscheduleSelf(this); 98 } 99 100 public boolean isRunning() { 101 return mRunning; 102 } 103 104 private void nextFrame() { 105 unscheduleSelf(this); 106 scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration); 107 } 108 109 public void run() { 110 // TODO: This should be computed in draw(Canvas), based on the amount 111 // of time since the last frame drawn 112 mCurrentDegrees += mIncrement; 113 if (mCurrentDegrees > (360.0f - mIncrement)) { 114 mCurrentDegrees = 0.0f; 115 } 116 invalidateSelf(); 117 nextFrame(); 118 } 119 120 @Override 121 public boolean setVisible(boolean visible, boolean restart) { 122 mState.mDrawable.setVisible(visible, restart); 123 boolean changed = super.setVisible(visible, restart); 124 if (visible) { 125 if (changed || restart) { 126 mCurrentDegrees = 0.0f; 127 nextFrame(); 128 } 129 } else { 130 unscheduleSelf(this); 131 } 132 return changed; 133 } 134 135 /** 136 * Returns the drawable rotated by this RotateDrawable. 137 */ 138 public Drawable getDrawable() { 139 return mState.mDrawable; 140 } 141 142 @Override 143 public int getChangingConfigurations() { 144 return super.getChangingConfigurations() 145 | mState.mChangingConfigurations 146 | mState.mDrawable.getChangingConfigurations(); 147 } 148 149 public void setAlpha(int alpha) { 150 mState.mDrawable.setAlpha(alpha); 151 } 152 153 public void setColorFilter(ColorFilter cf) { 154 mState.mDrawable.setColorFilter(cf); 155 } 156 157 public int getOpacity() { 158 return mState.mDrawable.getOpacity(); 159 } 160 161 public void invalidateDrawable(Drawable who) { 162 if (mCallback != null) { 163 mCallback.invalidateDrawable(this); 164 } 165 } 166 167 public void scheduleDrawable(Drawable who, Runnable what, long when) { 168 if (mCallback != null) { 169 mCallback.scheduleDrawable(this, what, when); 170 } 171 } 172 173 public void unscheduleDrawable(Drawable who, Runnable what) { 174 if (mCallback != null) { 175 mCallback.unscheduleDrawable(this, what); 176 } 177 } 178 179 @Override 180 public boolean getPadding(Rect padding) { 181 return mState.mDrawable.getPadding(padding); 182 } 183 184 @Override 185 public boolean isStateful() { 186 return mState.mDrawable.isStateful(); 187 } 188 189 @Override 190 protected void onBoundsChange(Rect bounds) { 191 mState.mDrawable.setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom); 192 } 193 194 @Override 195 public int getIntrinsicWidth() { 196 return mState.mDrawable.getIntrinsicWidth(); 197 } 198 199 @Override 200 public int getIntrinsicHeight() { 201 return mState.mDrawable.getIntrinsicHeight(); 202 } 203 204 @Override 205 public ConstantState getConstantState() { 206 if (mState.canConstantState()) { 207 mState.mChangingConfigurations = super.getChangingConfigurations(); 208 return mState; 209 } 210 return null; 211 } 212 213 @Override 214 public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) 215 throws XmlPullParserException, IOException { 216 217 final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedRotateDrawable); 218 219 super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedRotateDrawable_visible); 220 221 TypedValue tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotX); 222 final boolean pivotXRel = tv.type == TypedValue.TYPE_FRACTION; 223 final float pivotX = pivotXRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); 224 225 tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY); 226 final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; 227 final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); 228 229 final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12); 230 final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150); 231 232 final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0); 233 Drawable drawable = null; 234 if (res > 0) { 235 drawable = r.getDrawable(res); 236 } 237 238 a.recycle(); 239 240 int outerDepth = parser.getDepth(); 241 int type; 242 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && 243 (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 244 245 if (type != XmlPullParser.START_TAG) { 246 continue; 247 } 248 249 if ((drawable = Drawable.createFromXmlInner(r, parser, attrs)) == null) { 250 Log.w("drawable", "Bad element under <animated-rotate>: " 251 + parser .getName()); 252 } 253 } 254 255 if (drawable == null) { 256 Log.w("drawable", "No drawable specified for <animated-rotate>"); 257 } 258 259 final AnimatedRotateState rotateState = mState; 260 rotateState.mDrawable = drawable; 261 rotateState.mPivotXRel = pivotXRel; 262 rotateState.mPivotX = pivotX; 263 rotateState.mPivotYRel = pivotYRel; 264 rotateState.mPivotY = pivotY; 265 rotateState.mFramesCount = framesCount; 266 rotateState.mFrameDuration = frameDuration; 267 268 init(); 269 270 if (drawable != null) { 271 drawable.setCallback(this); 272 } 273 } 274 275 @Override 276 public Drawable mutate() { 277 if (!mMutated && super.mutate() == this) { 278 mState.mDrawable.mutate(); 279 mMutated = true; 280 } 281 return this; 282 } 283 284 final static class AnimatedRotateState extends Drawable.ConstantState { 285 Drawable mDrawable; 286 287 int mChangingConfigurations; 288 289 boolean mPivotXRel; 290 float mPivotX; 291 boolean mPivotYRel; 292 float mPivotY; 293 int mFrameDuration; 294 int mFramesCount; 295 296 private boolean mCanConstantState; 297 private boolean mCheckedConstantState; 298 299 public AnimatedRotateState(AnimatedRotateState source, AnimatedRotateDrawable owner, 300 Resources res) { 301 if (source != null) { 302 if (res != null) { 303 mDrawable = source.mDrawable.getConstantState().newDrawable(res); 304 } else { 305 mDrawable = source.mDrawable.getConstantState().newDrawable(); 306 } 307 mDrawable.setCallback(owner); 308 mPivotXRel = source.mPivotXRel; 309 mPivotX = source.mPivotX; 310 mPivotYRel = source.mPivotYRel; 311 mPivotY = source.mPivotY; 312 mFramesCount = source.mFramesCount; 313 mFrameDuration = source.mFrameDuration; 314 mCanConstantState = mCheckedConstantState = true; 315 } 316 } 317 318 @Override 319 public Drawable newDrawable() { 320 return new AnimatedRotateDrawable(this, null); 321 } 322 323 @Override 324 public Drawable newDrawable(Resources res) { 325 return new AnimatedRotateDrawable(this, res); 326 } 327 328 @Override 329 public int getChangingConfigurations() { 330 return mChangingConfigurations; 331 } 332 333 public boolean canConstantState() { 334 if (!mCheckedConstantState) { 335 mCanConstantState = mDrawable.getConstantState() != null; 336 mCheckedConstantState = true; 337 } 338 339 return mCanConstantState; 340 } 341 } 342} 343