RecyclerView scrollToPositionWithOffset with animation - android

Im trying to animate a card view's position and resize it to fit screen at the same time.
When the user clicks a button inside the cardview, the cardview should expand to the size of its container, and scroll up to become fully visible at the same time
in my custom animator class, im using the following function:
#Override
protected void applyTransformation(float interpolatedTimeTransformation t) {
int newHeight = (int) (startHeight + (targetHeight - startHeight) *interpolatedTime);
view.getLayoutParams().height = newHeight;
((LinearLayoutManager) MainPage.mainGroupRecycler.getLayoutManager()).scrollToPositionWithOffset(MainPage.mainGroupRecycler.getChildAdapterPosition((CardView) view.getParent()), 0);
view.requestLayout();
}
in the following case, the recyclerview instantly shows the cardview at the top of the visible area without animating the scrolling, then it animates the resize. I need it to scroll at the same time the resize is happening.
i tried calling another scroll function:
MainPage.mainGroupRecycler.scrollToPosition(MainPage.mainGroupRecycler.getChildAdapterPosition((CardView) view.getParent()));
But the problem with this is that the scrolling will only animate after the resize is complete.
I need the resizing and the scrolling to happen simultaneously.
Any help will be appreciated

For the two effects to occur in tandem, you must start the animation of the view, and call scrollToPosition at the same time.
Also, remove the call to "scrollToPosition" from within the animation's applyTransformation method. This is bad because it's telling the layout manager that it needs to scroll to a certain position on every iteration of the animation, which could occur hundreds of times. You only need to call scrollToPosition once.
Also, instead of scrollToPosition, use smoothScrollToPosition for a nice smooth scroll effect.

I figured out the solution.
In my animator class:
#Override
protected void applyTransformation(float interpolatedTime, Transformation t){
calculatedOffset = (int) (tempOffset*interpolatedTime);
tempOffset = tempOffset-calculatedOffset;
recyclerView.scrollBy(0,calculatedOffset);
int newHeight = (int) (startHeight + (targetHeight - startHeight) * interpolatedTime);
LinearLayout ll = (LinearLayout)view.findViewById(R.id.cardchild);
ll.setLayoutParams(new CardView.LayoutParams(ll.getWidth(),newHeight));
}
A note to anyone using a similar approach, make sure to set recyclerView.setItemViewCacheSize(itemList.size) if u have a large number of items because if you dont, the cached objects will be reused thus resizing multiple items and not just the item u clicked

Related

Animate a view towards left

I'm using the following code to expand a view with an animation:
public class HorizontallyAnimate extends Animation {
private int toWidth;
private int startWidth;
private View view;
private String TAG = HorizontallyAnimate.class.getSimpleName();
private int newWidth;
public HorizontallyAnimate(View view) {
this.view = view;
this.startWidth = this.view.getWidth();
this.toWidth = (this.startWidth == view.getHeight() ? this.startWidth * 4 : view.getHeight());
Log.d(TAG,"Start width" + this.startWidth);
Log.d(TAG,"view hieght " + view.getHeight());
}
protected void applyTransformation(float interpolatedTime, Transformation t) {
newWidth = this.startWidth + (int) ((this.toWidth - this.startWidth) * interpolatedTime);
this.view.getLayoutParams().width = newWidth;
this.view.requestLayout();
}
}
The above code animates the view from left to right when the width changes.
But, I'm trying to animate it from the right to left. In other words, the width should grow in opposite direction. How can I be able to do so?
The problem you're dealing with here is an anchoring issue. A view's anchor (or pivot point) determines which point on the view stays still when other parts change.
A view's anchor when adjusting its dimensions highly depends on how the view is laid out. Since you didn't supply any information on how your view is laid out in the code you posted, I'll assume from the problem you're experiencing that the view's horizontal anchor is its left side.
This anchoring issue would produce a growth which would cause the left-most side to stay still, while the right side expands right-wards.
Making the view's left side to expand leftwards while the right side stays still can be achieved in several ways. One way would be to alter the way the view is laid out in its parent (i.e., if the parent is a RelativeLayout, set the view to alignParentRight=true, or playing with gravity in other containers).
However, since you didn't specify how the view is laid out, I will give you a solution which does not make any assumptions on its container. This solution isn't perfect as it may cause some stuttering, but it should still achieve what you're trying to do.
In your applyTransformation method, you will need to compensate for the right growth by translating leftwards. You can compensate for this by using translationX:
protected void applyTransformation(float interpolatedTime, Transformation t) {
// hold the change in a separate variable for reuse.
int delta = (int) ((this.toWidth - this.startWidth) * interpolatedTime);
newWidth = this.startWidth + delta;
this.view.getLayoutParams().width = newWidth;
// shift the view leftwards so that the right side appears not to move.
// shift amount should be equal to the amount the view expanded, but in the
// opposite direction.
this.view.setTranslationX(-delta);
this.view.requestLayout();
}
As you can see, this is a bit of "trick". While the view is expanding to the right, we are moving it to the left at the exact same ratio which causes the illusion of the view expanding to the left.
Test this code and see if it works for you. I would also recommend seeing if you can play around with the view's alignment or gravity from within its container. Doing this would solve your issue in a more standard manner, i.e., without any "tricks".
Hope this helps.

