有可能有一个动画drawable吗?

时间:2011-01-10 21:19:31

标签: android animation progress-bar drawable

是否可以创建具有某种动画的drawable,无论是逐帧动画,旋转等,都被定义为xml drawable并且可以由单个Drawable对象表示而无需处理用代码中的动画?

我想如何使用它: 我有一个列表,此列表中的每个项目可能在某个时候发生了一些事情。虽然它正在发生,但我希望有一个类似于不确定的ProgressBar的旋转进度动画。由于屏幕上可能还有其中的几个,我认为如果它们共享相同的Drawable,它们只需要在内存中有一个实例,它们的动画会被同步,所以你不会在各个点上有一堆旋转对象在旋转动画中。

我不喜欢这种方法。我只是想想一种最有效的方式来显示几个旋转进度动画,理想情况下将它们同步在一起,使它们在外观上保持一致。

由于

回应Sybiam的回答:

我尝试过实现RotateDrawable,但它没有旋转。

到目前为止,这是我的xml for the drawable:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
 android:drawable="@drawable/my_drawable_to_rotate"
 android:fromDegrees="0" 
 android:toDegrees="360"
 android:pivotX="50%"
 android:pivotY="50%"
 android:duration="800"
 android:visible="true" />

我尝试使用drawable作为ImageView的src和背景,并且两种方式都只产生非旋转图像。

是否有必须开始图像旋转的东西?

7 个答案:

答案 0 :(得分:16)

是的!我通过阅读ProgressBar代码发现的(未记录的)密钥是您必须在Drawable.setLevel()中调用onDraw()才能使<rotate>内容生效。 ProgressBar的工作方式与此类似(省略了额外的不重要代码):

可绘制的XML:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate
             android:drawable="@drawable/spinner_48_outer_holo"
             android:pivotX="50%"
             android:pivotY="50%"
             android:fromDegrees="0"
             android:toDegrees="1080" />
    </item>
    <item>
        <rotate
             android:drawable="@drawable/spinner_48_inner_holo"
             android:pivotX="50%"
             android:pivotY="50%"
             android:fromDegrees="720"
             android:toDegrees="0" />
    </item>
</layer-list>

onDraw()

    Drawable d = getDrawable();
    if (d != null)
    {
        // Translate canvas so a indeterminate circular progress bar with
        // padding rotates properly in its animation
        canvas.save();
        canvas.translate(getPaddingLeft(), getPaddingTop());

        long time = getDrawingTime();

        // I'm not sure about the +1.
        float prog = (float)(time % ANIM_PERIOD+1) / (float)ANIM_PERIOD; 
        int level = (int)(MAX_LEVEL * prog);
        d.setLevel(level);
        d.draw(canvas);

        canvas.restore();

        ViewCompat.postInvalidateOnAnimation(this);
    }

MAX_LEVEL是常量,总是10000(根据文档)。 ANIM_PERIOD是动画的句点,以毫秒为单位。

很遗憾,因为您需要修改onDraw(),所以不能将此drawable放在ImageView中,因为ImageView永远不会更改可绘制级别。但是,您可以从外部更改ImageView的可绘制级别。 ProgressBar(ab)使用AlphaAnimation来设置关卡。所以你会做这样的事情:

mMyImageView.setImageDrawable(myDrawable);

ObjectAnimator anim = ObjectAnimator.ofInt(myDrawable, "level", 0, MAX_LEVEL);
anim.setRepeatCount(ObjectAnimator.INFINITE);
anim.start();

它可能有效,但我没有测试过。

修改

实际上有一个ImageView.setImageLevel()方法,因此它可能很简单:

ObjectAnimator anim = ObjectAnimator.ofInt(myImageVew, "ImageLevel", 0, MAX_LEVEL);
anim.setRepeatCount(ObjectAnimator.INFINITE);
anim.start();

答案 1 :(得分:7)

Drawables

你去吧!这个RotateDrawable。我相信,从Doc开始它应该是非常紧张的前进。您可以在xml文件中定义所有内容,并将视图的背景设置为可绘制的xml。 /drawable/myrotate.xml - &gt; @绘制/ myrotate

编辑: 这是我在这里找到的答案。 Drawable Rotating around its center Android

编辑2: 你是对的,RotateDrawable好像坏了。我不知道我也试过了。我还没有成功使它成为动画。但我确实成功地轮换了它。你必须使用setLevel来旋转它。虽然看起来不太有用。我浏览了代码并且RotateDrawable甚至没有夸大动画持续时间,当前旋转似乎奇怪地使用关卡作为旋转度量。我相信你必须将它与AnimationDrawable一起使用,但在这里再次使用它。它只是为我坠毁。我还没有使用过这个功能,但计划将来使用它。我浏览了网页,RotateDrawable似乎与几乎每个Drawable对象都没有文档。

答案 2 :(得分:2)

这是一种可能的方法(特别是当你有一个Drawable设置并需要为它设置动画时)。我们的想法是包装drawable并用动画装饰它。在我的情况下,我不得不旋转它,所以下面你可以找到示例实现:

public class RotatableDrawable extends DrawableWrapper {

    private float rotation;
    private Rect bounds;
    private ObjectAnimator animator;
    private long defaultAnimationDuration;

    public RotatableDrawable(Resources resources, Drawable drawable) {
        super(vectorToBitmapDrawableIfNeeded(resources, drawable));
        bounds = new Rect();
        defaultAnimationDuration = resources.getInteger(android.R.integer.config_mediumAnimTime);
    }

    @Override
    public void draw(Canvas canvas) {
        copyBounds(bounds);
        canvas.save();
        canvas.rotate(rotation, bounds.centerX(), bounds.centerY());
        super.draw(canvas);
        canvas.restore();
    }

