Android - Updating View's Height on translationY() Animation - android

I have a layout that I want to expand after button click, as shown below:
The problem is I need to use Animation, so I decided to use View.animate.translationY(). Here is my code:
private void showBottomThreeLines(boolean show){
if(show)
mShiftContainer.animate().translationY(0);
else
mShiftContainer.animate().translationY(-(mFifthLineContainer.getHeight() * 3));
}
However, this is what I get after testing:
The current height is still the same as the previous height! The view's height is using MATCH_PARENT. I even tried to change it to 1000dp, but it still has the same height. How do I update view's height during translationY() animation?

After doing some research, I find out that defining view height after its translation will not increase its height at all. It appears that the sum of the entire view's height CANNOT go exceed its parent layout's height. Which means, if you set parent layout's height as MATCH_PARENT and your screen size is 960 dp, your child view's maximum height will be 960 dp, even if you define its height, e.g. android:layout_height="1200dp".
Therefore, I decided to dynamically re-size parent layout's height, and make the footer layout's height MATCH_PARENT. By default, my parent layout's height is MATCH_PARENT, but I call below method on onCreateView():
private void adjustParentHeight(){
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
ViewGroup.LayoutParams params = mView.getLayoutParams();
mFifthLineContainer.measure(0, 0);
params.height = metrics.heightPixels + (mFifthLineContainer.getMeasuredHeight() * 3);
mView.setLayoutParams(params);
}
This will make my footer layout become off-screen. Then I tried to use View.animate().translationY(), but then I got another problem! There is a bug in Android animation that causes flicker when you call View.setY() on onAnimationEnd(). It seems the cause is onAnimationEnd() is being called before the animation truly ends. Below are references that I use to solve this problem:
Android Animation Flicker
Android Flicker when using Animation and onAnimationEnd Listener
Therefore, I changed my showBottomThreeLines() method:
private void showBottomThreeLines(boolean show){
if(show){
TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, -(mFifthLineContainer.getHeight() * 3), 0);
translateAnimation.setDuration(300);
translateAnimation.setFillAfter(true);
translateAnimation.setFillEnabled(true);
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
mShiftContainer.setY(mShiftContainer.getY() + mFifthLineContainer.getHeight() * 3);
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
mShiftContainer.startAnimation(translateAnimation);
} else{
TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, mFifthLineContainer.getHeight() * 3, 0);
translateAnimation.setDuration(300);
translateAnimation.setFillAfter(true);
translateAnimation.setFillEnabled(true);
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
mShiftContainer.setY(mFifthLineContainer.getY());
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
mShiftContainer.startAnimation(translateAnimation);
}
}

Use this:
private ActionMode mActionMode;
...
private void expandView(View summary, int height, final boolean isSearch) {
if (isSearch) summary.setVisibility(View.VISIBLE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.EXACTLY);
summary.measure(widthSpec, height);
Animator animator = slideAnimator(summary.getHeight(), height, summary);
animator.start();
}
private void collapseView(final View summary, int height, final boolean isSearch) {
int finalHeight = summary.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, height, summary);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.EXACTLY);
summary.measure(widthSpec, height);
Animator animator = slideAnimator(summary.getHeight(), height, summary);
animator.start();
mAnimator.start();
}
/**
* Slide animation
*
* #param start start animation from position
* #param end end animation to position
* #param summary view to animate
* #return valueAnimator
*/
private ValueAnimator slideAnimator(int start, int end, final View summary) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = summary.getLayoutParams();
layoutParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, value, getResources().getDisplayMetrics());//value;
summary.setLayoutParams(layoutParams);
}
});
return animator;
}
private ActionMode.Callback mActionModeCallBack = new ActionMode.Callback() {
//Contextual action menu. Shows different options in action bar when a list item is long clicked!
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = actionMode.getMenuInflater();
inflater.inflate(R.menu.contextual_menu_options, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
mActionMode.finish();
return true;
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
mActionMode = null;
}
};
Hope this will help you!

Related

Enlarge animation in android

