Android 2.3: Animating a View? - android

How can you change the coordinates of a view while its animating? For instance, if I'm doing a translation of a View on the screen from left to right, I want the View to push everything to the right of it as its moving?
public Animation expandHiddenPanel(final View v, final boolean expand) {
panelExpanded = expand;
v.measure(MeasureSpec.makeMeasureSpec(200, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
final int initialWidth = v.getMeasuredWidth();
Log.i("test", "initialWidth = " + initialWidth);
v.getLayoutParams().width = 0;
v.setVisibility(View.VISIBLE);
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newWidth;
if (expand) {
newWidth = (int)(initialWidth * interpolatedTime);
Log.i("test", "new Width = " + newWidth);
}
else {
newWidth = (int)(initialWidth * (1 - interpolatedTime));
Log.i("test", "new Width = " + newWidth);
}
v.getLayoutParams().width = newWidth;
v.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setInterpolator(new AccelerateInterpolator());
a.setDuration(2500);
a.setFillAfter(true);
v.startAnimation(a);
return a;
}

I fixed this by overlaying the main layout over my panel, placing the button in the upper left hand corner of my main layout, and changing the code i posted above to pad the left margin of my main layout to reveal the panel.
To get the sliding drawer effect, I changed the panel's visibility and applied a translate animation that ran for the same duration and used the same interpolator.

Related

Android - Animate a view and change his real position (and another view follow with behavior dependency)

I'm trying to animate my appBar using a ViewDragHelper, when I drag it down it should start an animation and go down.
On top I have another view (pink in the example) that "follow" my appbar with a behavior (the top view depends on AppBarLayout).
As you can see here the appbar is animated but the "real" position do not change and, obviously, my top frame doesn't move.
In order to animate I use the following code:
#Override
public void onViewReleased(final View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (releasedChild.getTop() > childHeight) {
Log.e("TAG", "SHOULD SCROLL " + releasedChild.getTop() + " > " + childHeight);
final int offset = height - releasedChild.getTop() - 100;
ViewCompat.animate(releasedChild).translationY(offset).setDuration(1000).setInterpolator(new OvershootInterpolator()).start();
} else {
Log.e("TAG", "SHOULD NOT SCROLL " + releasedChild.getTop() + " < " + childHeight);
eleasedChild.offsetTopAndBottom(-releasedChild.getTop());
}
if (mDragFrameLayoutController != null) {
mDragFrameLayoutController.onDragDrop(false);
}
}
I've tried with many other animations:
TranslateAnimation scale = new TranslateAnimation(0.0f, 0.0f, 0.f, offset);
scale.setInterpolator(new OvershootInterpolator());
scale.setDuration(3000L);
scale.setFillEnabled(true);
scale.setFillAfter(true);
scale.setAnimationListener(new Animation.AnimationListener() {
or
ObjectAnimator moveY = ObjectAnimator.ofInt(releasedChild, "y", releasedChild.getTop(), offset);
AnimatorSet as = new AnimatorSet();
as.play(moveY);
as.start();
All these snippets give me the same result.
The only way I found to move the view is using offset but this doesn't animate, it just moves down...
releasedChild.offsetTopAndBottom(offset);
Any idea? Thanks.
I finally found the solution:
on my viewDragHelper I overrode the following methods:
#Override
public int getViewVerticalDragRange(View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
#Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if(yvel>0) {
mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), getMeasuredHeight()-releasedChild.getMeasuredHeight());
} else {
mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), 0);
}
invalidate();
}
then on my custom view I overrode the following method:
#Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
And everything worked fine!
I hope this can help someone in future.

Expand Animation to work on both sides? (on negative axis)

