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..
Related
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.
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
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.
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
I'm looking to implement an animation that will slide open or expand a ListView in android. At the moment, I'm using a scale animation (shown below)
Animation slideDown = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f);
slideDown.setDuration(1000);
but this doesn't work for 2 reasons:
1) I want the first item to fade in, and then the list to drop down. If I do a fade in animation, the entire list shows up and then jumps back up to the top and scales down to full size
2) I'm not really looking to scale, I would like to just reveal the ListView by animating down the bottom (if that makes sense).
Is there a way that I can do a TranslateAnimation on just the bottom margin or something similar? I looked into the drawable Clip resource, but that didn't seem to fit my needs here (I'm not sure how to apply it to a ListView). And I'm using a normal ListView, not an ExpandableListView, because I just have one group of items.
So I started searching for different things and it turns out that animating the height was my solution. Essentially I start with a dummy object in the list (so that it has at least 1 row), then I animate it from the height of that row to the height of the full list. Below is the code for the runnable that I am now calling.
private Runnable slideDownList1 = new Runnable() {
#Override
public void run() {
final ListView detail = (ListView)getView().findViewById(R.id.detail_menu_1);
ValueAnimator va = ValueAnimator.ofInt(detail.getHeight(), detail.getHeight()*mItem.containedObjects.size());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
detail.getLayoutParams().height = value.intValue();
detail.requestLayout();
}
});
va.setDuration(1000);
va.start();
}
};
The XML is just a basic ListView with the id of detail_menu_1.
The new element Recycler View include animations. You can use it with Support Library
https://developer.android.com/training/material/lists-cards.html