Je veux créer une image de progrès en rotation et me demander quelle est la meilleure façon de procéder. Je peux le faire fonctionner avec une liste d'animation avec par exemple 12 images changeant toutes les 100ms. Cela fonctionne bien, mais il est assez fastidieux de créer 12 images ou pour chaque taille et résolution:
<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" />
Je suppose qu'une solution plus simple consiste à utiliser une image par résolution, mais plutôt à la faire pivoter pour chaque image. Dans les ressources de la plate-forme (Android-sdk-windows/plateformes ...), j'ai trouvé quelque chose appelé Animated-Rotation dans le fichier drawable/search_spinner.xml, mais si je copie le code, une erreur du compilateur se plaint d'Android: framesCount et Android: frameDuration (Google API 2.2 dans 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" />
J'ai également essayé d'utiliser une animation de rotation répétée (à utiliser dans le dossier de ressources anim), mais je préfère en réalité l'aspect de la version de liste d'animation.
Quelle est la méthode recommandée pour résoudre ce problème?
Rotate drawable
suggéré par Praveen ne vous donnera pas le contrôle du nombre d'images. Supposons que vous souhaitiez implémenter un chargeur personnalisé composé de 8 sections:
En utilisant l'approche animation-list
, vous devez créer 8 images pivotées manuellement de 45*frameNumber
degrés. Alternativement, vous pouvez utiliser la 1ère image et y définir l'animation de rotation:
Fichier 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" />
Fichier MainActivity.Java
Animation a = AnimationUtils.loadAnimation(getContext(), R.anim.progress_anim);
a.setDuration(1000);
imageView.startAnimation(a);
Cela vous donnera une animation fluide au lieu de 8 étapes. Pour résoudre ce problème, nous devons implémenter un interpolateur personnalisé:
a.setInterpolator(new Interpolator() {
private final int frameCount = 8;
@Override
public float getInterpolation(float input) {
return (float)Math.floor(input*frameCount)/frameCount;
}
});
Aussi, vous pouvez créer un widget personnalisé:
Fichier 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>
Fichier 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);
}
}
Fichier 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"/>
Fichier res/anim/progress_anim.xml
: répertorié ci-dessus
Vous devez créer un fichier XML dessinable comme ci-dessous:
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" />
J'ai trouvé que la réponse de vokilam était la meilleure pour créer une animation de Nice pas à pas/décalée. Je suis allé chercher sa dernière suggestion et j'ai créé un widget personnalisé. Le seul problème que j'ai rencontré était que la visibilité ne fonctionnait pas car elle était animée et donc toujours visible ...
J'ai ajusté son code (ProgressView.Java que j'ai renommé StaggeredProgress.Java) comme ceci:
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();
}
}
De cette façon, définir la visibilité de la vue démarre et arrête l'animation selon les besoins ... Merci encore à vokilam!
voir les exemples ici http://developer.Android.com/resources/samples/ApiDemos/src/com/example/Android/apis/view/index.html
en particulier: Barre de progression
Merci @vokilam. Cette solution similaire (une vue personnalisée à rotation automatique) utilise <animation-list>
de manière dynamique dans son implémentation:
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;
}
}
Voir Android-FramesAnimatorView sur GitHub pour obtenir le code source complet (et probablement plus mis à jour).
La solution de SACPK fonctionne définitivement. Une autre solution peut être d'utiliser <animated-rotate>
comme dans la question et de supprimer les attributs Android:framesCount="12"
Android:frameDuration="100"
pour ceux que le compilateur se plaint. Cela fonctionne toujours même pour mon image de 8 images.
Cependant, je n'ai pas compris comment contrôler la vitesse de l'animation :(.