I implemented an ExpandAnimation on view like this:
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
LayoutParams lp =
(LayoutParams) view.getLayoutParams();
if(rightAnimation){
lp.width = (int) (mStartWidth + mDeltaWidth *
interpolatedTime);
view.setLayoutParams(lp);
}else{
}
}
With this animation my view expands and shrinks.
What i want to do is to implement this animation to the other side (to -x side) as well. But i get confused a little bit, since shrinking the width will not work.
(Since minus widths or heights are not allowed)
Does anyone know a better way to implement expand animation to the left (-x) or up(-y) side as well?
Or maybe mirroring the view?
Finally I achieved to make a left hand side expand animation. Here is the code:
private class ExpandAnimation extends Animation implements Animation.AnimationListener {
private final int mStartWidth;
private final int mDeltaWidth;
int delta;
public ExpandAnimation(int startWidth, int endWidth) {
mStartWidth = startWidth;
mDeltaWidth = endWidth - startWidth;
RelativeLayout.LayoutParams param = (RelativeLayout.LayoutParams) getLayoutParams();
delta = param.width;
setAnimationListener(this);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
RelativeLayout.LayoutParams param = (RelativeLayout.LayoutParams) getLayoutParams();
delta = param.width;
if(param.width <= 0){
delta = 0;
}
param.width = (int) (mStartWidth + mDeltaWidth *
interpolatedTime);
if(delta != 0){
delta = (int) (mStartWidth + mDeltaWidth *
interpolatedTime) - delta;
}
param.leftMargin -=delta;
setLayoutParams(param);
}
#Override
public boolean willChangeBounds() {
return true;
}
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
setText(text);
requestLayout();
mExpanded = !mExpanded;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
For a reason that I don't know, width comes as -2 in the start. So i filter out that by an if. Apply Transformation method does the job.

Expand/Collapse animation in Android ListView

I'm trying to have my listview expanded/collapsed with an animation, and I have the following code.
What I wanna have is a listview, on the top part of the screen, that expands and collapses, but when it collapses up, I just wanna see the currently selected item from the listview (one one item, one row).
public static Animation expand(final View v, final boolean expand) {
try {
Method m = v.getClass().getDeclaredMethod("onMeasure", int.class,
int.class);
m.setAccessible(true);
m.invoke(v,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(
((View) v.getParent()).getMeasuredWidth(),
MeasureSpec.AT_MOST));
} catch (Exception e) {
e.printStackTrace();
}
final int initialHeight = v.getMeasuredHeight();
if (expand) {
v.getLayoutParams().height = 50;
} else {
v.getLayoutParams().height = initialHeight;
}
v.setVisibility(View.VISIBLE);
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime,
Transformation t) {
int newHeight = 50;
if (expand) {
newHeight = (int) (initialHeight * interpolatedTime);
} else {
newHeight = (int) (initialHeight * (1 - interpolatedTime));
}
v.getLayoutParams().height = newHeight;
v.requestLayout();
if (interpolatedTime == 1 && !expand)
v.setVisibility(View.GONE);
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(500);
return a;
}
The problem is that when collapsing, I'd like to have the first row of the listview visible, and not the listview completely hidden, but I can't find the part of the code that will do that.
Any help, please?
Thanks a lot in advance.

How to add animation to a view while it is added to mainview

In android how can i add animation to a view when its added to mainview, for example slowly growing and occupying the mainview. I think its possible but where to start, I need it for my ListView to expand when other view are removed.
Thanks
I have one demo. It moves up and down very smoothly when add or hide view
public class ExpandAnimation extends Animation {
private View mAnimatedView;
private LayoutParams mViewLayoutParams;
private int mMarginStart, mMarginEnd;
private boolean mIsVisibleAfter = false;
private boolean mWasEndedAlready = false;
ImageButton mImageButton;
/**
* Initialize the animation
* #param view The layout we want to animate
* #param duration The duration of the animation, in ms
*/
public ExpandAnimation(View view, int duration, ImageButton button) {
this.mImageButton = button;
setDuration(duration);
mAnimatedView = view;
mViewLayoutParams = (LayoutParams) view.getLayoutParams();
// if the bottom margin is 0,
// then after the animation will end it'll be negative, and invisible.
mIsVisibleAfter = (mViewLayoutParams.bottomMargin == 0);
mMarginStart = mViewLayoutParams.bottomMargin;
mMarginEnd = (mMarginStart == 0 ? (0- view.getHeight()) : 0);
view.setVisibility(View.VISIBLE);
button.setImageResource(R.drawable.bar_down);
}
#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);
mImageButton.setImageResource(R.drawable.bar_up);
}
mWasEndedAlready = true;
}
}
}
btnShowBookmarkBar.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
animation = new ExpandAnimation(bookmarkControlView,
CommConstant.DEFAULT_SHOW_UP_TIME, btnShowBookmarkBar);
btnShowBookmarkBar.startAnimation(animation);
}
});

How do I animate View.setVisibility(GONE)

