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)
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
I apply the expand animation in ListView using,
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Calculating the new bottom margin, and setting it
mViewLayoutParams.bottomMargin = mMarginStart
+ (int) ((mMarginEnd - mMarginStart) * interpolatedTime);
// Invalidating the layout, making us seeing the changes we made
mAnimatedView.requestLayout();
// Making sure we didn't run the ending before (it happens!)
} else if (!mWasEndedAlready) {
mViewLayoutParams.bottomMargin = mMarginEnd;
mAnimatedView.requestLayout();
if (mIsVisibleAfter) {
mAnimatedView.setVisibility(View.GONE);
}
mWasEndedAlready = true;
}
}
Here, when I try to click the last item of the ListView, the last item of my Listview expand downward so, I cannot see the whole contents of the last one without scrolling upward.
In other words, If I click the last one, I want my focus on the bottom side of the last item (automatically scrolling) so I can see the full contents of the last one immediately.
Is there any way to solve it?
Maybe this will help:(First check if the user has clicked the last item)
listView.setSelection(ListAdapter.getCount() - 1);
Or sometimes you have to do something like this:
listView.post(new Runnable()
{
#Override
public void run()
{
listView.setSelection(ListAdapter.getCount() - 1);
}
});
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();
}
}
}
});
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
}
}