I have an AnimationDrawable, which plays a frame animation and then the next Activity is started with a circular reveal animation:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
AnimationDrawable animationDrawable =
(AnimationDrawable) mAnimationSceneImage.getDrawable();
animationDrawable.start();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
startMapActivityWithAnimation();
}
}, 1000);
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void startMapActivityWithAnimation() {
int colorFrom = ContextCompat.getColor(this, R.color.window_background_splash);
int colorTo = ContextCompat.getColor(this, android.R.color.white);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mCircularRevealView.setBackgroundColor((Integer) valueAnimator.getAnimatedValue());
}
});
int cx = mCircularRevealView.getWidth() / 2;
int cy = mCircularRevealView.getHeight() / 2;
float finalRadius = Math.max(cx, cy);
Animator circularRevealAnimation = ViewAnimationUtils
.createCircularReveal(mCircularRevealView, cx, cy, 0, finalRadius);
mCircularRevealView.setVisibility(View.VISIBLE);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(200);
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
mLogoImage.animate()
.alpha(0.0f)
.setDuration(200)
.setInterpolator(new AccelerateInterpolator())
.start();
}
#Override
public void onAnimationEnd(Animator animation) {
startMapActivity();
}
});
animatorSet.playTogether(colorAnimation, circularRevealAnimation);
animatorSet.start();
}
The animations run perfectly on a normal device. I also have some Espresso UI tests, so I've disabled all the animations in the developer options screen on the test device and now I'm getting the following exception:
java.lang.IllegalStateException: Animator has already started, cannot change it now!
at android.view.RenderNodeAnimator.checkMutable(RenderNodeAnimator.java:149)
at android.view.RenderNodeAnimator.setDuration(RenderNodeAnimator.java:324)
at android.view.RenderNodeAnimator.setDuration(RenderNodeAnimator.java:322)
at android.animation.AnimatorSet.updateAnimatorsDuration(AnimatorSet.java:760)
at android.animation.AnimatorSet.getTotalDuration(AnimatorSet.java:1721)
at android.animation.AnimatorSet.forceToEnd(AnimatorSet.java:446)
at android.animation.AnimatorSet.doAnimationFrame(AnimatorSet.java:981)
at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:145)
at android.animation.AnimationHandler.-wrap2(Unknown Source:0)
at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:54)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:884)
at android.view.Choreographer.doCallbacks(Choreographer.java:698)
at android.view.Choreographer.doFrame(Choreographer.java:630)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:872)
at android.os.Handler.handleCallback(Handler.java:769)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6540)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Do you have any suggestions how to fix this problem?
I met the same problem and I have resolved it. You can't use method setDuration in class AnimatorSet, it may cause this problem on devices 8.0. You can use method setDuration in each Animator instead.
private void startMapActivityWithAnimation() {
int colorFrom = ContextCompat.getColor(this, R.color.window_background_splash);
int colorTo = ContextCompat.getColor(this, android.R.color.white);
ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
colorAnimation.setDuration(200);
colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mCircularRevealView.setBackgroundColor((Integer) valueAnimator.getAnimatedValue());
}
});
int cx = mCircularRevealView.getWidth() / 2;
int cy = mCircularRevealView.getHeight() / 2;
float finalRadius = Math.max(cx, cy);
Animator circularRevealAnimation = ViewAnimationUtils
.createCircularReveal(mCircularRevealView, cx, cy, 0, finalRadius);
circularRevealAnimation.setDuration(200);
mCircularRevealView.setVisibility(View.VISIBLE);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
mLogoImage.animate()
.alpha(0.0f)
.setDuration(200)
.setInterpolator(new AccelerateInterpolator())
.start();
}
#Override
public void onAnimationEnd(Animator animation) {
startMapActivity();
}
});
animatorSet.playTogether(colorAnimation, circularRevealAnimation);
animatorSet.start();
}
What about checking if the animator has already started?
if (!animatorSet.isStarted()) {
animatorSet.start();
}
You could enable the animations for that specific test in the #Before method. After the test is done, you could disable the animations again in the #After method. That way your subsequent tests will not get affected.
You could also create a test Rule to enable/disable animations for a specific class as shown here
Related
I'm trying to fade out items when it's removed then collapse recycleview. But it doesn't work.
Please help me
for(Object appData : mInvisibleEntries){
mVisibleEntries.remove(appData);
}
for(View view:viewHoldersList){
Animation a = new AlphaAnimation(1.00f, 0.00f);
a.setDuration(2000);
view.startAnimation(a);
}
notifyItemRangeRemoved(MAX_VISIBLE_APPS_COUNT + 1, mInvisibleEntries.size());
collapseRv();
private void collapseRv(){
final int initialHeight = mRecyclerView.getMeasuredHeight();
ValueAnimator valueAnimator = ValueAnimator.ofInt(initialHeight,HEIGHT_VIEW_APPDATA*3+HEIGHT_SECTION_FOOTER);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mRecyclerView.getLayoutParams().height = (int) animation.getAnimatedValue();
mRecyclerView.requestLayout();
}
});
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.setDuration(2000);
valueAnimator.start();
}
To make it simlpe, You can use library like https://github.com/wasabeef/recyclerview-animators
The following code is working when the supplied view is a Button but when it's a LinearLayout nothing happens (Background color doesn't change).
If I manually set the color on the LinearLayout without the ValueAnimator it works. I also tried using a ObjectAnimator but the result looked terrible.
An example call:
playBlinkAnimation(view, activity.getResources().getColor(R.color.transparent), activity.getResources().getColor(R.color.colorRed), 5000);
view.getBackground().setColorFilter(activity.getResources().getColor(R.color.colorRed), PorterDuff.Mode.DARKEN);
private void playBlinkAnimation(final View view, int colorFrom, int colorTo, int duration) {
ValueAnimator colorAnimation = new ValueAnimator();
colorAnimation.setIntValues(colorFrom, colorTo);
colorAnimation.setEvaluator(new ArgbEvaluator());
colorAnimation.setDuration(duration);
colorAnimation.setRepeatCount(ValueAnimator.INFINITE);
colorAnimation.setRepeatMode(ValueAnimator.REVERSE);
colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animator) {
view.getBackground().setColorFilter((int) animator.getAnimatedValue(), PorterDuff.Mode.DARKEN);
}
});
colorAnimation.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
view.getBackground().setColorFilter(null);
}
#Override
public void onAnimationCancel(Animator animation) {
view.getBackground().setColorFilter(null);
view.setAlpha(1);
view.getBackground().setColorFilter(null);
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
colorAnimation.start();
}
You can try using AnimatorSet and it's playTogether function. It takes an array of Animators, and it's designed to run multiple animations together, but it should work with only one animation as well.
Hope that helps you!
Since you seem to want your layout to transition from transparent background to opaque red, you should animate the layout's alpha parameter
ObjectAnimator animator = ObjectAnimator.ofInt(view, "alpha", 0, 1);
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
animator.start();
Now I have to create an animation of a spinning fan. When user click POWER ON button, this fan begin to spin, then keep it's spinning speed at some level.When user click POWER OFF button, it slowing down then it stop.
I make some code as follows:
ImageView mFanImageview;
private ValueAnimator mFanValueAnimator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_centripetal_particle);
ButterKnife.bind(this);
}
private void startFan() {
mFanValueAnimator = new ValueAnimator();
mFanValueAnimator.setDuration(1000);
mFanValueAnimator.setFloatValues(mFanImageview.getRotation(), mFanImageview.getRotation() + 360);
mFanValueAnimator.setInterpolator(new AccelerateInterpolator());
mFanValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mFanImageview.setRotation((Float) animation.getAnimatedValue());
}
});
mFanValueAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
mFanValueAnimator = new ValueAnimator();
mFanValueAnimator.setDuration(500);
mFanValueAnimator.setFloatValues(mFanImageview.getRotation(), mFanImageview.getRotation() + 360);
mFanValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mFanValueAnimator.setRepeatMode(ValueAnimator.RESTART);
mFanValueAnimator.setInterpolator(new LinearInterpolator());
mFanValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mFanImageview.setRotation((Float) animation.getAnimatedValue());
}
});
mFanValueAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationCancel(Animator animation) {
stopFanValueAnimator().start();
}
});
mFanValueAnimator.start();
}
#Override
public void onAnimationCancel(Animator animation) {
stopFanValueAnimator().start();
}
});
mFanValueAnimator.start();
}
private ValueAnimator stopFanValueAnimator() {
ValueAnimator stopAnimator = new ValueAnimator();
stopAnimator.setDuration(1000);
stopAnimator.setFloatValues(mFanImageview.getRotation(), mFanImageview.getRotation() + 360);
stopAnimator.setInterpolator(new DecelerateInterpolator());
stopAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mFanImageview.setRotation((Float) animation.getAnimatedValue());
}
});
return stopAnimator;
}
#OnClick(R.id.stop_button)
public void onStopButtonClicked() {
mFanValueAnimator.cancel();
}
#OnClick(R.id.start_button)
public void onStartButtonClicked() {
startFan();
}
The animation above seems ok, but I found that the fan spinning speed at the end of AccelerateInterpolator animator is hard to match the beginning of LinearInterpolator.I have to adjust duration of LinearInterpolator animator carefully.
How can I get the update rate at the end of AccelerateInterpolator animator then set the right duration of LinearInterpolator animator?
I think the best approach is set accelerate interpolator initially, and on repeat again set LinearInterpolator. Now in case of repeat put a flag for handling switch off. If the switch off demanded then change value of switch Off flag that will apply Decelerate interpolator and make sure that the animation will not run again.
mFanValueAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationRepeat(Animator animation) {
if (switchOff) {
animation.setInterpolator(new DecelerateInterpolator());
} else {
animation.setInterpolator(new LinearInterpolator());
}
}
});
I put three ObjectAnimator into AnimatorSet ,when I cancel AnimatorSet or AnimatorSet finishes ,i need record the getAnimatedValue . But when I cancel the AnimatorSet, a ObjectAnimator maybe not had runned and i can call isRunning in onAnimationCancel but how do I judge this ObjectAnimator had runned at onAnimationEnd .
animatorSet.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mSunMovPos = (float) sunAnimator.getAnimatedValue();
mCurSunSetColor = (int) sunSetAnimator.getAnimatedValue();
//How do judge nightAnimator had runned ?
mCurNightColor = (int) nightAnimator.getAnimatedValue();
}
#Override
public void onAnimationCancel(Animator animation) {
mSunMovPos = (float) sunAnimator.getAnimatedValue();
mCurSunSetColor = (int) sunSetAnimator.getAnimatedValue();
if (nightAnimator.isRunning())
mCurNightColor = (int) nightAnimator.getAnimatedValue();
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
I plan to implement integer number increase on textView from 0 to some value with animation within certain seconds.
e.g show animation which increase number from 0 to 600 on textview for 5 seconds duration.
How can I implement this?
You could use the ValueAnimator for that:
private void startCountAnimation() {
ValueAnimator animator = ValueAnimator.ofInt(0, 600);
animator.setDuration(5000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
textView.setText(animation.getAnimatedValue().toString());
}
});
animator.start();
}
Take a look at this simple solution:
public void animateTextView(int initialValue, int finalValue, final TextView textview) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(initialValue, finalValue);
valueAnimator.setDuration(1500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
textview.setText(valueAnimator.getAnimatedValue().toString());
}
});
valueAnimator.start();
}
Use a ValueAnimator
TextView textview = findViewById(R.id.textview1);
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 600);
valueAnimator.setDuration(5000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
textview.setText(valueAnimator.getAnimatedValue().toString());
}
});
valueAnimator.start();
I propose answer here for Kotlin developers :
fun startAnimation(textView: TextView) {
val animator = ValueAnimator.ofInt(0, 600)
animator.duration = 5000 // 5 seconds
animator.addUpdateListener { animation ->
textView.text = animation.animatedValue.toString()
}
animator.start()
}
Look here :
Animate TextView to increase integer and stop at some point?
and a nice lib here : https://github.com/sd6352051/RiseNumber