我想创建一个旋转的进度图像,并想知道什么是最好的方法。我可以使用动画列表,例如每100毫秒更换12个图像。这样可以正常工作,但创建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 / platforms ...)中,我在文件drawable / search_spinner.xml中找到了一个名为animated-rotate的东西,但如果我复制代码得到编译器错误抱怨android:framesCount和android: frameDuration(Eclipse中的Google API 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" />
我也尝试过使用重复旋转动画(在动画资源文件夹中使用),但我实际上更喜欢动画列表版本的外观。
解决此问题的推荐方法是什么?
答案 0 :(得分:61)
Rotate drawable
不会让你控制帧数。假设您要实现一个自定义加载器,该加载器由8个部分组成:
使用animation-list
方法,您需要手动创建8个以45*frameNumber
度旋转的帧。或者,您可以使用第一帧并为其设置旋转动画:
档案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
:上面列出
答案 1 :(得分:13)
您必须创建一个可绘制的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" />
答案 2 :(得分:7)
我发现vokilam的答案是创造一个漂亮的阶梯/交错动画的最佳选择。我找到了他的最终建议并制作了一个自定义小部件,我遇到的唯一问题是设置可见性不起作用,因为它是动画的,因此总是可见...
我调整了他的代码(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!
答案 3 :(得分:2)
在此处查看示例 http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html
具体为: 进度条
答案 4 :(得分:0)
SACPK的解决方案绝对有效。另一个解决方案是使用<animated-rotate>
就像问题一样,并删除编译器抱怨的android:framesCount="12"
android:frameDuration="100"
属性。它甚至可以用于我的8帧图像。
但是,我还没想出如何控制动画的速度:(。
答案 5 :(得分:0)
谢谢@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,以获取完整的(可能还要更新的)源代码。