Android ExpandableListView using animation - android

I'm using
<ExpandableListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ExpandableListView>
i want add animation slide for child when onclick parent . So How can i do ?

Final Update
It's been quite a while since I wrote this answer. Since then a lot has changed. The biggest change is with the introduction of RecyclerView that makes animating a list or grid easy. I highly recommend switching over to RecyclerViews if you can. For those who can't I will see what I can do regarding fixing the bugs for my library.
Original answer
I actually do not like the popular implementation of an animated ExpandableListView that simply uses a ListView with an expand animation because in my use case, each of my groups had a lot of children, therefore it was not feasible to use a normal ListView as the child views will not be recycled and the memory usage will be huge with poor performance. Instead, I went with a much more difficult but more scalable and flexible approach.
I extended the ExpandableListView class and overrode the onCollapse and onExpand functions, I also created a subclass of a BaseExpandableListAdapter called AnimatedExpandableListAdapter. Inside the adapter, I overrode the getChildView function and made the function final so that the function cannot be overrode again. Instead I provided another function called getRealChildView for subclasses to override to provide a real child view. I then added an animation flag to the class and made getChildView return a dummy view if the animation flag was set and the real view if the flag was not set. Now with the stage set I do the following for onExpand:
Set the animation flag in the adapter and tell the adapter which group is expanding.
Call notifyDataSetChanged() (forces the adapter to call getChildView() for all views on screen).
The adapter (in animation mode) will then create a dummy view for the expanding group that has initial height 0. The adapter will then get the real child views and pass these views to the dummy view.
The dummy view will then start to draw the real child views within it's own onDraw() function.
The adapter will kick off an animation loop that will expand the dummy view until it is of the right size. It will also set an animation listener so that it can clear the animation flag once the animation completes and will call notifyDataSetChanged() as well.
Finally with all of this done, I was able to not only get the desired animation effect but also the desired performance as this method will work with group with over 100 children.
For the collapsing animation, a little more work needs to be done to get this all setup and running. In particular, when you override onCollapse, you do not want to call the parent's function as it will collapse the group immediately leaving you no chance to play an animation. Instead you want to call super.onCollapse at the end of the collapse animation.
UPDATE:
I spent some time this weekend to rewrite my implementation of this AnimatedExpandableListView and I'm releasing the source with an example usage here:
https://github.com/idunnololz/AnimatedExpandableListView/

animateLayoutChanges adds auto-animation
<ExpandableListView
android:animateLayoutChanges="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>

#idunnololz solution works great. however i would like to add some code to collapse previously expanded group.
private int previousGroup=-1;
listView.setOnGroupClickListener(new OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
// We call collapseGroupWithAnimation(int) and
// expandGroupWithAnimation(int) to animate group
// expansion/collapse.
if (listView.isGroupExpanded(groupPosition)) {
listView.collapseGroupWithAnimation(groupPosition);
previousGroup=-1;
} else {
listView.expandGroupWithAnimation(groupPosition);
if(previousGroup!=-1){
listView.collapseGroupWithAnimation(previousGroup);
}
previousGroup=groupPosition;
}
return true;
}
});

#idunnololz solution is working great, but I experienced weird behavior with my custom layout for group. The expand operation was not executed properly, the collapse however worked perfect. I imported his test project and it worked just fine, so I realized the problem is with my custom layout. However when I was not able to locate the problem after some investigation, I decided to uncomment these lines of code in his AnimatedExpandListView:
if (lastGroup && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return expandGroup(groupPos, true);
}
which caused the problem (my app is aimed for Android 4.0+).

