Alex Fu,您的文章非常有帮助,但可以进行一些改进。
每次在 onAnimationEnd 中重新启动动画是低效的。相反,在开始之前将动画重复计数设置为无限,然后当动作完成时,将重复计数设置为 0。动画将结束当前循环,然后调用 onAnimationEnd,您可以在其中调用 setActionView(null)。
我还遇到了 ActionBarSherlock 和本机 ICS ActionBar 的问题(至少在模拟器中,还没有在真实设备上测试过,因为我没有),动画的最后几帧与调用 setActionView(null) 后的静态按钮。我的解决方案是将正在动画的 ImageView 包装在 LinearLayout 中,然后在 LinearLayout 上调用 setVisibility(View.GONE)。如果您为视图设置动画,使用 setVisibility 隐藏它是行不通的,但隐藏它的父级会。
以下是所有相关代码:
layout/actionbar_progress.xml(请注意,我添加了 id,尽管只有 ImageView 需要一个):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/actionbar_progress_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/actionbar_progress_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:src="@drawable/ic_action_refresh"
style="@style/Widget.Sherlock.ActionButton"
/>
</LinearLayout>
动画/refresh_rotate.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:duration="1000"
android:interpolator="@android:anim/linear_interpolator"
android:repeatCount="infinite">
</rotate>
repeatCount 设置为无限,但这不是必需的,因为 java 代码必须将其设置为无限。
在 onCreateOptionsMenu 我有
...
menuInflater.inflate(R.menu.main, menu);
mRefreshItem = menu.findItem(R.id.menu_refresh);
...
这只是为了避免每次单击刷新按钮时都执行 findItem 。
在 onOptionsItemSelected 中:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.menu_refresh:
refresh();
break;
...
}
}
刷新方法只是一个 5 秒延迟的虚拟帖子;它调用 refreshAnim 来启动动画,然后在完成后调用 refreshAnimEnd 来停止动画:
private void refresh() {
refreshAnim();
getWindow().getDecorView().postDelayed(
new Runnable() {
@Override
public void run() {
refreshAnimEnd();
}
}, 5000);
}
以下是最重要的部分,附有评论:
private void refreshAnim() {
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//The inflated layout and loaded animation are put into members to avoid reloading them every time.
//For convenience, the ImageView is also extracted into a member
if (mRefreshIndeterminateProgressView == null || mRefreshRotateClockwise == null) {
mRefreshIndeterminateProgressView = inflater.inflate(R.layout.actionbar_progress, null);
mRefreshRotateClockwise = AnimationUtils.loadAnimation(this, R.anim.refresh_rotate);
mRefreshImage = mRefreshIndeterminateProgressView.findViewById(R.id.actionbar_progress_image);
}
//reset some stuff - make the animation infinite again,
//and make the containing view visible
mRefreshRotateClockwise.setRepeatCount(Animation.INFINITE);
mRefreshIndeterminateProgressView.setVisibility(View.VISIBLE);
mRefreshRotateClockwise.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
//This is important to avoid the overlapping problem.
//First hide the animated icon
//setActionView(null) does NOT hide it!
//as long as the animation is running, it will be visible
//hiding an animated view also doesn't work
//as long as the animation is running
//but hiding the parent does.
mRefreshIndeterminateProgressView.setVisibility(View.GONE);
//make the static button appear again
mRefreshItem.setActionView(null);
}
});
mRefreshItem.setActionView(mRefreshIndeterminateProgressView);
//everything is set up, start animating.
mRefreshImage.startAnimation(mRefreshRotateClockwise);
}
private void refreshAnimEnd() {
//sanity
if ( mRefreshImage == null || mRefreshItem == null ) {
return;
}
Animation anim = mRefreshImage.getAnimation();
//more sanity
if (anim != null) {
//let the animation finish nicely
anim.setRepeatCount(0);
} else {
//onAnimationEnd won't run in this case, so restore the static button
mRefreshItem.setActionView(null);
}
}