I want to make an Animation for when a View gets it's visibility set to GONE. Instead of just dissapearing, the View should 'collapse'. I tried this with a ScaleAnimation but then the View is collapse, but the layout will only resize it's space after (or before) the Animation stops (or starts).
How can I make the Animation so that, while animating, the lower Views will stay directly below the content, instead of having a blank space?
Put the view in a layout if it's not and set android:animateLayoutChanges="true" for that layout.
There doesn't seem to be an easy way to do this through the API, because the animation just changes the rendering matrix of the view, not the actual size. But we can set a negative margin to fool LinearLayout into thinking that the view is getting smaller.
So I'd recommend creating your own Animation class, based on ScaleAnimation, and overriding the "applyTransformation" method to set new margins and update the layout. Like this...
public class Q2634073 extends Activity implements OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.q2634073);
findViewById(R.id.item1).setOnClickListener(this);
}
#Override
public void onClick(View view) {
view.startAnimation(new MyScaler(1.0f, 1.0f, 1.0f, 0.0f, 500, view, true));
}
public class MyScaler extends ScaleAnimation {
private View mView;
private LayoutParams mLayoutParams;
private int mMarginBottomFromY, mMarginBottomToY;
private boolean mVanishAfter = false;
public MyScaler(float fromX, float toX, float fromY, float toY, int duration, View view,
boolean vanishAfter) {
super(fromX, toX, fromY, toY);
setDuration(duration);
mView = view;
mVanishAfter = vanishAfter;
mLayoutParams = (LayoutParams) view.getLayoutParams();
int height = mView.getHeight();
mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin - height;
mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) - height;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
int newMarginBottom = mMarginBottomFromY
+ (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime);
mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,
mLayoutParams.rightMargin, newMarginBottom);
mView.getParent().requestLayout();
} else if (mVanishAfter) {
mView.setVisibility(View.GONE);
}
}
}
}
The usual caveat applies: because we are overriding a protected method (applyTransformation), this is not guaranteed to work in future versions of Android.
I used the same technique as Andy here has presented. I wrote my own Animation class for that, that animate the margin's value, causing the effect of the item to disappear/appear.
It looks like this:
public class ExpandAnimation extends Animation {
// Initializations...
#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();
}
}
}
I have a full example that works on my blog post
http://udinic.wordpress.com/2011/09/03/expanding-listview-items/
I used the same technique as Andy here, and refined it so that it can be used for expanding and collapsing without glitches, also using a technique described here: https://stackoverflow.com/a/11426510/1317564
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;
class LinearLayoutVerticalScaleAnimation extends ScaleAnimation {
private final LinearLayout view;
private final LinearLayout.LayoutParams layoutParams;
private final float beginY;
private final float endY;
private final int originalBottomMargin;
private int expandedHeight;
private boolean marginsInitialized = false;
private int marginBottomBegin;
private int marginBottomEnd;
private ViewTreeObserver.OnPreDrawListener preDrawListener;
LinearLayoutVerticalScaleAnimation(float beginY, float endY,
LinearLayout linearLayout) {
super(1f, 1f, beginY, endY);
this.view = linearLayout;
this.layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
this.beginY = beginY;
this.endY = endY;
this.originalBottomMargin = layoutParams.bottomMargin;
if (view.getHeight() != 0) {
expandedHeight = view.getHeight();
initializeMargins();
}
}
private void initializeMargins() {
final int beginHeight = (int) (expandedHeight * beginY);
final int endHeight = (int) (expandedHeight * endY);
marginBottomBegin = beginHeight + originalBottomMargin - expandedHeight;
marginBottomEnd = endHeight + originalBottomMargin - expandedHeight;
marginsInitialized = true;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (!marginsInitialized && preDrawListener == null) {
// To avoid glitches, don't draw until we've initialized everything.
preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
if (view.getHeight() != 0) {
expandedHeight = view.getHeight();
initializeMargins();
adjustViewBounds(0f);
view.getViewTreeObserver().removeOnPreDrawListener(this);
}
return false;
}
};
view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
if (interpolatedTime < 1.0f && view.getVisibility() != View.VISIBLE) {
view.setVisibility(View.VISIBLE);
}
if (marginsInitialized) {
if (interpolatedTime < 1.0f) {
adjustViewBounds(interpolatedTime);
} else if (endY <= 0f && view.getVisibility() != View.GONE) {
view.setVisibility(View.GONE);
}
}
}
private void adjustViewBounds(float interpolatedTime) {
layoutParams.bottomMargin =
marginBottomBegin + (int) ((marginBottomEnd - marginBottomBegin) * interpolatedTime);
view.getParent().requestLayout();
}
}

Categories

Resources