Found this snnipet not remebering where here in Stack Overflow. Have two basic static methods: expand(View v) and collapse(View v).
You only have to pass the view you want to hide show.
Note: I Don't recomend pass a view having wrap_content as height. May not work fine.
public class expand {
public static void expand(View view) {
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(view, 0, view.getMeasuredHeight());
mAnimator.start();
}
public static void collapse(final View view) {
int finalHeight = view.getHeight();
ValueAnimator mAnimator = slideAnimator(view, finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
view.setVisibility(View.GONE);
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}
private static ValueAnimator slideAnimator(final View v, 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 = v.getLayoutParams();
layoutParams.height = value;
v.setLayoutParams(layoutParams);
}
});
return animator;
}
}

Related

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

Implement Google+ card liked animation via RecylerView

Previously, I implemented Google+ card liked animation (Video), by using technique mentioned in New Google Now and Google+ card interface
Override LinearLayout's onGlobalLayout to start animation, so that when activity first launched, we can see the slide up animation of cards.
Override ScrollView's onScrollChanged to start animation, so that during scrolling, we can see the newly visible cards being animated.
So far, I don't see any technique from RecylerView's example.
I was wondering, without using LinearLayout and ScrollView, can with achieve the same outcome, by using RecylerView? Is there any code example available? (So far, I unable to find one yet)
Add these lines in RecyclerView 's adaptor. Make a Variable for Each card i.e(convertView) here and initialise it.
int mLastPosition = 0;
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView card_head, card_body;
View convertView;
#SuppressLint("NewApi")
public ViewHolder(View v) {
super(v);
convertView = v;
}
}
Bind you view by position
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
// do your thing here
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
float initialTranslation = (mLastPosition <= position ? 150f
: -150f);
holder.convertView.setTranslationY(initialTranslation);
holder.convertView.animate()
.setInterpolator(new DecelerateInterpolator(1.0f))
.translationY(0f).setDuration(900l).setListener(null);
}
// Keep track of the last position we loaded
mLastPosition = position;
}
here , you have to animate the who view it calls onBindViewHolder method when new card is created animate the whole view customize it.

Can I partially hide a layout?

As I've a master in MS Paint, I will just upload a picture selfdescripting what I'm trying to achieve.
I've searched, but I'm not really sure what do I've to search. I've found something called Animations. I managed to rotate, fade, etc an element from a View (with this great tutorial http://www.vogella.com/articles/AndroidAnimation/article.html)
But this is a bit limited for what I'm trying to achieve, and now, I'm stuck, because I don't know how is this really called in android development. Tried words like "scrollup layouts" but I didn't get any better results.
Can you give me some tips?
Thank you.
You can see a live example, with this app: https://play.google.com/store/apps/details?id=alexcrusher.just6weeks
Sincerely,
Sergi
Use something like this as your layout (Use Linear, Relative or other layout if you wish):
<LinearLayout
android:id="#+id/lty_parent">
<LinearLayout
android:id="#+id/lyt_first" />
<LinearLayout
android:id="#+id/lyt_second"/>
</LinearLayout>
And then in an onClick method on whatever you want to use to control it, set the Visibility between Visible and Gone.
public void buttonClickListener(){
((Button) findViewById(R.id.your_button))
.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (lyt_second.getVisibility() == View.GONE) {
lyt_second.setVisibility(View.VISIBILE);
}
else {
lyt_second.setVisibility(View.GONE);
}
});
Which is fine if you just want a simple appear/disappear with nothing fancy. Things get a little bit more complicated if you want to animate it, as you need to play around with negative margins in order to make it appear to grow and shrink, like so:
We use the same onClick method that we did before, but this time when we click it starts up a custom SlideAnimation for the hidden/visible view.
#Override
public void onClick(View v) {
SlideAnimation slideAnim = new SlideAnimation(lyt_second, time);
lyt_second.startAnimation(slideAnim);
}
The implementation of the SlideAnimation is based on a general Animation class, which we extend and then Override the transformation.
public SlideAnimation(View view, int duration) {
//Set the duration of the animation to the int we passed in
setDuration(duration);
//Set the view to be animated to the view we passed in
viewToBeAnimated = view;
//Get the Margin Parameters for the view so we can edit them
viewMarginParams = (MarginLayoutParams) view.getLayoutParams();
//If the view is VISIBLE, hide it after. If it's GONE, show it before we start.
hideAfter = (view.getVisibility() == View.VISIBLE);
//First off, start the margin at the bottom margin we've already set.
//You need your layout to have a negative margin for this to work correctly.
marginStart = viewMarginParams.bottomMargin;
//Decide if we're expanding or collapsing
if (marginStart == 0){
marginEnd = 0 - view.getHeight();
}
else {
marginEnd = 0;
}
//Make sure the view is visible for our animation
view.setVisibility(View.VISIBLE);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
if (interpolatedTime < 1.0f) {
// Setting the new bottom margin to the start of the margin
// plus the inbetween bits
viewMarginParams.bottomMargin = marginStart
+ (int) ((marginEnd - marginStart) * interpolatedTime);
// Request the layout as it happens so we can see it redrawing
viewToBeAnimated.requestLayout();
// Make sure we have finished before we mess about with the rest of it
} else if (!alreadyFinished) {
viewMarginParams.bottomMargin = marginEnd;
viewToBeAnimated.requestLayout();
if (hideAfter) {
viewToBeAnimated.setVisibility(View.GONE);
}
alreadyFinished = true;
}
hideAfter = false;
}
}
EDIT: If anyone had used this code before and found that if you click on the button that starts the animation more than once before the animation was finished, it would mess up the animation from then on, causing it to always hide the view after the animation finished. I missed the reset of the hideAfter boolean near the bottom of the code, added it now.
you can do this manually by using setvisibility feature on the event onClick()
or
use this
dynamically adding two views one below other

