Android 旋转图像。动画列表或动画旋转?(安卓)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3760381/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rotating image. Animation list or animated rotate? (Android)
提问by Gunnar Lium
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 100ms. This works fine, but it's quite tedious to create 12 images or for every size and resolution:
我想创建一个旋转的进度图像,想知道最好的方法是什么。我可以使它与动画列表一起使用,例如每 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" />
I suppose that an easier solution is to use one image per resolution, but rather rotate it for each frame. In the platform resources (android-sdk-windows/platforms...) I found something called animated-rotate in the file drawable/search_spinner.xml, but if I copy the code get a compiler error complaining about android:framesCount and android:frameDuration (Google APIs 2.2 in Eclipse):
我想一个更简单的解决方案是每个分辨率使用一个图像,而是为每一帧旋转它。在平台资源(android-sdk-windows/platforms...)中,我在文件 drawable/search_spinner.xml 中发现了一个叫做 animation-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" />
I have also tried using a repeating rotate animation (using in the anim resource folder), but I actually prefer the look of the animation list version.
我也尝试过使用重复旋转动画(在动画资源文件夹中使用),但我实际上更喜欢动画列表版本的外观。
What is the recommended way of solving this problem?
解决此问题的推荐方法是什么?
回答by vokilam
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:
Rotate drawable
Praveen 建议不会让您控制帧数。假设您要实现一个由 8 个部分组成的自定义加载器:
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:
使用animation-list
方法,您需要45*frameNumber
手动创建 8 个按度旋转的帧。或者,您可以使用第一帧并为其设置旋转动画:
File res/anim/progress_anim.xml
:
文件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
文件 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:
这将为您提供流畅的动画,而不是 8 步。为了解决这个问题,我们需要实现自定义插值器:
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
:
文件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
:
文件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
:
文件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
文件res/anim/progress_anim.xml
:上面列出
回答by Praveen
You have to create a drawable xml file like below:
您必须创建一个可绘制的 xml 文件,如下所示:
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" />
回答by user1504495
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...
我发现 vokilam 的答案是创建漂亮的阶梯/交错动画的最佳答案。我听取了他的最终建议并制作了一个自定义小部件,我遇到的唯一问题是设置可见性不起作用,因为它是动画的,因此始终可见......
I adjusted his code (ProgressView.java which I renamed StaggeredProgress.java) like this:
我像这样调整了他的代码(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();
}
}
This way setting the view's visibility starts and stops the animation as required...Many thanks again to vokilam!
通过这种方式设置视图的可见性可以根据需要启动和停止动画……再次感谢 vokilam!
回答by mishkin
see examples here http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html
在此处查看示例 http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/index.html
specifically: Progress Bar
特别是:进度条
- Incremental Demonstrates large and small rotating progress indicators that can be incremented or decremented in units.
- Smooth Demonstrates large and small continuously rotating progress indicators used to indicate a generic "busy" message.
- Dialogs Demonstrates a ProgressDialog, a popup dialog that hosts a progress bar. This example demonstrates both determinate and indeterminate progress indicators.
- In Title Bar Demonstrates an Activity screen with a progress indicator loaded by setting the WindowPolicy's progress indicator feature.
- Incremental 显示可以以单位递增或递减的大小旋转进度指示器。
- Smooth 展示用于指示一般“忙”消息的大小连续旋转的进度指示器。
- Dialogs 演示了一个 ProgressDialog,一个承载进度条的弹出对话框。此示例演示了确定和不确定的进度指示器。
- 在标题栏中演示带有进度指示器的活动屏幕,通过设置 WindowPolicy 的进度指示器功能加载。
回答by ernazm
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.
SACPK 的解决方案绝对有效。另一种解决方案可以是<animated-rotate>
像有问题一样使用并删除android:framesCount="12"
android:frameDuration="100"
编译器抱怨的属性。即使对于我的 8 帧图像,它仍然有效。
However, I havn't figured out how to control the speed of the animation :(.
但是,我还没有弄清楚如何控制动画的速度:(。
回答by Mir-Ismaili
Thank @vokilam. This similar solution (a custom view that rotates automatically) uses <animation-list>
dynamically in its implementation:
谢谢@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;
}
}
See Android-FramesAnimatorView on GitHubfor full (and probably more updated) source code.
请参阅GitHub 上的 Android-FramesAnimatorView 以获取完整(可能还有更多更新)的源代码。