I have created a rotate animation when the answer is false it shake the CardView,
But it doesn't work until i change the view state such as < cardView.setVisibility(View.INVISIBLE) >
or calling nextQuestion().
AND Please suggest which class to choose for animation ObjectAnimator, Animation etc..
public void checkAnswer(boolean isTrue) {
if (isTrue == questionList.get(questionCount).getAnswer()) {
Toast.makeText(this, R.string.correct_answer, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, R.string.wrong_answer, Toast.LENGTH_SHORT).show();
shakeAnimation();
}
}
public void nextQuestion() {
if (questionCount < questionList.size()) {
question_textView.setText(questionList.get(++questionCount).getQuestion());
question_counter.setText((questionCount + 1) + "/" + questionList.size());
}
}
public void shakeAnimation() {
Animation shake = AnimationUtils.loadAnimation(MainActivity.this, R.anim.shake_animation);
cardView.setAnimation(shake);
}
You are only setting the animation.so whenever the view will be drawn or recreated your animation will work.
You have to call the start animation method on the view to show animation.
For Example in your case code will be
public void shakeAnimation() {
Animation shake = AnimationUtils.loadAnimation(MainActivity.this, R.anim.shake_animation);
cardView.startAnimation(shake);
}
Difference between setAnimation and startAnimation method
- SetAnimation
when the view is added to the view group, the animation will be called. when the view has been added, the animation will not be called
- StartAnimation
the animation will be called all the time even though the view has been added.
Note: StartAnimation internally call setAnimation method.
Related
I have a layout that has several cardView, that have recyclerViews in them
in the beginning the first recyclerview is empty, so the card doesn't show.
later on, the recyclerview gets populated, so I want it to have a nice animation to it.
I'm trying to move the other cards down, and then fade in the card.
to do that I set the database of the recyclerView, and then I attach a OnPreDrawListener to the cardview around it, so I can get the height of the view, then I set the view to GONE and run a transationY animation on the card below it.
Thing is that when I call getMeasuredHeight I get 0.
It's almost as the notifyDatasetChanged() only happens in the next frame, so the view didn't get it's new height yet.
here is my code:
private void runSearchAnimation(List<Route> searchResult) {
if(rvSearchResults.getMeasuredHeight() == 0) {
Log.i(TAG, "height is 0");
resultsAdapter.setDatabase(searchResult);
cvSearchResults.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
cvSearchResults.getViewTreeObserver().removeOnPreDrawListener(this);
Log.i(TAG, "cvSearchResults.getMeasuredHeight() = " + cvSearchResults.getMeasuredHeight());
cvSearchResults.setVisibility(View.GONE);
ObjectAnimator contentAnim = ObjectAnimator.ofFloat(cvRecentSearches,
"translationY", cvRecentSearches.getTranslationY(), cvSearchResults.getMeasuredHeight());
contentAnim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
Log.i(TAG, "animation started");
}
#Override
public void onAnimationEnd(Animator animation) {
Log.i(TAG, "animation ended");
cvSearchResults.setVisibility(View.VISIBLE);
}
});
contentAnim.setDuration(300);
contentAnim.start();
return true;
}
});
}
}
Does anyone have an idea on how to solve this?
I believe that RecyclerView supports such animations via RecyclerView.ItemAnimator.
See the docs.
so after some thinking I came up with this easy solution:
cvSearchResults.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if(cvSearchResults.getMeasuredHeight() != 0) {
cvSearchResults.getViewTreeObserver().removeOnPreDrawListener(this);
Since I will keep getting my onPreDraw method called until I will remove the listener, I remove it only when I know the view has been measured with the recyclerview data I gave it
upon onClickListener, I am starting an animation on textView (icon)
Animation anim = AnimationUtils
.loadAnimation(activity.getBaseContext(), R.anim.rotate_refresh);
icon.startAnimation(anim);
on clicking again, I want to check, if the icon is animating (rotating) , keep it that way or else start the animation again.
I want to know how can I check textview is in animation?
Maybe try something like this
Animation animation = icon.getAnimation();
if(animation != null && animation.hasStarted() && !animation.hasEnded()){
//do what you need to do if animating
}
else{
//start animation again
}
This line will let you know if the TextView is animated
Animation animation=textView.getAnimation();
It will most probably work for you as you want to do.
boolean isInBetweenAnim = false;
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
isInBetweenAnim = true;
}
#Override
public void onAnimationEnd(Animation animation) {
isInBetweenAnim = false;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
So whenever a animation starts you will have isInBetweenAnim true and for rest you will have it false. Easily check the value of isInBetweenAnim in onClick of your button and you can code for it.
Hope it Works ! Happy Coding..
If you have multiple views that can start an animation on one view , you must focus on animated view, not animation.
I had multiple info imageViews that shows help text in a textView. To avoid multiple animations on textView, I used Arun's approach:
if (helpTextView.getAnimation() == null) {
helpTextView.startAnimation(helpAnimation);
}
when I click on button then an animation goes to left to right and right to left only for menu section layout and according to this animation the width of other layout (head of family should be expand or collapse..
my problem is that the animation for menu layout is working properly but the width of other layout not to collapse or expand simultaneously ,how can i do this.
my code is this
if(flagmenu)
{
//menu layout set animation
lpmenu.startAnimation(animationFallout);
Thread t=new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(2500);
runOnUiThread(new Runnable() {
public void run() {
lpmenu.setVisibility(View.GONE);
}
});
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
t.start();
// lpmenu.setVisibility(View.GONE);
flagmenu = false;
}
else
{
lpmenu.startAnimation(animationFalling);
lpmenu.setVisibility(View.VISIBLE);
flagmenu = true;
}
Use this animation code here v is view group mean layout interpolatedTime of animation work fine for me . if code for collapse if u want expend then use + sine
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
if (interpolatedTime == 1) {
v.setVisibility(View.GONE);
} else {
v.getLayoutParams().width= initialwidth
- (int) (initialwidth * interpolatedTime);
// replace - to + for expend
v.requestLayout();
}
}
#Override
public boolean willChangeBounds() {
return true;
}
};
First, I would suggest you to use ObjectAnimators to achieve this instead of Animations, since Animation does not actually change the View's position. Then, in order to perform simultaneous animations with Animators you may use AnimatorSet class (playTogether method). If you need to support old Android versions there is a NineOldAndroids library which backports Animators. Another way (without animators) is to use AnimationSet (again you should add Animations for each view you want to move) class and implement AnimationListener changing the Layoutparams of your views (in order to update layout)
I have a main activity which keeps 2 fragments.
On the first fragment i call an animation from the main activity.
If i call it from a button it animates and everything is ok
If i try to call it automatically (like if(x>3) animate()) it doesn't show the animation at all and in addition if i push the button it continues not to showing the animation. the code is below
Parent
public void showAnimation()
{
AnimationSet as = new AnimationSet(true);
Animation up = AnimationUtils.loadAnimation(this.getBaseContext(), R.anim.slide_up);
up.setStartOffset(2000);
Animation down = AnimationUtils.loadAnimation(this.getBaseContext(), R.anim.slide_down);
down.setStartOffset(6000);
as.addAnimation(up);
as.addAnimation(down);
Log.v("FFF","ok");
test.setVisibility(View.VISIBLE);
test.startAnimation(as);
test.setVisibility(View.GONE);
}
Child
if(!settings.getBoolean(BADGE_D7, false)){
if (days >= 7){
days7.setImageResource(R.drawable.days7);
Log.v("FFF","done");
parent.showAnimation();
}
}
and in the constructor of the child i have
public Child(MainActivity p) {
this.parent = p;
}
by the way the log messages are shown up the only problem is the animation. What is the problem??
Well, i kinda found out a solution, it's not the best but it works.
First i put all the code in the child activity and i use it from there. I access the textView from the child and i added a treeObserver
test = (TextView) parent.findViewById(R.id.test1);
ViewTreeObserver vto = test.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
ready = true;
return true;
}
});
and I edited the showAnimation() like this:
public void showAnimation()
{
as = new AnimationSet(true);
Animation up = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_up);
Animation down = AnimationUtils.loadAnimation(this.getActivity(), R.anim.slide_down);
down.setStartOffset(4000);
as.addAnimation(up);
as.addAnimation(down);
Log.v("FFF","ok");
while(!ready){} //Added this line
test.setVisibility(View.VISIBLE);
test.startAnimation(as);
test.setVisibility(View.GONE);
}
so while the TextView isn't in place it is stucks in a loop until everything is ok.
I had a view that I declared at the end of my layout to keep its Z index above its siblings. I had to touch the page to make the animation work.
So I set the Z index again through Java and it worked.
view.bringToFront();
It seems that an android animation is not truly finished when the onAnimationEnd event is fired although animation.hasEnded is set to true.
I want my view to change it's background drawable on the end of it's ScaleAnimation which it does, but you can clearly see that it is changed some miliseconds before it finishes. The problem is, that it flickers because the new background appears (=is) scaled for a short time until the animation really finishes.
Is there a way to get either the real end of the animation or just prevent the new background from beeing scaled this short period of time?
Thank you!
//EDIT: I'm using an AnimationListener to get the following call:
#Override
public void onAnimationEnd(Animation animation)
{
View view = (MyView) ((ExtendedScaleAnimation) animation).getView();
view.clearAnimation();
view.requestLayout();
view.refreshBackground(); // <-- this is where the background gets changed
}
Here is the actual bug related to this issue http://code.google.com/p/android-misc-widgets/issues/detail?id=8
This basically states that the onAnimationEnd method doesn't really work well when an AnimationListener is attached to an Animation
The workaround is to listen for the animation events in the view to which you were applying the animation to
For example if initially you were attaching the animation listener to the animation like this
mAnimation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(Animation arg0) {
//Functionality here
}
});
and then applying to the animation to a ImageView like this
mImageView.startAnimation(mAnimation);
To work around this issue, you must now create a custom ImageView
public class MyImageView extends ImageView {
and then override the onAnimationEnd method of the View class and provide all the functionality there
#Override
protected void onAnimationEnd() {
super.onAnimationEnd();
//Functionality here
}
This is the proper workaround for this issue, provide the functionality in the over-riden View -> onAnimationEnd method as opposed to the onAnimationEnd method of the AnimationListener attached to the Animation.
This works properly and there is no longer any flicker towards the end of the animation.
I was abe to resolve this by calling clearAnimation() on the view being animated inside onAnimationEnd, that took away the flicker
Its weird why would anyone have to do that, as onAnimationEnd callback should have been called only if the animation has already ended. But I guess the answer lies in the depth of Framework on how view/layout handles animation callback.
For now take it as a hack-free solution, that just works.
animation.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation anim) {
innerView.clearAnimation(); // to get rid of flicker at end of animation
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams
(innerBlockContainer.getWidth(), innerBlockContainer.getHeight());
/* Update lp margin, left/top to update layout after end of Translation */
ViewGroup parent_ofInnerView = (ViewGroup)innerView.getParent();
vp.updateViewLayout(innerBlockContainer, lp);
}
public void onAnimationRepeat(Animation arg0) {}
public void onAnimationStart(Animation arg0) {
}
});
innerView.startAnimation(animation);
I had same issue and solved it using
view.clearAnimation();
before
view.startAnimation(anim);
I had a similar problem and I used Soham's solution with custom view class.
It worked fine, but at the end, I've found a simpler solution that worked for me.
After calling the view.StartAnimation(animation), and before the next step in my program, I've added a short delay that will be long enough to let the animation finish, but short enough to be unnoticeable by the user:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
nextStepInMyProgram();
}
}, 200);// delay in milliseconds (200)
For some reason the onAnimationStart works properly, and the onAnimationEnd doesnt. So heres how I originally did it and what I changed:
Attempt 1 (flicker):
a) Move image from 0px to 80px
b) In onAnimationEnd, set the image's location to 80px
Attempt 2 (no flicker):
a) In onAnimationStart, set the image's location to 80px
b) Move the image from -80px to 0px
Hope that made sense. Basically I flipped the way I did it
Try to use getAnimation() from your object:
public void onShowListBtnClick(View view)
{
rightPanel.startAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_left));
rightPanel.getAnimation().setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation) {
// write your code here
}
});
}
An easy fix is to add one line to AnimationListener.onAnimationEnd():
#Override
public void onAnimationEnd(Animation a) {
a.setAnimationListener(null);
…
}
annimation can be also stopped on screen rotation. in this case onAnimationEnd() is not being called. my workaround:
animation.setDuration(animationDuration);
animation.setAnimationListener(new AnimationListenerAdapter() {...});
view.startAnimation(animation);
handler.postDelayed(new Runnable() {
#Override
public void run() {
if(!animation.hasEnded()) {
// here you can handle this case
}
}
}, animationDuration + 100);
I had this issue because my Animation was not started in the main thread.
This resulted in a duration of 0.
However , the Animation did play correctly - it just called onAnimationEnd() immediately after execution.
If you are using repeat count as infinite, then onAnimationEnd would not get called. Check the documentation link
You can also use setUpdateListener, then check the current fraction of the animation progress and act accordingly.
Here's a Kotlin example for a fade-out animation which eventually makes the view gone from the layout:
view.animate()
.alpha(0f)
.setDuration(duration)
.setUpdateListener { animation ->
if (animation.animatedFraction > 0.99f) {
view.visibility = View.GONE
}
}
.start()
This worked for me:
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if (Float.compare(animation.getAnimatedFraction(),1.0f)) {
// animation ended;
//do stuff post animation
}
}