I'm trying to create an animation that expands a View from top to bottom.
Like this:
I can't use a ScaleAnimation with pivotY set to 0 because this stretches the Nine-Patch like a normal Bitmap. I need to modify the height of my View.
My current solution: (test is the View reference)
ValueAnimator anim = ValueAnimator.ofInt(test.getMeasuredHeight(), 800);
anim.setInterpolator(new FastOutSlowInInterpolator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = test.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
test.setLayoutParams(params);
}
});
anim.setDuration(250).start();
This works perfectly, but it expands the view to both sides:
Does anyone know a way to create such animation? Any help is appreciated.
A solution is to keep your implementation but limit the expansion to bottom by having an element above that won't let your view grow up:
Example:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
...
android:layout_height="100dp"/> //assuming your view starts 100dps from top
<YourView
...
.../>
</LinearLayout>
Since your container is a LinearLayout and the first View(FrameLayout) is 100dp height and above the view you wish to expand, it won't expand it's Height up because it can't overlap an element of the same hierarchy, so it will expand down
It also works if you use RelativeLayout by adding android:below="#id/framelayout_id" to YourView which will also guarantee that YourView doesn't expand up
Add an extra variable that holds initial position on Y axis.
expands top
public void expandTop(final View test) {
final float bottomAnchor = test.getY() + test.getHeight();
final ValueAnimator anim = ValueAnimator.ofInt(test.getMeasuredHeight(), 800);
anim.setInterpolator(new FastOutSlowInInterpolator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = test.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
test.setY(bottomAnchor - (int) animation.getAnimatedValue());
test.setLayoutParams(params);
}
});
anim.setDuration(2500).start();
}
expandBottom
public void expandBottom(final View test) {
final float topAnchor = test.getY();
final ValueAnimator anim = ValueAnimator.ofInt(test.getMeasuredHeight(), 800);
anim.setInterpolator(new FastOutSlowInInterpolator());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = test.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
test.setY(topAnchor);
test.setLayoutParams(params);
}
});
anim.setDuration(2500).start();
}
In my Android app I want to hide/show a Toolbar in an animation and adjust the FrameLayout below it so that it always fills up the whole screen. I do that by animating the top margin of the FrameLayout according to the display status of the Toolbar:
public void toggleToolbars(final boolean show) {
mToolbarHidden = !show;
final Toolbar tt = getBaseActivity().getToolbar();
final FrameLayout cv = getBaseActivity().getContentView();
int translation = show ? 0 : -tt.getHeight();
ValueAnimator animator = ValueAnimator.ofFloat(ViewHelper.getTranslationY(tt), translation).setDuration(200);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float translationY = (float) animation.getAnimatedValue();
ViewHelper.setTranslationY(tt, translationY);
FrameLayout.LayoutParams clp = (FrameLayout.LayoutParams) cv.getLayoutParams();
clp.topMargin = tt.getHeight() + (int) translationY;
cv.setLayoutParams(clp);
}
});
animator.start();
}
This works fine, except for a flickering in the animation. I assume that using setLayoutParams is a costly method. requestLayout() is equally slow. How can I make this animation smoother? Can I reduce the frame rate somehow?
Note, that for certain reasons I cannot use setTranslationY and the built-in animations of CoordinatorLayout.
In android 5.0 i am trying to work with circular reveal animation
Problem
When i click on button to start reveal animation, on first click animation doesn't start
Second Click onwards it works normally
My Code
public class MainActivity extends ActionBarActivity {
Animator a;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final View cardType = findViewById(R.id.cardtype);
cardType.setVisibility(View.GONE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
a = ViewAnimationUtils.createCircularReveal(cardType,
cardType.getWidth(),
cardType.getHeight(),
0,
cardType.getHeight() * 2)
.setDuration(2500);
a.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
cardType.setVisibility(View.VISIBLE);
}
});
a.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
cardType.setVisibility(View.GONE);
}
});
findViewById(R.id.icon_first_activity).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
a.start();
}
});
}
}}
I haven't tried your code, but I think you have a small ordering problem. I think you just need to set the cardType visible before you start the animation.
Edited to add:
... and you should be setting your button View.INVISIBLE, not View.GONE.
Here: This code works.
Edited once more to add:
Yes. Your problem is that you set the view GONE initially. That means it has 0 size. Then you use cardType.getHeight and cardType.getWidth as reveal coordinates. They are 0. You are going to want to set the view INVISIBLE, initially, and then use width/2 and height/2 as the center of the reveal.
Basically what others answers say, it's correct, but the problem is if you want visibility GONE (because your layout requires it GONE!) you have to set visibility INVISIBLE in the xml with height 0dp (and/or width 0dp as well) and programmatically set the correct LayoutParams even inside the click event it will work. For example my code:
...
expandButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//To not have empty scroll, the container is INVISIBLE with 0dp height.
//Otherwise the Reveal effect will not work at the first click.
//Here I set the parameters programmatically.
viewContainer.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
if (viewContainer.getVisibility() == View.VISIBLE) {
expandButton.animate().rotation(0f).setDuration(duration).start();
Utils.unReveal(viewContainer, 0, 0);
} else {
expandButton.animate().rotation(180f).setDuration(duration).start();
Utils.reveal(viewContainer, viewContainer.getWidth(), 0);
}
}
});
...
#TargetApi(VERSION_CODES.LOLLIPOP)
public static void reveal(final View view, int cx, int cy) {
if (!hasLollipop()) {
view.setVisibility(View.VISIBLE);
return;
}
//Get the final radius for the clipping circle
int finalRadius = Math.max(view.getWidth(), view.getHeight());
//Create the animator for this view (the start radius is zero)
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
//Make the view VISIBLE and start the animation
view.setVisibility(View.VISIBLE);
animator.start();
}
#TargetApi(VERSION_CODES.LOLLIPOP)
public static void unReveal(final View view, int cx, int cy) {
if (!hasLollipop()) {
view.setVisibility(View.GONE);
return;
}
//Get the initial radius for the clipping circle
int initialRadius = view.getWidth();
//Create the animation (the final radius is zero)
Animator animator =
ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, 0);
//Make the view GONE when the animation is done
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.GONE);
}
});
//Start the animation
animator.start();
}
If you set only GONE in the xml, the first time will never work because height/width/x/y/etc.. are 0. Also, if you just set INVISIBLE before the call to the animation it will not work as well, but if you start with visibility INVISIBLE it will initialize the layout params.
what i did is, Like i have two view with same height,As we now visibility gone returns 0 {height and width} than i am giving visible view height every time and its work for me.
The solution is don't get values directly into code
Either put the animation code on click and the values outside onclick
or get the values from other activity
By values i mean cardType.getWidth() and cardType.getHeight()
I've got a TextView that I want to have an animation set on.
Initially the TextView is set to be visibility=GONE with a height of wrap_content. When the user preforms a specific action, the TextView is set to visible. After a few seconds I want to animate the height of the TextView from it's wrap_content height to 0dp. That I have working w/o any issue. My issue comes when the animation is over. After the animation is complete I set the TextView back to visibility=GONE and try to reset the TextView's height back to wrap_content but the height doens't seem to change.
I've tried setting the TextView's layout params back to height of wrap_content followed by invalidating the TextView but that doesn't work.
Any idea how to go about doing that?
Here is how I'm doing the animation currently:
Animation slideUp = new Animation() {
{
setDuration(1000);
setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) { }
#Override
public void onAnimationRepeat(Animation animation) { }
#Override
public void onAnimationEnd(Animation animation) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mTxtTimerSet.getLayoutParams();
lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
mTxtTimerSet.setVisibility(View.INVISIBLE);
mTxtTimerSet.setLayoutParams(lp);
mTxtTimerSet.invalidate();
mTxtTimerSet.setVisibility(View.GONE);
}
});
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mTxtTimerSet.getLayoutParams();
lp.height = mTxtTimerSet.getHeight() - ((int) (mTxtTimerSet.getHeight() * interpolatedTime));
mTxtTimerSet.setLayoutParams(lp);
}
};
mTxtTimerSet.startAnimation(slideUp);
Thanks
How are you checking height of your TextBox after animation finish ? Maybe you want to use visibility=View.INVISIBLE instead of View.GONE ? If you want to animate it again you can try to assign width when animation starts.
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);
}
});
}