Ok, so i checked out
http://android-developers.blogspot.com/2011/02/animation-in-honeycomb.html
He says you can animate the property of an object in a given time. And i tried moving around objects and it looks fine. I encountered a problem when i went changing the width of a LinearLayout. I got this:
10-26 14:51:27.190: E/PropertyValuesHolder(12681): Couldn't find setter/getter for property width with value type float
Then i tried extending LinearLayout, with "myWidth"
public void setMyWidth(int myWidth) {
LinearLayout.LayoutParams params = (LayoutParams) getLayoutParams();
params.weight = myWidth;
setLayoutParams(params);
requestLayout();
this.myWidth = myWidth;
}
No luck. Then i tried changing LayoutParams.width, turns out width and height are the only public properties in java history, and ObjectAnimator needs getter and setter. No luck.
I'm embarassed to say i tried extending LayoutParams too... with no luck ofc.
Anybody succeded doing such a thing? I used old android.view.animation and i got what i wanted, but i'm curious for the future.
In situations where there isn't a simple property getter/setter you should use ValueAnimator and perform the animation manually.
Assuming:
v is the view you're animating
END_WIDTH is the target width of the view in pixels.
DURATION is the desired length of the animation in milliseconds.
Your code should look something like this:
ValueAnimator anim = ValueAnimator.ofInt(v.getMeasuredWidth(), END_WIDTH);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
layoutParams.width = val;
v.setLayoutParams(layoutParams);
}
});
anim.setDuration(DURATION);
anim.start();
For what it's worth this works. After you change the width on the layout params you have to reset the layout params object.
private class WidthEvaluator extends IntEvaluator {
private View v;
public WidthEvaluator(View v) {
this.v = v;
}
#Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
int num = (Integer)super.evaluate(fraction, startValue, endValue);
ViewGroup.LayoutParams params = v.getLayoutParams();
params.width = num;
v.setLayoutParams(params);
return num;
}
}
// your client code
ValueAnimator.ofObject(new WidthEvaluator(box), box.getWidth(), v.getWidth()).start();
I've created a small library ViewPropertyObjectAnimator that can do that in a very simple way (and uses a similar approach to this proposed by Tomer Weller).
You could achieve this animation with (assuming the mLinearLayout is the animated View and mEndWidth is the end value of the View's width):
ViewPropertyObjectAnimator.animate(mLinearLayout).width(mEndWidth).start();
You have made a mistake
params.weight = myWidth;
I think it is params.width not params.weight
I think a better way to accomplish this would be to use View's scaleX and scaleY property which are defined all the way down in View class, so it would be valid with practically any view or layout.
For example, consider this
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(MyLinearLayout,"scaleX",0f,1f);
objectAnimator.setDuration(300);
objectAnimator.start();
It works.
Related
I have a bottomBar that i would like to expand. I think i know how to do it with animations.
But according to What is the difference between an Animator and an Animation?
If i use old animations. Then the buttons would not be relocated. Only visually.
How could i achieve this with Animators.
What i am trying to achieve
So could someone nudge me in the right direction.
This solved my problem.
public static ValueAnimator slideAnimator(int start, int end, final View view, int duration) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = val;
view.setLayoutParams(layoutParams);
}
});
animator.setDuration(duration);
return animator;
}
Credit to: Android property animation: how to increase view height?
Well, that can be done with a "one-liner":
TransitionManager.beginDelayedTransition(viewGroup);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = endValue;
view.invalidate();
Here's the animation:
public class WidthAnimation extends Animation {
protected final int originalWidth;
protected final View view;
protected float perValue;
public WidthAnimation(View view, int fromWidth, int toWidth) {
this.view = view;
this.originalWidth = fromWidth;
this.perValue = (toWidth - fromWidth);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().width = (int) (originalWidth + perValue * interpolatedTime);
view.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
}
When called by this (animating the View to be zero width), it works fine:
WidthAnimation widthAnim = new WidthAnimation(dashboardContainerView, getWindowWidthInPixels(), 0);
widthAnim.setDuration(500);
dashboardContainerView.startAnimation(widthAnim);
But when called by this (animating the View to being displayed), applyTransform is not called, and the animation is not shown:
WidthAnimation widthAnim = new WidthAnimation(dashboardContainerView, 0, getWindowWidthInPixels());
widthAnim.setDuration(500);
dashboardContainerView.startAnimation(widthAnim);
Both animations are being triggered by screen clicks. The getWindowWidthInPixels() method works correctly. I've seen several other questions on SO that suggest calling invalidate() or requestLayout() on the View, or its parent, can resolve this, but for me those solutions do not work.
Wow, that is weird, that it would work to shrink but not grow. Maybe Android is discarding the startAnimation call because the view is width 0. I suppose one quick fix would be to set the width to 1 before starting the grow animation.
dashboardContainerView.getLayoutParams().width = 1;
WidthAnimation widthAnim = new WidthAnimation(dashboardContainerView, 0, getWindowWidthInPixels());
widthAnim.setDuration(500);
dashboardContainerView.startAnimation(widthAnim);
Another option would be to switch to ObjectAnimator. My animations got a lot more straightforward when I did that. Yours would look something like this:
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dashboardWidth", fromWidth, toWidth);
anim.setDuration(500);
dashboardContainerView.startAnimation(anim);
public void setDashboardWidth(int width) {
ViewGroup.LayoutParams params = dashboardContainerView.getLayoutParams();
params.width = width;
dashboardContainerView.setLayoutParams(params);
}
That is the pattern I usually use with success. I don't have to call requestLayout in that case, but maybe setLayoutParams is doing that. Maybe just the getLayoutParams and requestLayout that you are using would be fine too.
Anyway, I really like the ObjectAnimator because it is so simple. One thing you do have to watch out for is if you are using ProGuard, you have to make sure it doesn't clean out the setDashboardWidth method, because it is not directly called in your code. It is called from within ObjectAnimator, which finds the method using reflection. Thus the exact name and signature of the method has to match the "dashboardWidth" property name in the call to ofInt.
Is there any way to animate a cropping of an ImageView?
Say for example, the ImageView is 720 x 480. I want to chop off the bottom rows of pixels with an animation until the ImageView is completely gone. I have only been able to move the image up when the onclicklistener is enabled, and make it transparent, which is ok, but not what the designer asked for.
ValueAnimator anim = ValueAnimator.ofInt(myImageView.getMeasuredHeight(), 0);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = myImageView.getLayoutParams();
layoutParams.height = val;
myImageView.setLayoutParams(layoutParams);
}
});
anim.setDuration(1000);
anim.start();
I got code from here and just changed it to height: ObjectAnimator animate LinearLayout width
Assuming you only want to chop off the height, using ObjectAnimator will work out to create such a custom cropped-height animation,
First of all you need to specify getHeight() and setHeight() methods and then create a custom animation with ObjectAnimator.
public float getHeight() { ... }
public void setHeight(float h) { ... }
ObjectAnimator heightAnim= ObjectAnimator.ofFloat(yourImageView, "height", heightBegin, heightEnd);
I am trying to animate changes of layout width. The layout element I'm trying to animate is RelativeLayout. It contains TextView which is positioned with the rule ALIGN_PARENT_BOTTOM. I'm creating everything from code by extending RelativeLayout viewgroup.
The following is a part of constructor:
1 title = new TextView(c);
2 LayoutParams tvParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
3 tvParams.addRule(ALIGN_PARENT_BOTTOM);
4 title.setLayoutParams(tvParams);
5 title.setText(this.feed.title);
6 this.addView(title);
And this is ValueAnimator:
mScaleX = ValueAnimator.ofInt(mParamsOriginal.width, mParamsLarge.width);
mScaleX.setInterpolator(new LinearInterpolator());
mScaleX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.width = val;
requestLayout();
}
});
The thing is then I comment rows 2-4 and just put TextView on the layout it works just fine. Otherwise profiler says it causes constant re-measurement of the view and the device spends 90% of processor time on it.
So, is there a way to solve this issue?
I have a LinearLayout that I use as a container for some buttons and textview's that I would like to animate the height of to give an impression of the layout sliding down when the user presses a "show" button.
I have set the LinearLayout to layout_height="0dp" and visibility="gone" in my xml. I then wish to set it to be visible and whatever height is need to wrap the content. At the moment I'm having issues even animating it at all, nevermind the wrap content height.
Here's my method for animating:
private void toggle(final LinearLayout v) {
v.setVisibility(View.VISIBLE);
ValueAnimator va = ValueAnimator.ofInt(0, 300);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
v.getLayoutParams().height = value.intValue();
v.invalidate();
}
});
va.start();
}
Perhaps the problem is how I am setting the height of the LinearLayout? Or am I misunderstanding the function of the ValueAnimator? I've looked around at the blog post's by Chet Haase but they do not contain any specific height animation examples. Neither have I been able to find and good examples of how to work with animations of height using API's from 3.0+. Would love some help on this, thanks!
Looking at this blog post: http://tech.chitgoks.com/2011/10/29/android-animation-to-expand-collapse-view-its-children/ I found that I shouldn't use view.invalidate() to have the layout redrawn. I should use view.requestLayout().
Thus the code becomes something like this:
ValueAnimator va = ValueAnimator.ofInt(0, height);
va.setDuration(700);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
v.getLayoutParams().height = value.intValue();
v.requestLayout();
}
});
I just wanted to add a note on how to get the height of the LinearLayout as well to make the animation dynamic. To get the height all the views need to be drawn first. Therfor we have to listen for an event that tells us that the drawing is done. This can be done from the onResume() method like this (note that in my xml I have declared the container to wrap_content for height and it is also visible, since I want to hide it from the start I do that efter measuring it):
#Override
public void onResume() {
super.onResume();
final ViewTreeObserver vto = filterContainer.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
height = filterContainer.getHeight();
filterContainer.setVisibility(View.GONE);
filterContainer.getLayoutParams().height = 0;
filterContainer.requestLayout();
ViewTreeObserver obs = filterContainer.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
});
}