How to smoothen Linear layout height animation, having heavy content

Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ViewGroup.LayoutParams params = slidingPane.getLayoutParams();
int calculatedHeight = expandedHeight - ((int) (expandedHeight * interpolatedTime));
if (calculatedHeight <= collapsedHeight) {
calculatedHeight = collapsedHeight;
}
params.height = calculatedHeight;
slidingPane.setLayoutParams(params);
slidingPane.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
animation.setDuration(ANIMATION_DURATION);
animation.setAnimationListener(animationListener);
slidingPane.startAnimation(animation);
SlidingPane is a LinearLayout and it has a ListView as a child. ListView contains images in every row.
Now this code is working absolutely fine, but animation is not smooth. If I remove images from listview then it works fine.
I have already tried following things based on the answers on SO on similar questions -
1. Hide the content of sliding pane before animation and then again make them visible on animation end. It helps but behavior looks very odd
2. Set android:animateLayoutChanges="true" in xml, but its not animating anything
Is there anyway to solve this problem?
Instead of manually changing the height on each animation step, you should try to use the more systematic animation, like ValueAnimator, or ObjectAnimator:
slidingPane.animate().scaleY(0f).setInterpolator(new AccelerateDecelerateInterpolator()).setDuration(ANIMATION_DURATION);
You can use pivotY/pivotY on the view to control the anchor point of the scale animation.
Related docs:
https://developer.android.com/reference/android/animation/ValueAnimator.html
https://developer.android.com/reference/android/animation/ObjectAnimator.html
https://developer.android.com/guide/topics/graphics/prop-animation.html

Android View with a translate animation - positioning is not working as expected

I have a View that has an OnClickListener. When clicked, the view translates up to a certain position on the page. This is no problem, the view goes where it should. When the view is clicked again, I would like to position it somewhere else, but this is not the case. After a little bit of trouble shooting, I found that my View's getTop() method returns the same value - even after the translation animation has moved the view to a different part of the screen. For the second animation, it is not using the current position (as I would expect), it instead uses the initial position.
Few things that I am doing: I am using the ObjectAnimation class rather than the TranslationAnimation class, since I wanted to keep the OnClickListener functioning. With the TranslationAnimation class, I found that the view was correctly moved, but the OnClickListener was only working in the area that the View started from. Using the ObjectAnimation class, I was able to easily get the translation to work AND the OnClickListener functions correctly - it is triggered where the view currently is on the screen.
Here's what I have so far:
final LinearLayout child = layouts.get(i); //ArrayList containing some dynamic layouts
final int offset = target - child.getTop();
ObjectAnimator anim = ObjectAnimator.ofFloat(child,"translationY",offset);
anim.setDuration(250);
anim.start();
This is what happens when the view is clicked the first time. It translates up along the Y axis, where the offset determines how far the View needs to move from its current position.
Now, here's what happens on the second click. The goal here was to align the view with the parent's base.
target = parent.getBottom();
offset = target - child.getTop();
anim = ObjectAnimator.ofFloat(child, "translationY",offset);
anim.setDuration(250);
anim.start();
prev = child;
This is where things fall apart - child.getTop() returns the Y coordinate of the view's ORIGINAL position. Not the current position. So after the animation, the view is placed well below the bottom of the parent. I read a different question which stated that I should use child.getY() instead, which is supposed to give me the translationY position plus the top position, but this didn't lead to any better results. I can't seem to get this to work just right. I'd simply like to move the view from its current position to the bottom of the screen, but this appears to be a hard thing to accomplish. Any help would be appreciated.
EDIT
I have added an animation listener:
ObjectAnimator anim = ObjectAnimator.ofFloat(child,"translationY",offset);
anim.setDuration(250);
anim.addListener(new ObjectAnimator.AnimatorListener(){
#Override
public void onAnimationStart(Animator animation) {
System.out.println("start: " + child.getTop() + " " + child.getY());
}
#Override
public void onAnimationEnd(Animator animation) {
System.out.println("end: " + child.getTop() + " " + child.getY() + " " + child.getTranslationY());
child.setTop((int)child.getY());
System.out.println(child.getTop());
}
#Override
public void onAnimationCancel(Animator animation) {}
#Override
public void onAnimationRepeat(Animator animation) {}
});
anim.start();
Here I am setting the listener to try to change where the Top of the view is located. Behaviour is again not working as expected. The view is actually sent up above the screen when I do this. Output of the System.out looks like this:
start: 2008 2008.0
end: 2008 478.0 -1530.0
478
So calling child.getTop() after the animation is complete and setting a new position returns a positive integer, but the view is not actually completely on screen. It is above the screen, partly visible. The height of the view itself is about 700px. I am still so confused as to why this is such a hard thing to accomplish.
EDIT 2
I have also tried setting layoutparams inside the onAnimationEnd method:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)child.getLayoutParams();
params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
params.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
params.topMargin = (int)child.getY();
child.setLayoutParams(params);
Result: child.getTop() still returns the original position of 2008.
You can get the very bottom of the screen coordinates like this :
float bottomOfScreen = getResources().getDisplayMetrics().heightPixels;
but you probably want it minus the height of your LinearLayout or else your LinearLayout will be cut off by the bottom :
float bottomOfScreen = getResources().getDisplayMetrics().heightPixels
- child.getHeight();
// if you want a little more space to the bottom
// try something like - child.getHeight()*2;
Then use ViewPropertyAnimator to animate your LL like this :
child.animate()
.translationY(bottomOfScreen)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(250);
The Interpolator is just to make the animation more realistic.
In the case that child.getHeight() returns 0 , your Linear Layout has not been finished setting up by the system, in that case you might want to do something like :
child.post(new Runnable() {
#Override
public void run() {
float bottomOfScreen = getResources().getDisplayMetrics().heightPixels
- child.getHeight()*2;
child.animate()
.translationY(bottomOfScreen)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(250);
}
});
Remember that a duration of 250 milliseconds is very fast, and does usually not look cool translating stuff on the screen, so you might want to set it a little higher, but thats just a matter of taste.