Adding items into listview dynamically [duplicate]

In iOS, there is a very easy and powerful facility to animate the addition and removal of UITableView rows, here's a clip from a youtube video showing the default animation. Note how the surrounding rows collapse onto the deleted row. This animation helps users keep track of what changed in a list and where in the list they were looking at when the data changed.
Since I've been developing on Android I've found no equivalent facility to animate individual rows in a TableView. Calling notifyDataSetChanged() on my Adapter causes the ListView to immediately update its content with new information. I'd like to show a simple animation of a new row pushing in or sliding out when the data changes, but I can't find any documented way to do this. It looks like LayoutAnimationController might hold a key to getting this to work, but when I set a LayoutAnimationController on my ListView (similar to ApiDemo's LayoutAnimation2) and remove elements from my adapter after the list has displayed, the elements disappear immediately instead of getting animated out.
I've also tried things like the following to animate an individual item when it is removed:
#Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
Animation animation = new ScaleAnimation(1, 1, 1, 0);
animation.setDuration(100);
getListView().getChildAt(position).startAnimation(animation);
l.postDelayed(new Runnable() {
public void run() {
mStringList.remove(position);
mAdapter.notifyDataSetChanged();
}
}, 100);
}
However, the rows surrounding the animated row don't move position until they jump to their new positions when notifyDataSetChanged() is called. It appears ListView doesn't update its layout once its elements have been placed.
While writing my own implementation/fork of ListView has crossed my mind, this seems like something that shouldn't be so difficult.
Thanks!
Animation anim = AnimationUtils.loadAnimation(
GoTransitApp.this, android.R.anim.slide_out_right
);
anim.setDuration(500);
listView.getChildAt(index).startAnimation(anim );
new Handler().postDelayed(new Runnable() {
public void run() {
FavouritesManager.getInstance().remove(
FavouritesManager.getInstance().getTripManagerAtIndex(index)
);
populateList();
adapter.notifyDataSetChanged();
}
}, anim.getDuration());
for top-to-down animation use :
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="20%p" android:toYDelta="-20"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
The RecyclerView takes care of adding, removing, and re-ordering animations!
This simple AndroidStudio project features a RecyclerView. take a look at the commits:
commit of the classic Hello World Android app
commit, adding a RecyclerView to the project (content not dynamic)
commit, adding functionality to modify content of RecyclerView at runtime (but no animations)
and finally...commit adding animations to the RecyclerView
Take a look at the Google solution. Here is a deletion method only.
ListViewRemovalAnimation project code and Video demonstration
It needs Android 4.1+ (API 16). But we have 2014 outside.
Since ListViews are highly optimized i think this is not possible to accieve. Have you tried to create your "ListView" by code (ie by inflating your rows from xml and appending them to a LinearLayout) and animate them?
Have you considered animating a sweep to the right? You could do something like drawing a progressively larger white bar across the top of the list item, then removing it from the list. The other cells would still jerk into place, but it'd better than nothing.
call
listView.scheduleLayoutAnimation();
before changing the list
I hacked together another way to do it without having to manipulate list view. Unfortunately, regular Android Animations seem to manipulate the contents of the row, but are ineffectual at actually shrinking the view. So, first consider this handler:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
Bundle bundle = message.getData();
View view = listView.getChildAt(bundle.getInt("viewPosition") -
listView.getFirstVisiblePosition());
int heightToSet;
if(!bundle.containsKey("viewHeight")) {
Rect rect = new Rect();
view.getDrawingRect(rect);
heightToSet = rect.height() - 1;
} else {
heightToSet = bundle.getInt("viewHeight");
}
setViewHeight(view, heightToSet);
if(heightToSet == 1)
return;
Message nextMessage = obtainMessage();
bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1);
nextMessage.setData(bundle);
sendMessage(nextMessage);
}
Add this collection to your List adapter:
private Collection<Integer> disabledViews = new ArrayList<Integer>();
and add
public boolean isEnabled(int position) {
return !disabledViews.contains(position);
}
Next, wherever it is that you want to hide a row, add this:
Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt("viewPosition", listView.getPositionForView(view));
message.setData(bundle);
handler.sendMessage(message);
disabledViews.add(listView.getPositionForView(view));
That's it! You can change the speed of the animation by altering the number of pixels that it shrinks the height at once. Not real sophisticated, but it works!
After inserting new row to ListView, I just scroll the ListView to new position.
ListView.smoothScrollToPosition(position);
I haven't tried it but it looks like animateLayoutChanges should do what you're looking for. I see it in the ImageSwitcher class, I assume it's in the ViewSwitcher class as well?
Since Android is open source, you don't actually need to reimplement ListView's optimizations. You can grab ListView's code and try to find a way to hack in the animation, you can also open a feature request in android bug tracker (and if you decided to implement it, don't forget to contribute a patch).
FYI, the ListView source code is here.
Here's the source code to let you delete rows and reorder them.
A demo APK file is also available. Deleting rows is done more along the lines of Google's Gmail app that reveals a bottom view after swiping a top view. The bottom view can have an Undo button or whatever you want.
As i had explained my approach in my site i shared the link.Anyways the idea is create bitmaps
by getdrawingcache .have two bitmap and animate the lower bitmap to create the moving effect
Please see the following code:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id)
{
listView.setDrawingCacheEnabled(true);
//listView.buildDrawingCache(true);
bitmap = listView.getDrawingCache();
myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom());
myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight());
listView.setDrawingCacheEnabled(false);
imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1));
imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2));
imgView1.setVisibility(View.VISIBLE);
imgView2.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, rowView.getBottom(), 0, 0);
imgView2.setLayoutParams(lp);
TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight());
transanim.setDuration(400);
transanim.setAnimationListener(new Animation.AnimationListener()
{
public void onAnimationStart(Animation animation)
{
}
public void onAnimationRepeat(Animation animation)
{
}
public void onAnimationEnd(Animation animation)
{
imgView1.setVisibility(View.GONE);
imgView2.setVisibility(View.GONE);
}
});
array.remove(positon);
adapter.notifyDataSetChanged();
imgView2.startAnimation(transanim);
}
});
For understanding with images see this
Thanks.
I have done something similar to this. One approach is to interpolate over the animation time the height of the view over time inside the rows onMeasure while issuing requestLayout() for the listView. Yes it may be be better to do inside the listView code directly but it was a quick solution (that looked good!)
Just sharing another approach:
First set the list view's android:animateLayoutChanges to true:
<ListView
android:id="#+id/items_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"/>
Then I use a handler to add items and update the listview with delay:
Handler mHandler = new Handler();
//delay in milliseconds
private int mInitialDelay = 1000;
private final int DELAY_OFFSET = 1000;
public void addItem(final Integer item) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
new Thread(new Runnable() {
#Override
public void run() {
mDataSet.add(item);
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
}
}).start();
}
}, mInitialDelay);
mInitialDelay += DELAY_OFFSET;
}