    public void rotate(float degrees) {
        rotate(degrees, defaultAnimationDuration);
    }

    public void rotate(float degrees, long millis) {
        if (null != animator && animator.isStarted()) {
            animator.end();
        } else if (null == animator) {
            animator = ObjectAnimator.ofFloat(this, "rotation", 0, 0);
            animator.setInterpolator(new AccelerateDecelerateInterpolator());
        }
        animator.setFloatValues(rotation, degrees);
        animator.setDuration(millis).start();
    }

    @AnimatorSetter
    public void setRotation(float degrees) {
        this.rotation = degrees % 360;
        invalidateSelf();
    }

    /**
     * Workaround for issues related to vector drawables rotation and scaling:
     * https://code.google.com/p/android/issues/detail?id=192413
     * https://code.google.com/p/android/issues/detail?id=208453
     */
    private static Drawable vectorToBitmapDrawableIfNeeded(Resources resources, Drawable drawable) {
        if (drawable instanceof VectorDrawable) {
            Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b);
            drawable.setBounds(0, 0, c.getWidth(), c.getHeight());
            drawable.draw(c);
            drawable = new BitmapDrawable(resources, b);
        }
        return drawable;
    }
}

你可以像这样使用它(旋转工具栏导航图标360度):

backIcon = new RotatableDrawable(getResources(), toolbar.getNavigationIcon().mutate());
toolbar.setNavigationIcon(backIcon);
backIcon.rotate(360);

添加一种无限期旋转的方法(setRepeatMode INFINITE for animator

并不难

答案 3 :(得分:1)

您可以从学习API Demos项目的ProgressBar2开始(它可以作为SDK的一部分使用)。特别要注意R.layout.progressbar_2

答案 4 :(得分:0)

private ValueAnimator rotateDrawable(RotateDrawable drawable, int fromDegree, int toDegree) {
    drawable.setFromDegrees(fromDegree);
    drawable.setToDegrees(toDegree);
    return ObjectAnimator.ofInt(drawable, "level", 0, 10000);
}

level是从0到100000的interpulting值。实际动画值由setter方法设置

答案 5 :(得分:0)

  

你可以使用stateListAnimator

    <ImageView
        android:id="@+id/your_id"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:src="@drawable/your_drawable"
        android:stateListAnimator="@anim/bounce" />
  

和反弹

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"

    android:interpolator="@android:anim/bounce">
    <translate
        android:duration="900"

        android:fromXDelta="100%p"
        android:toXDelta="0%p" />
</set>

答案 6 :(得分:0)

我将这篇帖子重新生动起来,只是将我的解决方案与可绘制的矢量一起发布:

因此,您需要在可绘制资源(@ drawable / ic_brush_24dp)中绘制1个矢量:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <group
        android:name="rotation"  <---- target name of animated-vector
        android:pivotX="12.0"
        android:pivotY="12.0"
        android:rotation="0.0">  <--- propertyName of animator
        <path
            android:fillColor="#FF000000"
            android:pathData="M7,14c-1.66,0 -3,1.34 -3,3 0,1.31 -1.16,2 -2,2 0.92,1.22 2.49,2 4,2 2.21,0 4,-1.79 4,-4 0,-1.66 -1.34,-3 -3,-3zM20.71,4.63l-1.34,-1.34c-0.39,-0.39 -1.02,-0.39 -1.41,0L9,12.25 11.75,15l8.96,-8.96c0.39,-0.39 0.39,-1.02 0,-1.41z" />
    </group>
</vector>

然后,您需要在动画器资源文件夹(@ animator / pendulum)中使用动画器

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="350"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:propertyName="rotation"
        android:valueFrom="0.0"
        android:valueTo="-90.0" />
</set>

然后,您需要在可绘制资源(@ drawable / test_anim_brush2)中使用动画矢量:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_brush_24dp">
    <target
        android:name="rotation"
        android:animation="@animator/pendulum" />
</animated-vector>

然后您需要扩展ImageView(因为这是我发现开始动画的唯一方法)

public class ImageView extends AppCompatImageView{
    public ImageView(Context context){
        super(context);
        init(context, null, 0);
    }
    public ImageView(Context context, AttributeSet attrs){
        super(context, attrs);
        init(context, attrs, 0);
    }
    public ImageView(Context context, AttributeSet attrs, int defStyleAttr){
        super(context, attrs, defStyleAttr);
        init(context, attrs, 0);
    }
    private void init(Context context, AttributeSet attrs, int defStyleAttr){

    }
@Override
protected void onAttachedToWindow(){
    super.onAttachedToWindow();
    Drawable d = getBackground();
    if(d instanceof AnimatedVectorDrawable){
        AnimatedVectorDrawable anim = (AnimatedVectorDrawable)d;
        anim.start(); 
    }        
}
@Override
protected void onDetachedFromWindow(){
    super.onDetachedFromWindow();
    Drawable d = getBackground();
    if(d instanceof AnimatedVectorDrawable){
        AnimatedVectorDrawable anim = (AnimatedVectorDrawable)d;
        anim.stop();
    }
}
}

然后将您的imageView添加到布局中:

    <ui.component.ImageView
    android:id="@+id/test_anim_brush"
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:layout_gravity="center_horizontal"
    android:background="@drawable/test_anim_brush2"/>

专业版:

  • 您可以使用对象动画师(持续时间,插值器等)在xml中完全定义动画
  • 处理所有可绘制的内容(只要在适当的位置添加启动动画器即可)

骗局

  • 仅适用于矢量
  • 仍然需要通过扩展类或在您认为聪明的时候添加自定义开始...