How to animate width of a holder in a CardView - android

I am trying to animate a bar (just an ImageView) in the onBindViewHolder method.
If i don't try to animate it, it is working perfectly with this line:
holder.progressBar.getLayoutParams().width = 100;
But as soon as i add a function to animate this bar/imageView, it doesn't work.
I can see the bar only on the first item in the recyclerView and the bar is not animated.
this is the code that i use. what is the problem?
Thanks to all.
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
//holder.progressBarWeatherFactor.getLayoutParams().width = 100;
animateWidth(0, 100, holder.progressBar.getLayoutParams());
}
public void animateWidth(int start, int end, final ViewGroup.LayoutParams myBar)
{
ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
valueAnimator.setDuration(1800);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
#Override
public void onAnimationUpdate( ValueAnimator valueAnimator)
{
myBar.width = ((int) valueAnimator.getAnimatedValue());
}
});
valueAnimator.start();
}

So, in order to help, i found a solution, maybe not the best but it is working and i hope it will help!
(the /10*3/64 is in relation with my image size, so don't matter of this)
#Override
public void onBindViewHolder(ViewHolder holder, int position)
{
animateWidth(0, 100, holder.progressBar);
}
public void animateWidth(int initialValue, int finalValue, ImageView image)
{
image.setPivotX(0f);
PropertyValuesHolder barWidth;
barWidth = PropertyValuesHolder.ofFloat("scaleX", 0, ((float)(((image.getResources().getDisplayMetrics().density)/10)*3)/64)*finalValue);
ObjectAnimator.ofPropertyValuesHolder(image, barWidth).setDuration(800).start();
}

Related

Translate animation for recyclerview and textview not smooth

I want to show a TextViewbelow the Toolbar, which should appear in case there is a new event coming in. It should slide down from below the Toolbar, appear for 1-2secs and then slide up again. Below the TextView is a RecyclerView with several items ordered in a GridLayout.
To achieve this, I'm using a TranslateAnimation to let the animation slide up again:
TranslateAnimation animate = new TranslateAnimation(0, 0, 0, -tvStatus.getHeight());
animate.setDuration(1000);
animate.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
tvStatus.setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
tvStatus.startAnimation(animate);
The sliding animation of the TextView itself is working fine, but the RecyclerView doesn't animate so well. The TextView slides up again until it is hidden and there is a gap for a short time.
And then the ReclerViewjust jumps up, which doesn't look very nice.
My question is: How can I adjust my code to make the RecylerViewslide up as well when the TextViewstarts to slide up?
EDIT - Solution:
I adjusted my solution including the input from Francescs answer. I solved the animation without a translation, just by changing the height of the view.
Method to expand statusbar:
public void expand() {
//set Visible
tvStatus.setVisibility(View.VISIBLE);
mAnimator = slideAnimator(0, mHeight);
mAnimator.setDuration(600);
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.start();
}
Method to collapse statusbar:
private void collapse() {
int finalHeight = tvStatus.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.setDuration(500);
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
tvStatus.setVisibility(View.GONE);
}
});
mAnimator.start();
}
Value Animator:
private ValueAnimator slideAnimator(int start, int end) {
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 = tvStatus.getLayoutParams();
layoutParams.height = value;
tvStatus.setLayoutParams(layoutParams);
}
});
return animator;
}
measureTextHeight() method called in onCreate to get TextView height:
public void measureTextHeight() {
tvStatus.setVisibility(View.VISIBLE);
tvStatus.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mHeight = tvStatus.getHeight();
if (mHeight > 0) {
tvStatus.getViewTreeObserver().removeOnPreDrawListener(this);
tvStatus.setVisibility(View.GONE);
return false;
}
return true;
}
});
}
You need to animate the layout params of your TextView as well, not just the translation. Add this and run it in parallel to your translation animation. Make sure you know the text view height before starting the animation (you may need to use a predraw listener if the textview is invisible):
final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
mTextView.setVisibility(View.VISIBLE);
ValueAnimator anim = ValueAnimator.ofInt(mTextView.getHeight());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
layoutParams.height = val;
mTextView.setLayoutParams(layoutParams);
}
});
anim.setDuration(1000);
anim.start();
Edit:
to measure the text view's height, make it visible in your layout, then add an onPreDrawListener where you inflate your layout:
private void measureTextHeight() {
mTextView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
mTextViewHeight = mTextView.getHeight();
if (mTextViewHeight > 0) {
mTextView.getViewTreeObserver().removeOnPreDrawListener(this);
mTextView.setTranslationY(-mTextViewHeight);
mTextView.setVisibility(View.GONE);
return false;
}
return true;
}
});
}
Now you have the height of the text view, so you show it using the function above. To hide it, you do this:
final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
mTextView.setVisibility(View.VISIBLE);
ValueAnimator anim = ValueAnimator.ofInt(mTextView.getHeight());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
layoutParams.height = mTextViewHeight - val;
mTextView.setLayoutParams(layoutParams);
}
});
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationCancel(Animator animation) {
mTextView.setVisibility(View.GONE);
}
#Override
public void onAnimationEnd(Animator animation) {
mTextView.setVisibility(View.GONE);
}
});
anim.setDuration(1000);
anim.start();
Remember that this needs to run in parallel to your translation animation, so you could use an animator set to run them together.

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.

Android padding left animation in RelativeLayout

I am trying to create a padding left animation in RelativeLayout. I wrote code which can animate left in click listener.
This is my code, but it is not working completely.
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
holder = (UserHolder) view.getTag();
layoutParams = (RelativeLayout.LayoutParams) holder.layoutmain
.getLayoutParams();
if (holder.layout.getVisibility() != View.VISIBLE) {
ValueAnimator varl = ValueAnimator.ofInt(-170);
varl.setDuration(1000);
varl.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
layoutParams.setMargins(
(Integer) animation.getAnimatedValue(), 0,
0, 0);
holder.layoutmain.setLayoutParams(layoutParams);
holder.layout.setVisibility(View.VISIBLE);
}
});
varl.start();
}
else
{
ValueAnimator varl = ValueAnimator.ofInt(0);
varl.setDuration(1000);
varl.addUpdateListener(new AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(
ValueAnimator animation) {
layoutParams.setMargins(0, 0,
(Integer) animation.getAnimatedValue(),
0);
// lp.setMargins(left, top, right, bottom)
holder.layoutmain.setLayoutParams(layoutParams);
holder.layout.setVisibility(View.INVISIBLE);
}
});
varl.start();
}
}
});
The first time, it works (I can animate padding left), but the second time I click the animation is not working. What am I doing wrong?
Through ValueAnimator class you can update margin or padding of any with animation. Please go through the following code :
ValueAnimator animator = ValueAnimator.ofInt(0,50);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator){
frameLayout.setPadding(0, (Integer) valueAnimator.getAnimatedValue(),0,0);
}
});
animator.setInterpolator(new DecelerateInterpolator(2));
animator.start();
Let me know in case of more explanation in comments.
You should try using ValueAnimator.ofInt(fromInt,toInt)
//first
ValueAnimator.ofInt(-70,0)
//second
ValueAnimator.ofInt(0,-70)
Otherwise the value animator doesn't know from or to which value it should animate.

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);

Resources