I want to achieve the below animation in android I have tried scenes but scenes do not work with text as per docs it is confirmed :
"If you try to resize a TextView with an animation, the text will pop to a new location before the object has completely resized. To avoid this problem, do not animate the resizing of views that contain text."
Please any solution , the enlarged layout text can contain images too.
animation video
this thing worked some how ,but the animation is little jittery,I guess layout height final value is attained first and layoutWidth later, have to fix this. this is my enlarge/reduce animation :
public class EnlargeAnimation extends Animation {
private final int diffHeight;
private final int diffWidth;
private final int initialHeight;
private final int initialWidth;
private final View targetView;
public EnlargeAnimation(View targetView, float targetHeight, float targetWidth) {
this.targetView = targetView;
this.initialHeight = targetView.getMeasuredHeight();
this.initialWidth = targetView.getMeasuredWidth();
this.diffHeight = (int) (targetHeight-initialHeight);
this.diffWidth = (int) (targetWidth-initialWidth);
}
#Override
public boolean willChangeBounds() {
return true;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float newHeight = initialHeight + diffHeight * interpolatedTime;
float newWidth = initialWidth + diffWidth * interpolatedTime;
targetView.getLayoutParams().height = (int) newHeight;
targetView.getLayoutParams().width = (int) newWidth;
targetView.requestLayout();
}
}
this is when enlarge animation is called :
I am using viewpager so i have to make padding negative to enlarge the card size :
ValueAnimator paddingAnimator = ValueAnimator.ofInt(20, -10).setDuration(400);
paddingAnimator.setInterpolator(new LinearInterpolator());
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int padding = (int) animation.getAnimatedValue();
view.setPadding(padding,
(int) DeviceUtils.convertDpToPx(50, v.getContext()), padding,
(int) DeviceUtils.convertDpToPx(50, v.getContext()));
view.requestLayout();
}
});
viewPagerItemSizeListener.onEnlarged();
EnlargeAnimation
enlargeAnimation =
new EnlargeAnimation(cardView, screenHeight, screenWidth);
enlargeAnimation.setDuration(400);
enlargeAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
paddingAnimator.start();
seeExampleText.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationEnd(Animation animation) {
stage.setVisibility(View.GONE);
cardEnlargedWidth = cardView.getLayoutParams().width;
cardEnlargedHeight = cardView.getLayoutParams().height;
crossContianer.setVisibility(View.VISIBLE);
detailTextContianer.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(enlargeAnimation);
I guess no one is reading it, but if something is confusing about variables let me know i will edit the answer.

more button between two recycler view

I have two recycler views in one screen and in between of them, there is one more button like this.
I want to update the height of recycler view of the upper one to MATCH PARENT with animation, So I had tried this.
binding.moreLl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ValueAnimator anim = ValueAnimator.ofInt();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = binding.fragmentWalletRv.getLayoutParams();
layoutParams.height = val;
binding.fragmentWalletRv.setLayoutParams(layoutParams);
}
});
anim.setDuration(500);
anim.start();
}
});
But unfortunately i cant get the expected result. So please guide me how i can do that thing.
Thanks in advance.
If you just want to have an animation when layout changes, no need to use a custom animation.
Try adding default layout change animation by enabling
android:animateLayoutChanges="true"
to the parent layout in xml.
On your more button click, only change the LayoutParams of the Recyclerview.
To change the speed of animation
LinearLayout layout = mContentView.findViewById(R.id.parent_layout);
LayoutTransition lt = layout.getLayoutTransition();
lt.setDuration(2000);
public class SlideAnimation extends Animation {
int mFromHeight;
int mToHeight;
View mView;
public SlideAnimation(View view, int fromHeight, int toHeight) {
this.mView = view;
this.mFromHeight = fromHeight;
this.mToHeight = toHeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation transformation) {
int newHeight;
if (mView.getHeight() != mToHeight) {
newHeight = (int) (mFromHeight + ((mToHeight - mFromHeight) * interpolatedTime));
mView.getLayoutParams().height = newHeight;
mView.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;
}
}
Use above animation like below
rv1 = findViewById(R.id.rv1);
Animation animation = new SlideAnimation(rv1, rv1.getHeight(), 500);
animation.setInterpolator(new AccelerateInterpolator());
animation.setDuration(500);
rv1.setAnimation(animation);
rv1.startAnimation(animation);

ValueAnimator/Expand/Collapse view not giving correct height

I have a search bar that expands when icon is clicked in toolbar. Now, everything is cool when it is expanded, everything works fine, the thing that is not good is that the height isnt good when the search bar is collapsed. The toolbar upon collapsing search bar has bigger height than it had. Here is the code:
private void expand(View view) {
//set Visible
view.setVisibility(View.VISIBLE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(widthSpec, heightSpec);
ValueAnimator mAnimator = slideAnimator(0, view.getMeasuredHeight(), view);
mAnimator.start();
}
private ValueAnimator slideAnimator(int start, int end, final View view) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//Update Height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = value;
view.setLayoutParams(layoutParams);
}
});
return animator;
}
private void collapse(final View view) {
int finalHeight = view.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0, view);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animator) {
//Height=0, but it set visibility to GONE
view.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
Everything is good here, check where you call method if you are changing background or any other property of the toolbar.

TextView animation expand only one line

I need to animate a TextView when i click a button. The height of the TextView is wrap_content. This TextView is inside a RecyclerView row and i need to expand it from visibility gone to his real height with content. I used ValueAnimator.
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = tvAdditional.getLayoutParams();
layoutParams.height = value;
tvAdditional.setLayoutParams(layoutParams);
}
});
return animator;
}
private void expand(View v) {
v.setVisibility(View.VISIBLE);
v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ValueAnimator mAnimator = slideAnimator(0, v.getMeasuredHeight());
mAnimator.start();
}
private void collapse(final View v) {
int finalHeight = v.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animator) {
v.setVisibility(View.GONE);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
In debug i noticed that when i use getMeasuredHeight() method on view in expand, the value is always 76, also if i added items with more than one row.
P.s. i call expand and collapse inside the click listener of the row.
Screenshot:
SOLVED
I changed my expand method adding a listener in which i set the height to wrap_content at the end of the animation:
private void expand(final View v) {
v.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
final int targetHeight = v.getMeasuredHeight();
ValueAnimator mAnimator = slideAnimator(0, targetHeight);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
v.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
v.requestLayout();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
When you have the Visibility as GONE, you cannot know it's measured height until it's actually drawn. In order to have that, you need to have GlobalLayoutListener on that TextView, so you can get it's measured height when it's drawn.
In your expand method, I see that you set the Visibility to Visible and then try to measure it, which won't be drawn immediately, so you cannot get the height.
So what you can do to get the actual height of the TextView if it's visibility is initially gone, first you should change the initial visibility to VISIBLE, second, set a Global Layout Listener and as third step, store the TextView's height in a global value after you measure it, and as the last step, you can set the Visibility of TextView as GONE.
This process is normally really quick, so you won't be able to see the TextView's visibility changing from VISIBLE to GONE with your eyes.

Animating Android ListView header height causes flicker at the end of animation

Problem
I'm trying to animate the ListView header height. I can get the animation right, but after the animation completes the ListView flickers.
Tried and failed
Use AnimationSet.setFillAfter() without changing the layout params. The animation works fine, but when you start scrolling the list, header jumps back to original position.
Use AnimationSet.setFillAfter() with new layout params applied at onAnimationEnd(). After the animation ends the header jumps to twice the required height (animated height plus the height set in layout params). When you start scrolling the list, header snaps to the required height.
Code
if (mSearchAdapter.getCount() > 0 && mListView.getChildAt(0) == mHeaderPlaceholder) {
Log.i(TAG, "Animating list view to make room for info bar");
AnimationSet slideAnimation = new AnimationSet(true);
TranslateAnimation translate = new TranslateAnimation(0, 0, 0, newHeight);
translate.setDuration(mInfoBarAnimationDuration);
translate.setInterpolator(new DecelerateInterpolator());
slideAnimation.addAnimation(translate);
slideAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
isAnimatingViewTransition = true;
}
#Override
public void onAnimationEnd(Animation animation) {
isAnimatingViewTransition = false;
final AbsListView.LayoutParams layoutParams = (AbsListView.LayoutParams) mHeaderPlaceholder.getLayoutParams();
layoutParams.height = newHeight;
mHeaderPlaceholder.setLayoutParams(layoutParams);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
mListView.startAnimation(slideAnimation);
} else {
Log.i(TAG, "Adjusting list view header to make room for info bar");
mHeaderPlaceholder.getLayoutParams().height = newHeight;
}
I think the flicker can be avoided by listening for and overriding the events onPreDraw() or onGlobalLayout() of the ViewTreeObserver of the ListView. But I don't know exactly how I can achieve it.
Any help is much appreciated!
Instead of animating ListView header I opted to used ValueAnimator to achieve the same effect. Here is code:
if (mSearchAdapter.getCount() > 0 && mListView.getChildAt(0) == mHeaderPlaceholder) {
ValueAnimator mSlideListViewAnimator = ObjectAnimator.ofInt(from, to);
mSlideListViewAnimator.setDuration(mInfoBarAnimationDuration);
mSlideListViewAnimator.setInterpolator(new DecelerateInterpolator());
mSlideListViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer animatedYValue= (Integer) animation.getAnimatedValue();
final AbsListView.LayoutParams layoutParams = (AbsListView.LayoutParams) mHeaderPlaceholder.getLayoutParams();
layoutParams.height = animatedYValue;
mHeaderPlaceholder.requestLayout();
}
});
mSlideListViewAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
isAnimatingViewTransition = true;
}
#Override
public void onAnimationEnd(Animator animation) {
isAnimatingViewTransition = false;
}
});
mSlideListViewAnimator.start();
} else {
Log.i(TAG, "Adjusting list view header to make room for info bar");
mHeaderPlaceholder.getLayoutParams().height = newHeight;
}

Categories

Resources