Android ScrollView, scroll before visible

how can I set scroll of scrollview to x pixels, before it's even shown?
In ScrollView I have some Views and i know that it will fill screen. Can I scroll to let say second View, so that first View is not visible when activity is started?
Now I have sth like this, but I think it's not perfect, sometimes, I see first View, and then it's scrolled to the second one
#Override
public void onGlobalLayout() {
if (mHorizontalScrollView.getChildCount() > 0 && mHorizontalScrollView.getChildAt(0).getWidth() > mScreenWidth) {
hsv.scrollTo(100, 0);
}
}
EDIT!!
Second attempt was to use http://developer.android.com/reference/android/view/ViewTreeObserver.OnPreDrawListener.html instead of http://developer.android.com/reference/android/view/ViewTreeObserver.OnGlobalLayoutListener.html
In OnPreDrawListener we can read that At this point, all views in the tree have been measured and given a frame. Clients can use this to adjust their scroll bounds or even to request a new layout before drawing occurs. so basically at this point we should adjust scrolling. So I created:
#Override
public boolean onPreDraw() {
if (hsv.getChildCount() > 0 && hsv.getChildAt(0).getWidth() > mScreenWidth) {
hsv.scrollTo(100, 0);
return false;
}
return true;
}
but now it's never working for me.
I combined onGlobalLayout with code below. It's not so great to use that hack but it still quite efficient. When I get onGlobalLayout event I check if layouts are done and then if it's ok I use method below.
// hsv - horizontal scroll view
private void forceShowColumn(final int column) {
int scrollTo = childViewWidth * column;
if (scrollTo == hsv.getScrollX()) {
return;
}
hsv.scrollTo(scrollTo, 0);
hsv.postDelayed(new Runnable() {
#Override
public void run() {
forceShowColumn(column);
}
}, 10);
}
try View.post(Runnable) in onCreate-method.
I solved this issue by implementing View.OnLayoutChangeListener in the fragment that controls my ScrollView.
public class YourFragment extends Fragment implements View.OnLayoutChangeListener{
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.your_fragment_layout, container, false);
//set scrollview layout change listener to be handled in this fragment
sv = (ScrollView) view.findViewById(R.id.your_scroll_view);
sv.addOnLayoutChangeListener(this);
return view;
}
...
public void onLayoutChange (View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom){
switch (v.getId())
{
case R.id.your_scroll_view:
{
sv.scrollTo(0, sv.getTop());
}
}
}
The correct way to do this, is to call scrollTo() on the ScrollView after the view has been through its measurement and layout passes, otherwise the width of the content in the scrollView will be 0, and thus scrollTo() will not scroll to the right position. Usually after the constructor has been called and after the view has been added to its parent.
See http://developer.android.com/guide/topics/ui/how-android-draws.html for more insight on how android draws views.
MrJre gives the most elegant solution: "override onLayout() in the scrollview and do it there after super.onLayout()"
Example code here:
https://stackoverflow.com/a/10209457/1310343
Use :
scrollTo (int x, int y)
It sets the scrolled position of your view. This will cause a call to onScrollChanged
For more details : see this
ScrollView ScrollTo

Categories

Resources