I am using following code to animate expandable layout:
class ExpandAnimation extends Animation {
private View _view;
private int _startHeight;
private int _finishHeight;
public ExpandAnimation( View view, int startHeight, int finishHeight ) {
_view = view;
_startHeight = startHeight;
_finishHeight = finishHeight;
setDuration(500);
System.out.println(_startHeight);
System.out.println(_finishHeight);
}
#Override
protected void applyTransformation( float interpolatedTime, Transformation t ) {
int newHeight = (int)((_finishHeight - _startHeight) * interpolatedTime + _startHeight);
_view.getLayoutParams().height = newHeight;
_view.requestLayout();
}
#Override
public void initialize( int width, int height, int parentWidth, int parentHeight ) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds( ) {
return true;
}
};
This animation is created every time I click a button. It is called properly (checked System.out.println and it prints correct values) however in emulator animation runs only like once of 15 times. To be exact hiding it works great but expanding works only once a few times (on emulator, cant get it working on phone).
What could be the problem?
Thanks in forward
EDIT: layout I am trying to animate is FrameLayout. It has TextView as child and finishHeight is measured by textView measure height. The values are correct. I have also tried calling textView.requestLayout() in apply transformation to redraw layout but it is not working. It still expands only sometimes. If you need any more code feel free to ask.
Calling
((View) toExpand.getParent()).invalidate();
just after startAnimation solved my problem. Must check it on other devices but I think it will work.
Related
I have been trying to animate view in android and I have done that animation but that problem is that some times the animation stuck in the middle I don't know why.
what I am doing is I have a view in the middle and that view contains horizontal list view and when an item is clicked the view animates to the top and on the top when user clicks it goes down to its original position
here is the code for animation.
public class DropDownAnim extends Animation {
private final float targetHeight;
private final View view;
private final boolean down;
public DropDownAnim(View view, float targetHeight, boolean down) {
this.view = view;
this.targetHeight = targetHeight;
this.down = down;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
int newHeight;
if (down) {
newHeight = (int) (targetHeight * interpolatedTime);
} else {
newHeight = (int) (targetHeight * (1 - interpolatedTime));
}
view.getLayoutParams().height = newHeight;
view.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
when the view that contains horizontal list view and in that an item is clicked I am doing
DropDownAnim drop = new DropDownAnim(_View, (width_And_Height[1] / 100) *
Utils.get_Percentage_For_animate_View_Based_On_Mobile_Resloution(),
true); //true means go to top
drop.setDuration(DURATION);
_View.setAnimation(drop);
_View.startAnimation(drop);
and when the view is on the top
DropDownAnim drop = new DropDownAnim(_View, (width_And_Height[1] / 100) * Utils.get_Percentage_For_animate_View_Based_On_Mobile_Resloution(),
false);//false means go to is orignal position
drop.setDuration(DURATION);
_View.setAnimation(drop);
_View.startAnimation(drop);
this is returning the height of the screen.
width_And_Height[1]
first when the horizontal scroll view is in the center and i click on mad it goes up and when the horizontal scroll view is on top and i click on map it comes to its orignal position.
But now the problem is some times the view not come to its orignal position from top and stuck in the middle
here are the images.
when the horizontal scroll view is in the middle of screen
when the horizontal scroll view is on The Top of screen
when the view not come to its original position from top and stuck in the middle
I dont know what is the problem
Thank's for making time for my question,and please HELP :)
I am not sure why you are extending an Animation. If you are trying to change the position or height of a view you should be using PropertyAnimations.
If you are trying to grow a view use a simple ObjectAnimator and do the following.
ObjectAnimator animation = ObjectAnimator.ofFloat(mView,"scaleX",0,2);
animation.setDuration(duration);
animation.start();
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.
I have 2 LinearLayouts with views within them held in a container LinearLayout that is using layout_weight to determine their sizes. I am trying to shrink the top view when the user clicks inside the bottom view with an animation.
I extended Animation with a class:
public class ShrinkTopViewAnimation extends Animation {
protected int mOriginalHeight;
protected final LinearLayout mView;
public ShrinkTopViewAnimation(LinearLayout view) {
this.mView = view;
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mOriginalHeight = height;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation
) {
int shrinkAmount = (int)(mOriginalHeight * interpolatedTime);
ViewGroup.LayoutParams layoutParams = mView.getLayoutParams();
layoutParams.height = mOriginalHeight - shrinkAmount;
mView.setLayoutParams(layoutParams);
mView.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
}
The BottomView onClick calls:
ShrinkTopViewAnimation shrinkTopViewAnimation = new ShrinkTopViewAnimation(mTopLinearLayout);
shrinkTopViewAnimation.setDuration(1000);
mTopLinearLayout.startAnimation(shrinkTopViewAnimation);
I am very confused as to what is happening. What I am seeing is that the first time through the applyTransform the interpolatedTime is 0 so the .height is set to the exact same number that it was before. But the next call to the applyTransformation a getHeight call to the mView gives a number way bigger than the starting height and at the end of the transform when interpolatedTime is 1 the view is back to the original size. The visual effect is the top view jumps larger, then shrinks back to original size.
Both getHeight() and the initialize Height are listed as px in the documentation, and the LayoutParams.Height is listed as px. But it seems like there is some translation to dp possibly going on?
Any ideas?
To keep the animated result, use Animation.setFillAfter(true).
When set to true, the animation transformation is applied after the animation is over.
What you used here is View Animation. This kind of animation only modify rendering transformation, so the actual size of view is not changed.
If you need to changed the actual size, you have to set LayoutParams via a AnimationListener, or use a Property Animation.
Hi I am trying to animate the height of a view in android say every 5 seconds :-
height goes from 0 to 5
height goes from 5 to 10
height goes from 10 to 3 etc
I am using the code below :-
public class ShowAnimation extends Animation{
float finalHeight;
View imageview;
public ShowAnimation(View view,float deltaheight){
this.imageview=view;
this.finalHeight=deltaheight;
}
protected void applyTransformation(float interpolatedtime,Transformation t){
imageview.getLayoutParams().height=(int)(finalHeight*interpolatedtime);
imageview.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
and initialize it like this:-
Animation anidelta = new ShowAnimation(delta, deltaheight);
anidelta.setDuration(500/* animation time */);
delta.startAnimation(anidelta);
But with this i get the below:-
height goes from 0 to 5
height goes from 0 to 10
height goes from 0 to 3
i want the height to be animated from its previous height rather than from 0 everytime.
Can someone please help me here
Edit1:-
I did this
Animation anidelta = new ShowAnimation(delta, deltaheight);
anidelta.setDuration(500/* animation time */);
anidelta.setFillAfter(true);
delta.startAnimation(anidelta);
But it still animates from 0 to the newheight.
Ok so this is how I finally solved it:-
public class ResizeAnimation extends Animation
{
View view;
int startH;
int endH;
int diff;
public ResizeAnimation(View v, int newh)
{
view = v;
startH = v.getLayoutParams().height;
endH = newh;
diff = endH - startH;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
view.getLayoutParams().height = startH + (int)(diff*interpolatedTime);
view.requestLayout();
}
#Override
public void initialize(int width, int height, int parentWidth, int parentHeight)
{
super.initialize(width, height, parentWidth, parentHeight);
}
#Override
public boolean willChangeBounds()
{
return true;
}}
You need an extra flag to tell your applyTransformation method whether you want to increase or decrease the view height.
Add a flag to the constructor and save that flag as an instance variable, and use that flag to decide whether to increase or decrease the view height:
public class ShowAnimation {
// other instance variables
private boolean increaseHeight;
public ShowAnimation(View view, float deltaheight, boolean increaseHeight) {
// other initializations
this.increaseHeight = increaseHeight;
}
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (increaseHeight)
imageview.getLayoutParams().height = (int) (finalHeight * interpolatedTime);
else {
imageview.getLayoutParams().height = (int) (finalHeight * (1 - interpolatedTime));
}
imageview.requestLayout();
}
}
I know this post has not been active for over a year but I will post what I think is the easiest way to animate view's height anyway.
Instead of declaring Animation class, you could use View.setScaleY API available from API level 11.
Using this, you can specify view to scale within the range from 0-1 (0 means no height and 1 means original height) in desired second.
In XML set your view's height to its maximum height, then when inflating the view (e.g. in Activity#onCreate() or Fragment#onViewCreated()) you can
yourView.setScaleY(0);
Then after 5 seconds you can set
yourView.setScaleY(0.5f);
This will scale your view's height instantly. If you want to scale the view with animation, you can for example do something like this:
yourView.animate().scaleY(0.5f).setDuration(200).start();
I hope it helps.
Try animation set. I am not sure whether this is what you want.
Animation 1 for : height goes from 0 to 5
Animation 2 for : height goes from 0 to 10
Animation 3 for : height goes from 0 to 3
public void applyAnimation(ImageView ivDH) {
System.out.println("Inside applyAnimation()");
scaleAnim1 = new ScaleAnimation(0, 5, 0, 5);
scaleAnim1.setDuration(5000);
scaleAnim1.setRepeatCount(0);
scaleAnim1.setInterpolator(new AccelerateDecelerateInterpolator());
scaleAnim1.setFillAfter(true);
scaleAnim2 = new ScaleAnimation(-5, 10, -5, 10);
scaleAnim2.setDuration(5000);
scaleAnim2.setRepeatCount(0);
scaleAnim2.setInterpolator(new AccelerateDecelerateInterpolator());
scaleAnim2.setFillAfter(true);
scaleAnim3 = new ScaleAnimation(10, 3, 10, 3);
scaleAnim3.setDuration(5000);
scaleAnim3.setRepeatCount(0);
scaleAnim3.setInterpolator(new AccelerateDecelerateInterpolator());
scaleAnim3.setFillAfter(true);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(scaleAnim1);
animationSet.addAnimation(scaleAnim2);
animationSet.addAnimation(scaleAnim3);
animationSet.setDuration(15000);
animationSet.setFillAfter(true);
ivDH.clearAnimation();
ivDH.startAnimation(animationSet);
}
I am trying to apply an animation to a view in my Android app after my activity is created. To do this, I need to determine the current size of the view, and then set up an animation to scale from the current size to the new size. This part must be done at runtime, since the view scales to different sizes depending on input from the user. My layout is defined in XML.
This seems like an easy task, and there are lots of SO questions regarding this though none which solved my problem, obviously. So perhaps I am missing something obvious. I get a handle to my view by:
ImageView myView = (ImageView) getWindow().findViewById(R.id.MyViewID);
This works fine, but when calling getWidth(), getHeight(), getMeasuredWidth(), getLayoutParams().width, etc., they all return 0. I have also tried manually calling measure() on the view followed by a call to getMeasuredWidth(), but that has no effect.
I have tried calling these methods and inspecting the object in the debugger in my activity's onCreate() and in onPostCreate(). How can I figure out the exact dimensions of this view at runtime?
Use the ViewTreeObserver on the View to wait for the first layout. Only after the first layout will getWidth()/getHeight()/getMeasuredWidth()/getMeasuredHeight() work.
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
viewWidth = view.getWidth();
viewHeight = view.getHeight();
}
});
}
There are actually multiple solutions, depending on the scenario:
The safe method, will work just before drawing the view, after the layout phase has finished:
public static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) {
final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
#Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
runnable.run();
return true;
}
};
view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
Sample usage:
ViewUtil.runJustBeforeBeingDrawn(yourView, new Runnable() {
#Override
public void run() {
//Here you can safely get the view size (use "getWidth" and "getHeight"), and do whatever you wish with it
}
});
On some cases, it's enough to measure the size of the view manually:
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
int width=view.getMeasuredWidth();
int height=view.getMeasuredHeight();
If you know the size of the container:
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxWidth, View.MeasureSpec.AT_MOST)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxHeight, View.MeasureSpec.AT_MOST)
view.measure(widthMeasureSpec, heightMeasureSpec)
val width=view.measuredWidth
val height=view.measuredHeight
if you have a custom view that you've extended, you can get its size on the "onMeasure" method, but I think it works well only on some cases :
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int newHeight= MeasureSpec.getSize(heightMeasureSpec);
final int newWidth= MeasureSpec.getSize(widthMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
If you write in Kotlin, you can use the next function, which behind the scenes works exactly like runJustBeforeBeingDrawn that I've written:
view.doOnPreDraw { actionToBeTriggered() }
Note that you need to add this to gradle (found via here) :
android {
kotlinOptions {
jvmTarget = "1.8"
}
}
implementation 'androidx.core:core-ktx:#.#'
Are you calling getWidth() before the view is actually laid out on the screen?
A common mistake made by new Android developers is to use the width
and height of a view inside its constructor. When a view’s
constructor is called, Android doesn’t know yet how big the view will
be, so the sizes are set to zero. The real sizes are calculated during
the layout stage, which occurs after construction but before anything
is drawn. You can use the onSizeChanged() method to be notified of
the values when they are known, or you can use the getWidth() and
getHeight() methods later, such as in the onDraw() method.
Based on #mbaird's advice, I found a workable solution by subclassing the ImageView class and overriding onLayout(). I then created an observer interface which my activity implemented and passed a reference to itself to the class, which allowed it to tell the activity when it was actually finished sizing.
I'm not 100% convinced that this is the best solution (hence my not marking this answer as correct just yet), but it does work and according to the documentation is the first time when one can find the actual size of a view.
Here is the code for getting the layout via overriding a view if API < 11 (API 11 includes the View.OnLayoutChangedListener feature):
public class CustomListView extends ListView
{
private OnLayoutChangedListener layoutChangedListener;
public CustomListView(Context context)
{
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
if (layoutChangedListener != null)
{
layoutChangedListener.onLayout(changed, l, t, r, b);
}
super.onLayout(changed, l, t, r, b);
}
public void setLayoutChangedListener(
OnLayoutChangedListener layoutChangedListener)
{
this.layoutChangedListener = layoutChangedListener;
}
}
public interface OnLayoutChangedListener
{
void onLayout(boolean changed, int l, int t, int r, int b);
}
You can check this question. You can use the View's post() method.
Use below code, it is give the size of view.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.e("WIDTH",""+view.getWidth());
Log.e("HEIGHT",""+view.getHeight());
}
This works for me in my onClickListener:
yourView.postDelayed(new Runnable() {
#Override
public void run() {
yourView.invalidate();
System.out.println("Height yourView: " + yourView.getHeight());
System.out.println("Width yourView: " + yourView.getWidth());
}
}, 1);
I was also lost around getMeasuredWidth() and getMeasuredHeight() getHeight() and getWidth() for a long time.......... later i found that getting the view's width and height in onSizeChanged() is the best way to do this........ you can dynamically get your CURRENT width and CURRENT height of your view by overriding the onSizeChanged() method.
might wanna take a look at this which has an elaborate code snippet.
New Blog Post: how to get width and height dimensions of a customView (extends View) in Android http://syedrakibalhasan.blogspot.com/2011/02/how-to-get-width-and-height-dimensions.html
In Kotlin file, change accordingly
Handler().postDelayed({
Your Code
}, 1)
You can get both Position and Dimension of the view on screen
val viewTreeObserver: ViewTreeObserver = videoView.viewTreeObserver;
if (viewTreeObserver.isAlive) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
//Remove Listener
videoView.viewTreeObserver.removeOnGlobalLayoutListener(this);
//View Dimentions
viewWidth = videoView.width;
viewHeight = videoView.height;
//View Location
val point = IntArray(2)
videoView.post {
videoView.getLocationOnScreen(point) // or getLocationInWindow(point)
viewPositionX = point[0]
viewPositionY = point[1]
}
}
});
}
If you need to know the dimensions of a View right after it is drawn you can simply call post() on that given View and send there a Runnable that executes whatever you need.
It is a better solution than ViewTreeObserver and globalLayout since it gets called repeatedly not just once.
This Runnsble will execute only once and you will know the views size.
works perfekt for me:
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
CTEditor ctEdit = Element as CTEditor;
if (ctEdit == null) return;
if (e.PropertyName == "Text")
{
double xHeight = Element.Height;
double aHaight = Control.Height;
double height;
Control.Measure(LayoutParams.MatchParent,LayoutParams.WrapContent);
height = Control.MeasuredHeight;
height = xHeight / aHaight * height;
if (Element.HeightRequest != height)
Element.HeightRequest = height;
}
}