代码之家  ›  专栏  ›  技术社区  ›  Gunnar Lium

旋转图像。动画列表还是动画旋转?(安卓)

  •  29
  • Gunnar Lium  · 技术社区  · 14 年前

    <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
    <item android:drawable="@drawable/ic_loading_grey_on_black_01" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_02" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_03" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_04" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_05" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_06" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_07" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_08" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_09" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_10" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_11" android:duration="100" />
    <item android:drawable="@drawable/ic_loading_grey_on_black_12" android:duration="100" />
    

    我想一个更简单的解决方案是每分辨率使用一个图像,而不是为每帧旋转一个图像。在平台资源(android sdk windows/platforms…)中,我在drawable/search_spinner.xml文件中找到了一个名为animated rotate的东西,但是如果我复制了代码,就会得到一个编译器错误,抱怨android:framesCount和android:frameDuration(Eclipse中的Google APIs 2.2):

    <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/spinner_black_20"
    android:pivotX="50%"
    android:pivotY="50%"
    android:framesCount="12"
    android:frameDuration="100" />
    

    我也尝试过使用重复的旋转动画(在anim资源文件夹中使用),但实际上我更喜欢动画列表版本的外观。

    6 回复  |  直到 14 年前
        1
  •  64
  •   vokilam    12 年前

    Rotate drawable Praveen的建议不会让你控制帧数。假设您希望实现一个自定义加载程序,它由8个部分组成:

    gif_icon

    animation-list 方法,您需要创建8个旋转的帧 45*frameNumber 手动角度。或者,可以使用第1帧并为其设置旋转动画:

    progress_icon

    文件 res/anim/progress_anim.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <rotate
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="infinite" />
    

    文件 MainActivity.java

    Animation a = AnimationUtils.loadAnimation(getContext(), R.anim.progress_anim);
    a.setDuration(1000);
    imageView.startAnimation(a);
    

    这将为您提供平滑的动画,而不是8步。要解决此问题,我们需要实现自定义插值器:

    a.setInterpolator(new Interpolator() {
        private final int frameCount = 8;
    
        @Override
        public float getInterpolation(float input) {
            return (float)Math.floor(input*frameCount)/frameCount;
        }
    });
    

    文件 res/values/attrs.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="ProgressView">
            <attr name="frameCount" format="integer"/>
            <attr name="duration" format="integer" />
        </declare-styleable>
    </resources>
    

    文件 ProgressView.java

    public class ProgressView extends ImageView {
    
        public ProgressView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            setAnimation(attrs);
        }
    
        public ProgressView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setAnimation(attrs);
        }
    
        public ProgressView(Context context) {
            super(context);
        }
    
        private void setAnimation(AttributeSet attrs) {
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ProgressView);
            int frameCount = a.getInt(R.styleable.ProgressView_frameCount, 12);  
            int duration = a.getInt(R.styleable.ProgressView_duration, 1000);
            a.recycle();
    
            setAnimation(frameCount, duration);
        }
    
        public void setAnimation(final int frameCount, final int duration) {
            Animation a = AnimationUtils.loadAnimation(getContext(), R.anim.progress_anim);
            a.setDuration(duration);
            a.setInterpolator(new Interpolator() {
    
                @Override
                public float getInterpolation(float input) {
                    return (float)Math.floor(input*frameCount)/frameCount;
                }
            });
            startAnimation(a);
        }
    }
    

    activity_main.xml :

    <com.example.widget.ProgressView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_progress" 
        app:frameCount="8"
        app:duration="1000"/>
    

    文件 res/anim/progress_anim.xml文件

        2
  •  13
  •   Mohammad Reza Norouzi Praveen    11 年前

    必须创建一个可绘制的xml文件,如下所示:

    代码:

    <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:pivotX="50%" android:pivotY="50%" android:fromDegrees="0"
    android:toDegrees="360" android:drawable="@drawable/imagefile_to_rotate" />
    
        3
  •  7
  •   user1504495    12 年前

    我发现沃基兰的答案是最好的一个创建一个不错的阶梯/交错动画。我采纳了他的最后建议,制作了一个自定义小部件,我遇到的唯一问题是设置可见性不起作用,因为它是动画的,因此总是可见的。。。

    我调整了他的代码(ProgressView.java,我将其重命名为staggeddprogress.java),如下所示:

    public class StaggeredProgress extends ImageView {
    
    private Animation staggered;
    
    public StaggeredProgress(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setAnimation(attrs);
    }
    
    public StaggeredProgress(Context context, AttributeSet attrs) {
        super(context, attrs);
        setAnimation(attrs);
    }
    
    public StaggeredProgress(Context context) {
        super(context);
    }
    
    private void setAnimation(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.StaggeredProgress);
        int frameCount = a.getInt(R.styleable.StaggeredProgress_frameCount, 12);  
        int duration = a.getInt(R.styleable.StaggeredProgress_duration, 1000);
        a.recycle();
    
        setAnimation(frameCount, duration);
    }
    
    public void setAnimation(final int frameCount, final int duration) {
        Animation a = AnimationUtils.loadAnimation(getContext(), R.anim.progress_anim);
        a.setDuration(duration);
        a.setInterpolator(new Interpolator() {
    
            @Override
            public float getInterpolation(float input) {
                return (float)Math.floor(input*frameCount)/frameCount;
            }
        });
        staggered = a;
        //startAnimation(a);
    }
    
    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if( visibility == View.VISIBLE )
            startAnimation(staggered);
        else
            clearAnimation();
    
    }
    
    
    }
    

    这样设置视图的可见性可以根据需要启动和停止动画…再次感谢vokilam!

        4
  •  2
  •   mishkin    14 年前

    参见此处的示例 http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html

    进度条

    1. 增量 演示可以以单位递增或递减的大小旋转进度指示器。
    2. 光滑的 演示用于指示一般“忙”消息的大小连续旋转进度指示器。
    3. 对话框 演示ProgressDialog,它是一个弹出式对话框,承载一个进度条。这个例子演示了确定的和不确定的进度指标。
    4. 演示通过设置WindowPolicy的进度指示器功能加载进度指示器的活动屏幕。
        5
  •  0
  •   ernazm    13 年前

    萨克的解决方案肯定有效。另一个解决方案是 <animated-rotate> 就像在讨论中一样 android:framesCount="12" android:frameDuration="100"

    但是,我还没有想出如何控制动画的速度:(。

        6
  •  -1
  •   Mir-Ismaili    7 年前

    谢谢@vokilam。类似的解决方案(自动旋转的自定义视图)使用 <animation-list>

    public class FramesAnimatorView extends AppCompatImageView {
        private int framesCount;
        private int duration;
        private Bitmap frameBitmap;
    
        public FramesAnimatorView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context, attrs);
        }
    
        public FramesAnimatorView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context, attrs);
        }
    
        public FramesAnimatorView(Context context) { super(context); }
    
        private void init(Context context, AttributeSet attrs) {
            final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FramesAnimatorView);
            framesCount = typedArray.getInt(R.styleable.FramesAnimatorView_framesCount, 12);
            duration = typedArray.getInt(R.styleable.FramesAnimatorView_duration, 1200);
            typedArray.recycle();
    
            // Method 1: Use <rotate> as Animation (RotateAnimation) and startAnimation() (Rotate view itself).
            //method1(framesCount, duration);
    
            // Method 2: Use <rotate> as Drawable (RotateDrawable) and ObjectAnimator. Usable for API 21+ (because of using RotateDrawable.setDrawable).
            //method2();
    
            // Method 3 (Recommended): Use <animation-list> (AnimationDrawable) dynamically.
            final int frameDuration = this.duration / framesCount;
            final AnimationDrawable animationDrawable = (AnimationDrawable) getDrawable();
    
            for (int i = 0; i < framesCount; i++)
                animationDrawable.addFrame(
                        new RotatedDrawable(frameBitmap, i * 360f / framesCount, getResources()),
                        frameDuration);
    
            animationDrawable.start();
        }
    
        @Override public void setImageResource(int resId) { //info();
            frameBitmap = BitmapFactory.decodeResource(getResources(), resId);
            super.setImageDrawable(new AnimationDrawable());
        }
    
        @Override public void setImageDrawable(@Nullable Drawable drawable) { //info();
            frameBitmap = drawableToBitmap(drawable);
            super.setImageDrawable(new AnimationDrawable());
        }
    
        @Override public void setImageBitmap(Bitmap bitmap) { //info();
            frameBitmap = bitmap;
            super.setImageDrawable(new AnimationDrawable());
        }
    
        /**
         * See <a href="https://stackoverflow.com/a/21376008/5318303">@android-developer's answer on stackoverflow.com</a>.
         */
        private static class RotatedDrawable extends BitmapDrawable {
            private final float degrees;
            private int pivotX;
            private int pivotY;
    
            RotatedDrawable(Bitmap bitmap, float degrees, Resources res) {
                super(res, bitmap);
                pivotX = bitmap.getWidth() / 2;
                pivotY = bitmap.getHeight() / 2;
                this.degrees = degrees;
            }
    
            @Override public void draw(final Canvas canvas) {
                canvas.save();
                canvas.rotate(degrees, pivotX, pivotY);
                super.draw(canvas);
                canvas.restore();
            }
        }
    
        /**
         * See <a href="https://stackoverflow.com/a/10600736/5318303">@André's answer on stackoverflow.com</a>.
         */
        @NonNull private static Bitmap drawableToBitmap(Drawable drawable) {
            final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        }
    }
    

    Android-FramesAnimatorView on GitHub 对于完整的(可能更新更多的)源代码。