I have a LinearLayout (called layout) with height as fill_parent and an initial top margin of 200dp inside a RelativeLayout.
When I clicked a button, I want the topMargin to become 0, expanding the LinearLayout.
I am using the next code:
final RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.FILL_PARENT);
Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
int topMargin = (int)((1-interpolatedTime)*200);
layoutParams.setMargins(0, topMargin, 0, 0);
layout.setLayoutParams(layoutParams);
LogThis.debug("topMargin: "+topMargin);
}
};
animation.setDuration(1000);
layout.startAnimation(animation);
The problem is that although it makes an animation, it is not soft. It does the transformation in 4 steps, so it does not look good.
Do you have any idea how to make this kind of transformation softly?
Thank you in advance.
EDIT
I have this in the first moment:
And after the animation I want this:
With the TranslationAnimation I get this:
the choppy animation is happening because that's not how the Animation are supposed to be used. Those are to be use Transformation object passed onto the method.
But because you're using a simple translation you can easily achieve that using a TranslateAnimation like on this answer:
translate animation
Related
Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ViewGroup.LayoutParams params = slidingPane.getLayoutParams();
int calculatedHeight = expandedHeight - ((int) (expandedHeight * interpolatedTime));
if (calculatedHeight <= collapsedHeight) {
calculatedHeight = collapsedHeight;
}
params.height = calculatedHeight;
slidingPane.setLayoutParams(params);
slidingPane.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
animation.setDuration(ANIMATION_DURATION);
animation.setAnimationListener(animationListener);
slidingPane.startAnimation(animation);
SlidingPane is a LinearLayout and it has a ListView as a child. ListView contains images in every row.
Now this code is working absolutely fine, but animation is not smooth. If I remove images from listview then it works fine.
I have already tried following things based on the answers on SO on similar questions -
1. Hide the content of sliding pane before animation and then again make them visible on animation end. It helps but behavior looks very odd
2. Set android:animateLayoutChanges="true" in xml, but its not animating anything
Is there anyway to solve this problem?
Instead of manually changing the height on each animation step, you should try to use the more systematic animation, like ValueAnimator, or ObjectAnimator:
slidingPane.animate().scaleY(0f).setInterpolator(new AccelerateDecelerateInterpolator()).setDuration(ANIMATION_DURATION);
You can use pivotY/pivotY on the view to control the anchor point of the scale animation.
Related docs:
https://developer.android.com/reference/android/animation/ValueAnimator.html
https://developer.android.com/reference/android/animation/ObjectAnimator.html
https://developer.android.com/guide/topics/graphics/prop-animation.html
I want to animate top map padding of a view programmatically but I have no idea how to do this.
private GoogleMap map;
map.setPadding(left, top, right, bottom);
Anyone have an idea how to animate top padding value from say 0 to 100 ?
The map padding is not a view property nor a object property. You can test it by giving a padding to the map view:
<com.google.android.gms.maps.MapView
android:id="#+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="10dp"/>
The result is that the map itself is padded which is different from
map.setPadding(left, top, right, bottom);
setPadding is a method that offsets the UI controls inside the map. There is no property to animate. Luckily android offers the ValueAnimator. Your problem can be solved this way:
ValueAnimator animation = ValueAnimator.ofInt(0, paddingBottom);
animation.setDuration(150);
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
getMap().setPadding(0, 0, 0, Integer.parseInt(valueAnimator.getAnimatedValue().toString()));
}
});
animation.start();
This basically animates an integer from 0 to paddingBottom without any context. You create the context in the onAnimationUpdate listener where you assign the animated padding value to the map padding.
You would use Property Animation for that.
final int newTopMargin = <value>;
Animation anim = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LayoutParams params = yourView.getLayoutParams();
params.topMargin = (int)(newTopMargin * interpolatedTime);
yourView.setLayoutParams(params);
}
};
yourView.startAnimation(anim);
You can achieve the animation as described above, it won't be for padding though but it will set the margin with the animation effect you want.
It's actually very simple to animate the padding changes without having to animate manually.
Right after setting the paddings, call
gooleMap.animateChanges(CameraUpdateFactory.newLatLngBounds(Bounds, padding));
or any of the CameraUpdateFactory helper methods.
It is easier and also much smoother than using a ValueAnimator, especially if you animate multiple padding sides.
In a RelativeLayout, I added an ImageView with some margin set in LayoutParams:
RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
rlLayoutParams.topMargin = (int) convertDpToPixel(kMARGIN_HEADER);
rlLayoutParams.leftMargin = (int) myMAGIC_MARGIN;
Now this ImageView is somewhere in the middle of the screen. I want to have it slide a few hundred pixels to its right, and stay there, so I applied the following TranslateAnimation:
private TranslateAnimation getAnimation(ImageView view, int displacement, int duration) {
int[] view_pos = new int[2];
view.getLocationOnScreen(view_pos);
TranslateAnimation slide = new TranslateAnimation(
view_pos[0],
view_pos[0] + displacement,
view_pos[1],
view_pos[1]);
slide.setFillAfter(true);
slide.setDuration(duration);
return slide;
}
Immediately after applying this animation, the whole view is gone. I checked the value of view_pos[] in debug mode, they are fine (not all zeroes). If I make the setFillAfter to false, then the view will come back after animation duration (for obvious reasons). So where did it go?
I also tried the following:
TranslateAnimation slide = new TranslateAnimation(
view_pos[0],
view_pos[0], // don't do the + displacement,
view_pos[1],
view_pos[1]);
I was expecting the view to stay where it is. But no, it was vanished as well, with a chance of coming back afterwards, depending on the value of setFillAfter.
I start to think I cannot apply TranslateAnimation if the view was placed by LayoutParams in a RelativeLayout. How should I achieve the goal of sliding the view?
The parameters for TranslateAnimation are deltas not positions, so you just want:
slide = new TranslateAnimation(0, displacement, 0, 0).
If you don't need to support Android <3.0 then use a Property Animation instead of a View Animation - see the Android docs for more details.
I have a menu I am trying to animate. I am splitting a menu by changing margins
and inserting a new menu. When I want to insert the menu, the animation is:
level3Height = level3Frame.getHeight();
final int newBottomMargin = (int)(origBottomMargin + level3Height/2);
final int newTopMargin = (int)(origTopMargin + level3Height/2);
splitUp = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnShopWireless.getLayoutParams();
params.bottomMargin = (int)(newBottomMargin * interpolatedTime);
btnShopWireless.setLayoutParams(params);
}
};
joinDown = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnShopWireless.getLayoutParams();
params.bottomMargin = (int)(origBottomMargin * interpolatedTime);
btnShopWireless.setLayoutParams(params);
}
};
splitUp.setDuration(1000);
splitUp.setInterpolator(new BounceInterpolator());
joinDown.setDuration(500);
joinDown.setInterpolator(new BounceInterpolator());
After getting the height of the inserted menu, the animation moves the Views upward very nicely:
btnShopWireless.startAnimation(splitUp);
All that works great! But....
When I want to remove the inserted level menu and move things back down I use the below, and the animation doesn't happen - the Views simply slam back into
their original place with no smooth motion.
btnShopWireless.startAnimation(joinDown);
I have AnimationListeners set up to setVisibility to VISIBLE onAnimationStart and also setVisibility to GONE onAnimationEnd. They are doing their job, so I know the animation is getting invoked or the visibility would never occur within the AnimationListeners for joinDown. But the animated movement backdown just never happens. I can only animate the first one, splitUp.
Anyone have any clues as to what I am missing in order to get the second animation working?
I figured out what my problem was - pure IQ underflow.
The originalMargin was 0. I increased them to about 190dp to fit in a 3rd menu. When it time to bring the menu together and remove the 3rd menu, the transformation used the original margins again multiplied by the time value. No matter how much time or which interpolator, the originalMargin*interpolatedTime would always be 0.
To fix it, I used this calculation:
joinDown = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnShopWireless.getLayoutParams();
params.bottomMargin = (int)(newBottomMargin -(level3Height/2 * interpolatedTime));
btnShopWireless.setLayoutParams(params);
}
};
I want to have myView move deltaX, deltaY pixels from current position.
But it moves two times longer than I want.
This is caused by setLayoutParams in onAnimationEnd.
Without that, myView's real positon is not changed.
How can I change myView's real positon and animate normally?
class MyLayout extends RelativeLayout {
Animation animation;
MyView myView;
animation = new TranslateAnimation(0, deltaX, 0, deltaY);
animation.setDuration(100);
animation.setFillAfter(true);
myView.startAnimation(animation);
}
class MyView extends ImageView {
protected void onAnimationEnd() {
super.onAnimationEnd();
LayoutParams params = (LayoutParams)getLayoutParams();
params.leftMargin += deltaX;
params.topMargin += deltaY;
setLayoutParams(params);
}
}
If you want to avoid doing the desired translation of the view twice as you are doing it now, you have to set the "setFillAfter" flag for the animation to false. As the documentation says "When setFillAfter is set to true, the animation transformation is applied after the animation is over."
On the other hand if you leave the "setFillAfter" flag to true, the view should translate to the desired location and no LayoutParams modification is needed in onAnimationEnd method. So it might be that something is blocking the transformation, if you could provide further information about the layout that the view is part of, it would be mostly helpful.
Another thing is that setting the margin of the layout params to move the view is not entirely the best idea. Take it more like a hack. Rather try to set the position of the view directly or try to play with weights in your parent view. That way you will get the same effect on multiple different screens.