Rotating image. Animation list or animated rotate? (Android)

前端 未结 6 790
甜味超标
甜味超标 2020-11-30 21:17

I want to create a rotating progress image, and wonder what\'s the best way to proceed. I can make it work with an animation list with for example 12 images changing every 1

相关标签:
6条回答
  • 2020-11-30 21:56

    Rotate drawable suggested by Praveen won't give you control of frame count. Let's assume you want to implement a custom loader which consists from 8 sections:

    gif_icon

    Using animation-list approach, you need to create 8 frames rotated by 45*frameNumber degrees manually. Alternatively, you can use 1st frame and set rotation animation to it:

    progress_icon

    File 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" />
    

    File MainActivity.java

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

    This will give you smooth animation instead of 8-stepped. To fix this we need to implement custom interpolator:

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

    Also you can create a custom widget:

    File 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>
    

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

    File 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"/>
    

    File res/anim/progress_anim.xml: listed above

    0 讨论(0)
  • 2020-11-30 21:56

    see examples here http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html

    specifically: Progress Bar

    1. Incremental Demonstrates large and small rotating progress indicators that can be incremented or decremented in units.
    2. Smooth Demonstrates large and small continuously rotating progress indicators used to indicate a generic "busy" message.
    3. Dialogs Demonstrates a ProgressDialog, a popup dialog that hosts a progress bar. This example demonstrates both determinate and indeterminate progress indicators.
    4. In Title Bar Demonstrates an Activity screen with a progress indicator loaded by setting the WindowPolicy's progress indicator feature.
    0 讨论(0)
  • 2020-11-30 21:56

    SACPK's solution definitely works. Another solution can be to use <animated-rotate> just like in question and remove android:framesCount="12" android:frameDuration="100" attributes for those the compiler complains. It still works even for my 8-frame image.

    However, I havn't figured out how to control the speed of the animation :(.

    0 讨论(0)
  • 2020-11-30 22:14

    Thank @vokilam. This similar solution (a custom view that rotates automatically) uses <animation-list> dynamically in its implementation:

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

    See Android-FramesAnimatorView on GitHub for full (and probably more updated) source code.

    0 讨论(0)
  • 2020-11-30 22:18

    You have to create a drawable xml file like below:

    Code:

    <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" />
    
    0 讨论(0)
  • 2020-11-30 22:19

    I found vokilam's answer to be the best one to create a nice stepped/staggered animation. I went for his final suggestion and made a custom widget, the only problem I encountered was that setting visibility wouldn't work because it was animated and thus would always be visible...

    I adjusted his code (ProgressView.java which I renamed StaggeredProgress.java) like this:

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

    This way setting the view's visibility starts and stops the animation as required...Many thanks again to vokilam!

    0 讨论(0)
提交回复
热议问题