Вращающееся изображение. Список анимации или анимированный поворот? (Android) - PullRequest
29 голосов
/ 21 сентября 2010

Я хочу создать вращающееся изображение прогресса и задаться вопросом, как лучше всего продолжить. Я могу заставить его работать со списком анимаций, например, с изменением 12 изображений каждые 100 мс. Это прекрасно работает, но довольно утомительно создавать 12 изображений для любого размера и разрешения:

<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 / platform ...) я обнаружил нечто, называемое animated-rotate, в файле drawable / search_spinner.xml, но если я скопирую код, получится ошибка компилятора с жалобами на android: framesCount and android: frameDuration (API Google 2.2 в Eclipse):

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

62 голосов
/ 21 февраля 2013

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: перечислено выше

13 голосов
/ 21 сентября 2010

Вы должны создать нарисованный 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" />
7 голосов
/ 07 мая 2013

Мне показалось, что ответ Вокилама - лучший способ создать приятную пошаговую / ступенчатую анимацию.Я пошел за его последним предложением и создал собственный виджет, единственная проблема, с которой я столкнулся, заключалась в том, что настройка видимости не работала, потому что она была анимированной и, следовательно, всегда была видимой ...

Я настроил его код (ProgressView.java, который я переименовал в StaggeredProgress.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!

2 голосов
/ 21 ноября 2010

см. Примеры здесь http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html

, в частности: индикатор выполнения

  1. Инкремент Показывает большие и маленькие вращающиеся индикаторы прогресса, которые можно увеличивать или уменьшать в единицах.
  2. Smooth Демонстрирует большие и маленькие непрерывно вращающиеся индикаторы прогресса, используемые для указания общего сообщения «занято».
  3. Диалоги Демонстрирует ProgressDialog, всплывающее диалоговое окно, в котором находится индикатор выполнения.Этот пример демонстрирует как детерминированные, так и неопределенные индикаторы прогресса.
  4. В строке заголовка Демонстрирует экран активности с индикатором прогресса, загруженным с помощью функции индикатора прогресса WindowPolicy.
0 голосов
/ 11 августа 2018

Спасибо @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 на GitHub для полного (и, возможно, более обновленного) исходного кода.

0 голосов
/ 28 октября 2011

Решение SACPK определенно работает.Другим решением может быть использование <animated-rotate>, как и в вопросе, и удаление атрибутов android:framesCount="12" android:frameDuration="100" для тех, на которые жалуется компилятор.Это все еще работает даже для моего 8-кадрового изображения.

Однако я не понял, как управлять скоростью анимации: (.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...