旋转图像。动画列表或动画旋转? (安卓)
Posted
技术标签:
【中文标题】旋转图像。动画列表或动画旋转? (安卓)【英文标题】:Rotating image. Animation list or animated rotate? (Android) 【发布时间】:2011-04-15 04:39:11 【问题描述】:我想创建一个旋转的进度图像,并想知道如何进行的最佳方式。我可以使其与动画列表一起使用,例如每 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 中发现了一些名为动画旋转的东西,但是如果我复制代码,则会出现编译器错误,抱怨 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" />
我也尝试过使用重复旋转动画(在动画资源文件夹中使用),但我实际上更喜欢动画列表版本的外观。
解决此问题的推荐方法是什么?
【问题讨论】:
【参考方案1】:Praveen 建议的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_
android:layout_
android:src="@drawable/ic_progress"
app:frameCount="8"
app:duration="1000"/>
文件res/anim/progress_anim.xml
:以上列出
【讨论】:
我无法使用android:visibility="invisible"
隐藏进度视图有什么原因吗?
这是一个很好的解释,谢谢!对于那些插值器有问题的人,我只使用了: a.setInterpolator(new LinearInterpolator());而且效果很好!
如何强制停止动画?
有用的进度myandroidwidgets.googlecode.com/svn/trunk/Custom_Progress_Bar/…
blog.mediarain.com/2011/04/…【参考方案2】:
您必须创建一个可绘制的 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】:我发现 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!
【讨论】:
【参考方案4】:在此处查看示例 http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html
具体来说: 进度条
-
增量
演示可以以单位递增或递减的大小旋转进度指示器。
光滑
演示用于指示通用“忙碌”消息的大小不断旋转的进度指示器。
对话框
演示 ProgressDialog,这是一个包含进度条的弹出对话框。此示例演示了确定和不确定的进度指示器。
在标题栏中
通过设置 WindowPolicy 的进度指示器功能,演示带有进度指示器的 Activity 屏幕。
【讨论】:
【参考方案5】:SACPK 的解决方案绝对有效。另一种解决方案是使用 <animated-rotate>
就像有问题的一样,并删除编译器抱怨的 android:framesCount="12"
android:frameDuration="100"
属性。即使对于我的 8 帧图像,它仍然有效。
但是,我还没有弄清楚如何控制动画的速度:(。
【讨论】:
【参考方案6】:感谢@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://***.com/a/21376008/5318303">@android-developer's answer on ***.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://***.com/a/10600736/5318303">@André's answer on ***.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 以获取完整(可能还有更多更新)源代码。
【讨论】:
以上是关于旋转图像。动画列表或动画旋转? (安卓)的主要内容,如果未能解决你的问题,请参考以下文章