如何正确创建自定义动画drawable?

时间:2014-09-08 08:55:17

标签: android animation android-drawable bounds

背景

我一直在很多地方搜索,以了解如何使用内置的drawable来动画可绘制的不使用动画视图和不使用

原因是我需要在drawable中准备一个自定义动画,稍后我可能会有不同的要求。

现在,我正在创建一个基本的动画drawable,只是在其中旋转一个给定的位图。

我已将它设置在imageView上,但我希望能够在任何类型的视图上使用它,甚至是已覆盖onDraw函数的自定义视图。

问题

无论视图的大小如何,我都无法知道如何在不切割的情况下显示可绘制的内容。这就是我所看到的:

enter image description here

代码

以下是代码:

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;
    }

}

和用法:

    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);

问题

如何设置它以使drawable适合视图?

我应该使用位图大小吗? fBounds?都?或者别的什么?

2 个答案:

答案 0 :(得分:2)

好的,修复是:

    @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();
    }

工作正常。我希望这对未来的改变是足够的。

编辑:这是对上述内容的优化,包括所有更改:

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;
    }

}

答案 1 :(得分:1)

尝试使用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;
    }
}
编辑:没有Animator的版本(使用[un] scheduleSelf),注意它使用View的Drawable.Callback机制,所以它通常不能直接从onCreate启动,而View不会#39 ; t已经附加了Handler

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();
    }
}