I'm trying to implement a Blink Animation.
This code makes a view to blink fade in and fade out:
AlphaAnimation blink = new AlphaAnimation(0.0f, 1.0f);
blink.setDuration(500);
blink.setStartOffset(0);
//changing it makes more than one animation appear in different time stamps.
blink.setRepeatMode(Animation.REVERSE);
blink.setRepeatCount(Animation.INFINITE);
I have two issues:
setStartOffset(n); --> Changing n makes more than one animation appear in different time stamps. Not synced. I want it to be synced, all animations should appear and dissappear at same time.
I do not want fade in or fade out, simply visible & gone with few millisecond delay.
Is there any other Class of Animation that i had to use, or i had to make a custom animation.
Pls. help.
For animation without the fading you can create your own custom Interpolator:
import android.view.animation.Interpolator;
public class StepInterpolator implements Interpolator {
float mDutyCycle;
public StepInterpolator(float dutyCycle) {
mDutyCycle = dutyCycle;
}
#Override
public float getInterpolation(float input) {
return input < mDutyCycle ? 0 : 1;
}
}
Then set the interpolator in the Animation object:
blink.setInterpolator(new StepInterpolator(0.5f));
So, my answer ... it's a class that toggles visibility of a view in a certain interval. Of course it can be solved differently, maybe you get some inspiration...
public static class ViewBlinker {
private Handler handler = new Handler();
private Runnable blinker;
public ViewBlinker(final View v, final int interval) {
this.blinker = new Runnable() {
#Override
public void run() {
v.setVisibility(v.getVisibility()==View.VISIBLE?View.INVISIBLE:View.VISIBLE);
handler.postDelayed(blinker, interval);
}
};
}
public void startBlinking() {
handler.post(blinker);
}
public void stopBlinking() {
handler.removeCallbacks(blinker);
}
}
and you use it like this :
ViewBlinker blinker = new ViewBlinker(YOUR_BLINK_VIEW, YOUR_BLINK_INTERVAL);
blinker.startBlinking();
and when your view is finished blinking, call
blinker.stopBlinking();
Actually, based on cold ash's answer in 2014, all of his/her code can be simplified as below (thanks to lambda):
blink.setInterpolator(input -> input < 0.5f ? 0 : 1);
Yes, just a single line. Very simple.
Related
I would like 3 TextViews to appear in sequence (the first one, then when it has finished the second one, and then the third one). I tried this code
tv1.animate().alpha(1f).setDuration(1000);
tv2.animate().alpha(1f).setDuration(1000);
tv3.animate().alpha(1f).setDuration(1000);
but they appear together, so I tried
tv1.animate().alpha(1f).setDuration(1000);
tv2.animate().alpha(1f).setStartDelay(1000).setDuration(1000);
tv3.animate().alpha(1f).setStartDelay(2000).setDuration(1000);
but when I open the app they appear together with no animation. How can I fix this?
There are many ways to do it.
If you want this by using alpha value, every view you animate must have 0.0f alpha value at the beginning. Then animate them to 1.0f alpha.
For example:
private void animateViews(final ArrayList<View> viewArrayList, long delay){
for (int i = 0; i < viewArrayList.size(); i++) {
final int position = i;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
viewArrayList.get(position).animate().alpha(1f).setDuration(200);
}
}, i * delay);
}
}
You can use this method I wrote.
Usage:
ArrayList<View> arrayList = new ArrayList<>();
arrayList.add(tv1);
arrayList.add(tv2);
arrayList.add(tv3);
animateViews(arrayList,300);
delay parameter means, every animation start 300 * index miliseconds delayed.
Also this method has 200 ms duration for alpha animation.
First starts at 0 delay,
Second starts at 1 * 300 ms delay
Third starts at 2 * 300 ms delay
...
..
.
You need set alpha to 0 before start animation
Add an AnimationListener and start the next animation when the previous one ends.
AlphaAnimation alphaAnimation = new AlphaAnimation(1f,0f);
alphaAnimation.setFillAfter(true);
alphaAnimation.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation anim)
{
};
public void onAnimationRepeat(Animation anim)
{
};
public void onAnimationEnd(Animation anim)
{
nextPuzzle();
};
});
tv1.startAnimation(alphaAnimation);
Change the time
tv1.animate().alpha(1f).setDuration(1000);
tv2.animate().alpha(1f).setStartDelay(2000).setDuration(1000);
tv3.animate().alpha(1f).setStartDelay(3000).setDuration(1000);
I have implemented the method shown in this question and the animation on scroll looks perfectly fine. However, the initial list fill animation just shows all objects appear on the screen at the same time and it just looks like a lag.
While debugging, I can see that the animation method is being called 7 times, but I guess it is so fast that they are all trying to run at basically the same time. Any ideas what I can do? I tried delaying the animation, but I got stuck with how to do that. I asked that question here. Thank you for the help!
Edit: I can post the same code that I put on the other question:
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
//Normal OnBindViewHolder stuff
SetAnimation(vh.ItemView, position);
}
And then the SetAnimation method:
private void SetAnimation(View viewToAnimate, int position)
{
if (position > lastPosition)
{
var animation = AnimationUtils.LoadAnimation(_context, Resource.Animation.up_from_bottom);
//animation.SetAnimationListener(new CheckpointAnimationListener());
viewToAnimate.StartAnimation(animation);
lastPosition = position;
}
}
What I really want here is for the animation to finish before the lastPostion = position line is called.
And the empty AnimationListener, since I am really not sure how to handle the wait.
private class CheckpointAnimationListener : Java.Lang.Object, Animation.IAnimationListener
{
public void OnAnimationEnd(Animation animation)
{
}
public void OnAnimationRepeat(Animation animation)
{
}
public void OnAnimationStart(Animation animation)
{
}
}
I had to do something similar. Basically, the approach I took was whenever I started an animation, I also informed when the minimum start time would be, based on the last time the animation was initiated.
Since you didn't post any code, I'll write a basic outline of what I did:
// member to know when the minimum start time of the next animation will be.
private long mNextAnimationStartTime;
...
public void onBindViewHolder(...) {
// perform binding logic
// sample the current time.
long currentTime = System.currentTimeMillisec();
// if the next animation time is greater than now...
if (mNextAnimationStartTime > currentTime) {
// calculate how much time to wait before showing the next animation.
int delay = mNextAnimationStartTime - currentTime;
// In this example, I use postDelayed to postpone the animation,
// but you could also use start offset of the animation itself.
postDelayed(new Runnable() {
#Override
public void run() {
// start the animation after a delay.
startAnimation(viewToAnimate);
}
}, delay);
} else {
// if the next animation time is in the past, just start the animation.
startAnimation(viewToAnimate);
}
// schedule the next animation start time to now + 100 ms (play with this 100 to fit your needs)
mNextAnimationStartTime = currentTime + 100;
}
Hope this was clear, and helps you get started.
The answer from Gil sent me in the right direction, but since I am working with Xamarin.Android I needed to do a couple of things differently. I used his idea for StartOffset instead, since C# does not use anonymous classes (making runnables quite a bit more difficult). Here is my final animation code (after playing around with the delays).
private void SetAnimation(View viewToAnimate, int position)
{
var animation = AnimationUtils.LoadAnimation(_context, Resource.Animation.up_from_bottom);
_currentTime = JavaSystem.CurrentTimeMillis();
long difference = _currentTime - _timeAtFirstCall;
//this will cause the first items to populate the view to animate in order instead of at the same time.
//_delay is increased each time so each item will start 75ms after the one before it. difference is
//there to make sure later items in the list dont get this delay and animate as they appear.
if (_nextAnimationStartTime > _currentTime && difference < 150)
{
if (position > lastPosition)
{
animation.StartOffset = _delay;
viewToAnimate.StartAnimation(animation);
lastPosition = position;
_delay += 75;
}
}
else
{
if (position > lastPosition)
{
animation.StartOffset = 75;
viewToAnimate.StartAnimation(animation);
lastPosition = position;
}
}
_nextAnimationStartTime = _currentTime + 400;
}
And then I had these variables defined and initialized at the top of the class:
private int lastPosition = -1;
private long _nextAnimationStartTime = 0;
private long _currentTime = 0;
private long _timeAtFirstCall = JavaSystem.CurrentTimeMillis();
private long _delay = 150;
This method is called at the end of OnBindViewHolder. So now, the first time OnBindViewHolder is called it goes through the else statement. I had to set an initial delay here as well because otherwise the animation seemed to start half way on the screen and just looked bad. This delay also ensures the items will animate in sequence on a fast scroll.
The one problem that I still have is if the user scrolls immediately when loading the view. The initial items keep their delay, but the first item that is set to animate when it is scrolled to might start its animation before the first items are finished. I am not sure how to get around this problem...
What I'm trying to do is load one animation on a view with a linear interpolator for example. Then, when a certain event happens in my application, change the interpolator to something like a deceleration interpolator. So the effect would be a constant animation until an event happened, then the animation would slow down to a stop. Is this possible to achieve? Im wondering how I would achieve this and would love some direction.
Thanks!
Yes. You would create a custom Interpolator for the animation. It contains the other interpolators that you want. When the event happens, just trigger it in the interpolator and have the interpolator swap one interpolator for the other.
public class CompositeInterpolator implements TimeInterpolator {
LinearInterpolator mLinear;
DecelerateInterpolator mDecelerate;
boolean mUseLinear = false;
public CompositeInterpolator() {
mLinear = new LinearInterpolator();
mDecelerate = new DecelerateInterpolator();
}
public void useLinear(boolean use) {
mUseLinear = use;
}
#Override
public float getInterpolation(float input) {
if (mUseLinear) {
return mLinear.getInterpolation(input);
} else {
return mDecelerate.getInterpolation(input);
}
}
}
Then just call useLinear(false) when you want to swap.
I want to build an animation of TextViews, which repeats itself just after completion.
For each View I want to animate, I use the following piece of code
final float oldX = v.getX();
final float newX = v.getX() - (float)totalWidth;
final AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
v.setX(oldX);
animFinished = true;
//This line won't compile
//v.animate().setDuration(animDuration).setInterpolator(newsInterpolator)
// .setListener(listener).x(newX);
}
};
v.animate().setDuration(animDuration).setInterpolator(newsInterpolator)
.setListener(listener).x(newX);
I tried to place the last piece of code into the onAnimationEnd, but Java will not compile since it considers the object listener as not initialized. Moreover, I don't think that this "recursive" animation invocation is a good solution, it was the first thing which came to my mind. I am suspicious that there is a simple and sound way to implement looping property animation, but I failed to locate it, so I turned here for help.
Thanks in advance
Well, I am going to answer myself again.
TranslateAnimation class has methods about repeating the animation, so I used it instead of ViewPropertyAnimator.
The following code seems to work:
long duration = 1000* ((long)totalWidth / newsScrollSpeed);
System.out.println("totalWidth="+totalWidth);
TranslateAnimation anim = new TranslateAnimation(0,-totalWidth,0,0);
anim.setInterpolator(linearInterpolator);
anim.setDuration(duration);
anim.setRepeatCount(TranslateAnimation.INFINITE);
anim.setRepeatMode(TranslateAnimation.RESTART);
for(i=0;i<this.getChildCount();i++)
{
View v = this.getChildAt(i);
if(v.getId() == R.id.yuruyen_yazi)
{
continue;
}
v.startAnimation(anim);
}
Not elegant way, but it works:
Runnable runnable = new Runnable() {
#Override
public void run() {
// update newX
v.animate().setDuration(animDuration).setInterpolator(newsInterpolator).x(newX).withEndAction(this).start();
}
};
v.animate().setDuration(animDuration).setInterpolator(newsInterpolator).x(newX).withEndAction(runnable).start();
i want to animate button(ie; rotation, translation) then change the text of a button. unfortunately, it always first changes the text of the button then do the animation.
How can I achieve my goal?
Pls help me
my code is like this;
AnimationSet set = new AnimationSet(true);
Animation anim1 = new RotateAnimation(0, 360, 500, 750);
anim1.setDuration(3000);
anim1.setFillAfter(true);
set.addAnimation(anim1);
Animation anim2 = new RotateAnimation(0, 360, 1024, 824);
anim2.setDuration(3000);
anim2.setFillAfter(true);
set.addAnimation(anim2);
anim2.setStartOffset(3000);
first.clearAnimation();
set.setFillAfter(true);
first.startAnimation(set);
numbers[0]=min + (int)(Math.random() * ((max - min) + 1));
Your code starts the animation but is not blocking : once the animation is started, the program goes on.
You could try getting an handler and post the change text event at the right time :
Handler mHandler=new Handler();
Runnable lRunnable =new Runnable()
{
public void run()
{
//Your change text code
}
};
mHandler.postDelayed(lRunnable , 3000); // Or any other duration so you have the right effect
A better solution is to add an AnimationListener to the animation, or if you are on JB use the view property animators and the withEndAction() method. You should avoid the old animation framework if possible. It doesn't actually change the properties, it just draw the view with a transformation.
set.setAnimationListener(new AnimationListener() {
public void onAnimationEnd() {
// ...
}
public void onAnimationStart() {
}
public void onAnimationRepeat() {
}
}
But I recommend the view property animations if you can use them. They are much better to work with.