Android Width Animation Conflicts in ValueAnimator

I am trying to expand and shrink the width of container with animation.
I am doing both of those with ValueAnimator and both of those are working fine separately.
The expand animation will be perform when the user scroll up the cards and shrink animation will be perform when the user scroll down the cards.
Because all of those cards are in ScrollView container, the user can scroll up / down whenever they want even though the expanding / shrinking Animation is not finished.
To be more clear, it looks like the shrink animation is being triggered when the cards are still expanding. Below is my code for expanding / shrinking.
private void animateResizeWidth(final View vcLogin, int widthReduce){
ValueAnimator anim;
if(widthReduce == 0){
anim = ValueAnimator.ofInt((ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH) - ScreenUtils.getObjInstance().getPixelFromDPI(widthReduce)), ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH));
}else{
anim = ValueAnimator.ofInt(ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH), (ScreenUtils.getObjInstance().getPixelFromDPI(CARD_WIDTH) - ScreenUtils.getObjInstance().getPixelFromDPI(widthReduce)));
}
final RelativeLayout.LayoutParams finalLp2 = new RelativeLayout.LayoutParams(vcLogin.getLayoutParams());
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int val = (Integer) valueAnimator.getAnimatedValue();
finalLp2.width = val;
finalLp2.addRule(RelativeLayout.CENTER_HORIZONTAL);
vcLogin.setLayoutParams(finalLp2);
vcLogin.invalidate();
}
});
anim.setDuration(400);
anim.start();
}
So, whenever that happen, the size of the cards are being set to regular size and the resize animation won't trigger anymore (Need to restart the Host Activity).
I have tried the answer from here. But, it is not helping me.
My Questions are 1: Is there any other Animation Mechanism that
I can use for width animation except ValueAnimator ? I have checked
ObjectAnimator but, not supporting. Ref 2: Is that thread
issue ? 3: I should not be blocking user from scrolling down
just because my expanding animation is not finished. So, what I haven
in mind is if the user scroll down, I will just terminate any of my
pending animations. Is there any thread-related issue that I need to consider for this approach ? (I have written a Custom
ScrollView to detect those scroll-related events.)
I think I am missing a few thread-safe logics in above animation implementation.
Thanks

ListView Cell Expand (Slide Down) animation [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I make a cell in a ListView in Android expand and contract vertically when it’s touched?
I would like to see a good example of a cell view expanding and pushing list items below down like on twitter application. What I want is user clicks on button in list view item and then the item_view expands gracefully to reveal a EditText and button.
Thanks
You need to do something like this..
public class ExpandAnimation extends Animation {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
// End animation sometimes calls with exactly 1.0f
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();
}
}
The code above changes the view's margins in every animation frame. That makes it appear like the your new view is pushing the rest of the items downwards. You can also try changing the subitem's height instead of its margins..

Categories

Resources