I have a few buttons on a grid. I need that this buttons are animations. The animation is very simple, I only want that this buttons change his color and return o his original color, like a "simon game".
So, my problem is the next:
I have been trying make this with AnimatorSet. I make my own class for the buttons :
class Button{
ObjectAnimator anim;
public Button button;
public int colorA;
public int colorB;
public Button(){};
};
And I have:
private Boton[] buttons;
buttons = new Boton[numButtons];
for(int i=0; i<numButtons; i++)
buttons[i] = new Boton();
After:
for(int i=0; i<numButtons; i++){
buttons[i].animacion=ObjectAnimator.ofFloat(buttons[i], "alpha", 1f, 1f);
buttons[i].animacion.setStartDelay(1000);
buttons[i].animacion.setDuration(2000);
buttons[i].animacion.addListener(new miAnimador(i));
}
Also, I defined my own Listener:
class miAnimador implements Animator.AnimatorListener{
int numBoton;
public miAnimador(int num){
numBoton=num;
}
public void onAnimationCancel(Animator paramAnonymousAnimator) {
}
#Override
public void onAnimationEnd(Animator animator) {
buttons[numBoton].button.setBackgroundColor(buttons[numBoton].colorA);
}
#Override
public void onAnimationRepeat(Animator animator) {
}
#Override
public void onAnimationStart(Animator animator) {
buttons[numBoton].button.setBackgroundColor(buttons[numBoton].colorB);
}
};
This is very powerful for me because I can have a vector of buttons and launching his animation when I want.
After I can make this: build a animatorSet with the sequence of animations that I want.
AnimatorSet s = new AnimatorSet();
final List<Animator>listaAnimaciones = new ArrayList<Animator>();
final List<Animator>AnimationsSequence = new ArrayList<Animator>();
AnimationsSequence.add(buttons[0].anim);
AnimationsSequence.add(buttons[1].anim);
//Add list
s.playSequentially(AnimationSequence);
s.setStartDelay(5000);
//Start animation
s.start();
Well, this work well, and I like the form of the AnimatorSet work, but the problem occur when I try animate the same buttons twice.
The exception is: "Circular dependencies cannot exist in AnimatorSet" and i don't understand why this happening.
Be that as it may, I need make this, and I was thinking that this is the best solution, but:
If this don't work, how can get this?
I accept any idea.
If you thinking that this is bad solution, please I would like to know your opinion.
Related
Using Rotate Animation in android, but no rotation in image after clicking the button.
public class MainActivity extends AppCompatActivity {
ImageView spinImage;
Button buton;
Random r;
int degree =0 , degreeold= 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buton = (Button) findViewById(R.id.spinbutton);
spinImage= (ImageView) findViewById(R.id.spinimage);
r= new Random();
buton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
degreeold = degree % 360;
degree = r.nextInt(3600)+720;
degree= 4400;
RotateAnimation rotate = new RotateAnimation( degreeold, degree,
RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF , 0.5f);
rotate.setDuration(3600);
rotate.setFillAfter(true);
rotate.setInterpolator( new DecelerateInterpolator());
rotate.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
spinImage.setAnimation(rotate);
}
});
}
i am not able to find the mistake why there is no rotation in image. there is no error while running and app open without any delay but there is no animation when clicking the button.
You haven't started the animation. Try to use spinImage.startAnimation(rotate);
setAnimation is meant to give you more fine grained control if you want to start the animation delayed or together with other animations.
From the documentation:
Sets the next animation to play for this view. If you want the animation to play immediately, use startAnimation(android.view.animation.Animation) instead. This method provides allows fine-grained control over the start time and invalidation, but you must make sure that 1) the animation has a start time set, and 2) the view's parent (which controls animations on its children) will be invalidated when the animation is supposed to start.
Replace spinImage.setAnimation(rotate); with spinImage.startAnimation(rotate);
probably another rhetorical question.
In iOS when we set a view's (any any UIView subclass such as UIButton) alpha to 0, iOS by default disables all user interaction on that view.
I got an Android app where I animate fade out the view by:
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(buttonSelectionContainer, "alpha", 1, 0);
fadeOut.setDuration(500);
fadeOut.start();
However, I am noticing that when I tap the screen, the animation starts again, leading me to believe, in Android, even when a button alpha is set to 0, it is still tappable, is this true?
Is there a way to globally tell Android to disable user interaction for a view (and all its subviews) when its alpha is set to 0, either explicitly through using:
view.setAlpha(0.0f);
or through the ObjectAnimator like the above code block I used ?
A temporary work around for my problem would probably be to schedule this code to run after 500 ms:
// psuedocode: after 500ms
dispatch_doSomethingAfter(500)
{
myButton.setEnabled(false);
}
Not the ideal solution but might be my only solution, unless some bright Android developers out there has a better solution ?
Use addListener on your ObjectAnimator to control what happens after the animation has finished.
fadeOut.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
button.setEnabled(false);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
You can create an Animator.AnimatorListener that automatically disables the target View when the animation ends.
Declare your custom DisableViewOnEndAnimatorListener class:
public class DisableViewOnEndAnimatorListener extends AnimatorListenerAdapter {
#Override
public void onAnimationEnd(Animator animation) {
if (animation instanceof ObjectAnimator) {
final Object target = ((ObjectAnimator) animation).getTarget();
if (target instanceof View) {
((View) target).setEnabled(false);
}
}
}
}
Then, in your code:
DisableViewOnEndAnimatorListener endAnimatorListener = new DisableViewOnEndAnimatorListener();
ObjectAnimator button1FadeOut = ObjectAnimator.ofFloat(button1, "alpha", 1, 0);
button1FadeOut.setDuration(500);
button1FadeOut.addListener(endAnimatorListener);
button1FadeOut.start();
ObjectAnimator button2FadeOut = ObjectAnimator.ofFloat(button2, "alpha", 1, 0);
button2FadeOut.setDuration(500);
button2FadeOut.addListener(endAnimatorListener);
button2FadeOut.start();
I'm doing an animation of bubbles on the screen, but the bubbles stop after finishing the animation time. How do I repeat the animation or make it infinite?
bub.animate();
bub.animate().x(x2).y(y2);
bub.animate().setDuration(animationTime);
bub.animate().setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
animators.add(animation);
}
#Override
public void onAnimationRepeat(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
});
Since ViewPropertyAnimator is only good for simple animations, use more advanced ObjectAnimator class - basically method setRepeatCount and additionally setRepeatMode.
This is actually possible. Here's an example of rotating a view:
final ViewPropertyAnimator animator = view.animate().rotation(360).setInterpolator(new LinearInterpolator()).setDuration(1000);
animator.setListener(new android.animation.Animator.AnimatorListener() {
...
#Override
public void onAnimationEnd(final android.animation.Animator animation) {
animation.setListener(null);
view.setRotation(0);
view.animate().rotation(360).setInterpolator(new LinearInterpolator()).setDuration(1000).setListener(this).start();
}
});
You can also use "withEndAction" instead of a listener.
You can use CycleInterpolator. For example, like this:
int durationMs = 60000;
int cycleDurationMs = 1000;
view.setAlpha(0f);
view.animate().alpha(1f)
.setInterpolator(new CycleInterpolator(durationMs / cycleDurationMs))
.setDuration(durationMs)
.start();
Here is an example in Kotlin with a simple way to repeat the animation by recursively calling it in withEndAction
Example
private var animationCount = 0
private fun gyrate() {
val scale = if (animationCount++ % 2 == 0) 0.92f else 1f
animate().scaleX(scale).scaleY(scale).setDuration(725).withEndAction(::gyrate)
}
This repeatedly animates the size of a view to get smaller, return to normal, get smaller, return to normal, etc. This is a pretty simple pattern to repeat whatever animation you'd like.
final ViewPropertyAnimator animator = view.animate().rotation(360).setInterpolator(new LinearInterpolator()).setDuration(1000); //Animator object
animator.setListener(new android.animation.Animator.AnimatorListener() {
...
#Override
public void onAnimationEnd(final android.animation.Animator animation) {
animation.setListener(this); //It listens for animation's ending and we are passing this to start onAniationEnd method when animation ends, So it works in loop
view.setRotation(0);
view.animate().rotation(360).setInterpolator(new LinearInterpolator()).setDuration(1000).setListener(this).start();
}
});
in kotlin you can do it like this. create a runnable. inside it animate the view and set withEndAction to runnable itself. and at the end run runnable to animation start.
var runnable: Runnable? = null
runnable = Runnable {
view.animate()
.setDuration(10000)
.rotationBy(360F)
.setInterpolator(LinearInterpolator())
.withEndAction(runnable)
.start()
}
runnable.run()
I have a ViewPager which has 4 pages and I want it scroll automatically.
What I am doing is viewPager.setCurrentItem(index); bound with a TimerTask where index is a precomputed integer.
The logic works fine but when it scroll (triggered by setCurrentImte) the animation is too fast.
How can I control the scroll speed of it? Please note I am talking about the scrolling animation speed, instead of the interval of 2 successive scroll.
Why not use a ViewFlipper instead? A ViewFlipper has built in functionality to handle auto-scrolling between views, which means you can dump the timer. Also, it implements ViewAnimator which gives you full control over the animations. It is relatively simple to create custom animations, and you have control over the time it takes to animate. A simple tutorial for a slide animation can be found here. Take note of the duration attribute of the animation xml files.
Looks like ViewPager doesn't provide such an API. I would suggest to do any of the following:
Take VieWPager code (it's under Your android sdk extras folder) and twek it the way you need (e.g. add necessary API). Now it seems to use internal constant (MAX_SETTLE_DURATION);
Intercept touches by yourself and call fakeDragBy() (not sure if its what You're looking for);
Why not try something like this? Use a value animator to animate the scroll values, then update the positioning so the fragment loading is correctly handled. Works fine for me :).
if(pager != null){
ValueAnimator scrollAnimator = ValueAnimator.ofInt(pager.getScrollX(), pager.getScrollX()+2560);
final int virtualPage = pager.getCurrentItem();
scrollAnimator.setDuration(1000);
scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
pager.setScrollX((Integer)animation.getAnimatedValue());
}
});
scrollAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
pager.setCurrentVirtualItem(virtualPage, false);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
scrollAnimator.start();
}
I have the same requirement on an app i am developing and my solution was this.
Observable.range(1, vpAdapter.getCount() - 1)
.concatMap(i-> Observable.just(i).delay(5000, TimeUnit.MILLISECONDS))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(integer -> {
currentAutoPage = integer;
binding.vpTour.beginFakeDrag();
lastFakeDrag = 0;
ValueAnimator va = ValueAnimator.ofInt(0, binding.vpTour.getWidth());
va.setDuration(1000);
va.addUpdateListener(animation -> {
if (binding.vpTour.isFakeDragging()) {
int animProgress = (Integer) animation.getAnimatedValue();
binding.vpTour.fakeDragBy(lastFakeDrag - animProgress);
lastFakeDrag = animProgress;
}
});
va.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (binding.vpTour.isFakeDragging()) {
binding.vpTour.endFakeDrag();
}
}
});
va.start();
}, Throwable::printStackTrace, () -> {
if (autoPageChangeDisposable != null && !autoPageChangeDisposable.isDisposed()) {
autoPageChangeDisposable.dispose();
}
});
In case you also want to stop the automatic swiping when user manually changes the page than just get the disposable from this code that i wrote, like this : "Disposable autoPageChangeDisposable = Observable.range(1, vpAdapter...." and than register and OnPageChangedListener and do this :
vpTour.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position != currentAutoPage) {
if (autoPageChangeDisposable != null && !autoPageChangeDisposable.isDisposed()) {
autoPageChangeDisposable.dispose();
}
}
}
});
What i want is that each animation, from the code below, to start with a delay, like a sequence. So i have this code:
public void setAnimation(){
View view;
String animation = prefs.getString("animations", "Scale");
String interpolator = prefs.getString("interpolators", "Bounce");
Animation animate = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.scale_in);
for(int i=0; i<gridView.getChildCount(); i++){
view = gridView.getChildAt(i);
view.startAnimation(animate);
}
}
because there is a for loop, all child animations will start instantly. I already tried with:
Thread.sleep....
Handler...
animate.setStartTime...
animate.setStartOffset...
but all child animations starts instantly.
I tried this method inside of loop, and the animations won't start:
animate.setAnimationListener(new AnimationListener(){
public void onAnimationEnd(Animation arg0) {
view.startAnimation(animate);
}
public void onAnimationRepeat(Animation arg0) {
}
public void onAnimationStart(Animation arg0) {
}
});
Thanx in advance.
The solution is to create a GridLayoutAnimationController or LayoutAnimationController.
I used LayoutAnimationController to show elements in LinearLayout with animation effect one by one using following code.
Animation fadeIn = AnimationUtils.loadAnimation(context, R.anim.anim_fade_in);
//lnrContactContainer is LinearLayout.
AnimationSet set = new AnimationSet(true);
set.addAnimation(fadeIn);
set.setDuration(500);
controller = new LayoutAnimationController(set, 1f);
lnrContactContainer.setLayoutAnimation(controller);
lnrContactContainer.setVisibility(View.VISIBLE);
But same approach does not work when I use it to show fadeout animation while hiding LinearLayout lnrContactContainer.setVisibility(View.GONE);