Java tutorial
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package graphic.expand_graphic; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Canvas; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; import android.view.ViewTreeObserver; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ListView; import android.widget.AbsListView.LayoutParams; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import com.example.onedayspb.R; /** * A custom listview which supports the preview of extra content corresponding to each cell * by clicking on the cell to hide and show the extra content. */ public class ExpandingListView extends ListView { private static boolean mShouldRemoveObserver = false; //for less getter private static List<View> mViewsToDraw = new ArrayList<View>(); private static int[] mTranslate = new int[] { 0, 0 }; //height of collapsed list element private final static int mCollapsedHeight = 68; private static HashMap<View, int[]> oldCoordinates; private static LayoutParams collapseLayoutParams; private static LayoutParams generalLayoutParams; private ViewTreeObserver.OnPreDrawListener expandPreDrawListener; private ViewTreeObserver.OnPreDrawListener collapsePreDrawListener; private static ViewHolder holder; private ViewTreeObserver observer; //variables for listeners; private static View mView; private static ExpandableListItem viewObject; private static int oldBottom; private static int oldTop; private static AnimatorSet s = new AnimatorSet(); private static ArrayList<Animator> animations = new ArrayList<Animator>(); public ExpandingListView(Context context) { super(context); init(); } public ExpandingListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ExpandingListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { setOnItemClickListener(mItemClickListener); oldCoordinates = new HashMap<View, int[]>(); collapseLayoutParams = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, mCollapsedHeight); generalLayoutParams = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT); expandPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { /* Determine if this is the first or second pass.*/ if (!mShouldRemoveObserver) { mShouldRemoveObserver = true; /* Calculate what the parameters should be for setSelectionFromTop. * The ListView must be offset in a way, such that after the animation * takes place, all the cells that remain visible are rendered completely * by the ListView.*/ int newTop = mView.getTop(); int newBottom = mView.getBottom(); int newHeight = newBottom - newTop; int oldHeight = oldBottom - oldTop; int delta = newHeight - oldHeight; getTopAndBottomTranslations(oldTop, oldBottom, delta, true); int currentTop = mView.getTop(); int futureTop = oldTop - mTranslate[0]; int firstChildStartTop = getChildAt(0).getTop(); int firstVisiblePosition = getFirstVisiblePosition(); int deltaTop = currentTop - futureTop; int i; int childCount = getChildCount(); for (i = 0; i < childCount; i++) { View v = getChildAt(i); int height = v.getBottom() - Math.max(0, v.getTop()); if (deltaTop - height > 0) { firstVisiblePosition++; deltaTop -= height; } else { break; } } if (i > 0) { firstChildStartTop = 0; } setSelectionFromTop(firstVisiblePosition, firstChildStartTop - deltaTop); /* Request another layout to update the layout parameters of the cells.*/ requestLayout(); /* Return false such that the ListView does not redraw its contents on * this layout but only updates all the parameters associated with its * children.*/ return false; } /* Remove the predraw listener so this method does not keep getting called. */ mShouldRemoveObserver = false; observer.removeOnPreDrawListener(this); int yTranslateTop = mTranslate[0]; int yTranslateBottom = mTranslate[1]; // ArrayList <Animator> animations = new ArrayList<Animator>(); optimize int index = indexOfChild(mView); /* Loop through all the views that were on the screen before the cell was * expanded. Some cells will still be children of the ListView while * others will not. The cells that remain children of the ListView * simply have their bounds animated appropriately. The cells that are no * longer children of the ListView also have their bounds animated, but * must also be added to a list of views which will be drawn in dispatchDraw.*/ for (View v : oldCoordinates.keySet()) { int[] old = oldCoordinates.get(v); v.setTop(old[0]); v.setBottom(old[1]); if (v.getParent() == null) { mViewsToDraw.add(v); int delta = old[0] < oldTop ? -yTranslateTop : yTranslateBottom; animations.add(getAnimation(v, delta, delta)); } else { int i = indexOfChild(v); if (v != mView) { int delta = i > index ? yTranslateBottom : -yTranslateTop; animations.add(getAnimation(v, delta, delta)); } ViewCompat.setHasTransientState(v, false); } } /* Adds animation for expanding the cell that was clicked. */ animations.add(getAnimation(mView, -yTranslateTop, yTranslateBottom)); /* Adds an animation for fading in the extra content. */ animations.add(ObjectAnimator.ofFloat(holder.expandingView, //mView.findViewById(R.id.expanding_layout), optimize View.ALPHA, 0, 1)); /* Disabled the ListView for the duration of the animation.*/ setEnabled(false); setClickable(false); /* Play all the animations created above together at the same time. */ // AnimatorSet s = new AnimatorSet(); optimize s.playTogether(animations); s.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // viewObject.setExpanded(true); optimize viewObject.mIsExpanded = true; setEnabled(true); setClickable(true); s.removeAllListeners(); oldCoordinates.clear(); animations.clear(); if (mViewsToDraw.size() > 0) { for (View v : mViewsToDraw) { ViewCompat.setHasTransientState(v, false); } } mViewsToDraw.clear(); } }); s.start(); return true; } }; collapsePreDrawListener = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (!mShouldRemoveObserver) { /*Same as for expandingView, the parameters for setSelectionFromTop must * be determined such that the necessary cells of the ListView are rendered * and added to it.*/ mShouldRemoveObserver = true; int newTop = mView.getTop(); int newBottom = mView.getBottom(); int newHeight = newBottom - newTop; int oldHeight = oldBottom - oldTop; int deltaHeight = oldHeight - newHeight; getTopAndBottomTranslations(oldTop, oldBottom, deltaHeight, false); int currentTop = mView.getTop(); int futureTop = oldTop + mTranslate[0]; int firstChildStartTop = getChildAt(0).getTop(); int firstVisiblePosition = getFirstVisiblePosition(); int deltaTop = currentTop - futureTop; int i; int childCount = getChildCount(); for (i = 0; i < childCount; i++) { View v = getChildAt(i); int height = v.getBottom() - Math.max(0, v.getTop()); if (deltaTop - height > 0) { firstVisiblePosition++; deltaTop -= height; } else { break; } } if (i > 0) { firstChildStartTop = 0; } setSelectionFromTop(firstVisiblePosition, firstChildStartTop - deltaTop); requestLayout(); return false; } mShouldRemoveObserver = false; observer.removeOnPreDrawListener(this); int yTranslateTop = mTranslate[0]; int yTranslateBottom = mTranslate[1]; int index = indexOfChild(mView); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View v = getChildAt(i); int[] old = oldCoordinates.get(v); if (old != null) { /* If the cell was present in the ListView before the collapse and * after the collapse then the bounds are reset to their old values.*/ v.setTop(old[0]); v.setBottom(old[1]); ViewCompat.setHasTransientState(v, false); } else { /* If the cell is present in the ListView after the collapse but * not before the collapse then the bounds are calculated using * the bottom and top translation of the collapsing cell.*/ int delta = i > index ? yTranslateBottom : -yTranslateTop; v.setTop(v.getTop() + delta); v.setBottom(v.getBottom() + delta); } } final View expandingLayout = holder.expandingView;// mView.findViewById (R.id.expanding_layout); optimize /* Animates all the cells present on the screen after the collapse. */ // ArrayList <Animator> animations = new ArrayList<Animator>(); optimize for (int i = 0; i < childCount; i++) { View v = getChildAt(i); if (v != mView) { float diff = i > index ? -yTranslateBottom : yTranslateTop; animations.add(getAnimation(v, diff, diff)); } } /* Adds animation for collapsing the cell that was clicked. */ animations.add(getAnimation(mView, yTranslateTop, -yTranslateBottom)); /* Adds an animation for fading out the extra content. */ animations.add(ObjectAnimator.ofFloat(expandingLayout, View.ALPHA, 1, 0)); /* Disabled the ListView for the duration of the animation.*/ setEnabled(false); setClickable(false); /* Play all the animations created above together at the same time. */ // AnimatorSet s = new AnimatorSet(); s.playTogether(animations); s.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { expandingLayout.setVisibility(View.GONE); mView.setLayoutParams(generalLayoutParams); viewObject.mIsExpanded = false; // viewObject.setExpanded(false); s.removeAllListeners(); animations.clear(); oldCoordinates.clear(); setEnabled(true); setClickable(true); /* Note that alpha must be set back to 1 in case this view is reused * by a cell that was expanded, but not yet collapsed, so its state * should persist in an expanded state with the extra content visible.*/ expandingLayout.setAlpha(1); } }); s.start(); return true; } }; } /** * Listens for item clicks and expands or collapses the selected view depending on * its current state. */ private AdapterView.OnItemClickListener mItemClickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // viewObject = (ExpandableListItem) getAdapter().getItem(position); // getItemAtPosition(position); //getPositionForView(view) optimize mView = view; holder = (ViewHolder) view.getTag(); viewObject = holder.viewObject; if (!viewObject.mIsExpanded) { expandView(mView); } else { collapseView(mView); } } }; /** * Calculates the top and bottom bound changes of the selected item. These * values are also used to move the bounds of the items around the one that * is actually being expanded or collapsed. * <p/> * This method can be modified to achieve different user experiences * depending on how you want the cells to expand or collapse. In this * specific demo, the cells always try to expand downwards (leaving top * bound untouched), and similarly, collapse upwards (leaving top bound * untouched). If the change in bounds results in the complete disappearance * of a cell, its lower bound is moved is moved to the top of the screen so * as not to hide any additional content that the user has not interacted * with yet. Furthermore, if the collapsed cell is partially off screen when * it is first clicked, it is translated such that its full contents are * visible. Lastly, this behaviour varies slightly near the bottom of the * listview in order to account for the fact that the bottom bounds of the * actual listview cannot be modified. */ private int[] getTopAndBottomTranslations(int top, int bottom, int yDelta, boolean isExpanding) { int yTranslateTop = 0; int yTranslateBottom = yDelta; int height = bottom - top; if (isExpanding) { boolean isOverTop = top < 0; boolean isBelowBottom = (top + height + yDelta) > getHeight(); if (isOverTop) { yTranslateTop = top; yTranslateBottom = yDelta - yTranslateTop; } else if (isBelowBottom) { int deltaBelow = top + height + yDelta - getHeight(); yTranslateTop = top - deltaBelow < 0 ? top : deltaBelow; yTranslateBottom = yDelta - yTranslateTop; } } else { int offset = computeVerticalScrollOffset(); int range = computeVerticalScrollRange(); int extent = computeVerticalScrollExtent(); int leftoverExtent = range - offset - extent; // Hack to fix negative leftover caused by items not filling view if (leftoverExtent < 0) { leftoverExtent *= -1; } boolean isCollapsingBelowBottom = (yTranslateBottom > leftoverExtent); boolean isCellCompletelyDisappearing = bottom - yTranslateBottom < 0; // Hack to force the view to shrink correctly isCollapsingBelowBottom = (yTranslateTop + yTranslateBottom > range) ? false : isCollapsingBelowBottom; if (isCollapsingBelowBottom) { yTranslateTop = yTranslateBottom - leftoverExtent; // Hack to force the view not shift the top bound when it is // visible yTranslateTop = yTranslateTop < range ? 0 : yTranslateTop; yTranslateBottom = yDelta - yTranslateTop; } else if (isCellCompletelyDisappearing) { yTranslateBottom = bottom; yTranslateTop = yDelta - yTranslateBottom; } } return new int[] { yTranslateTop, yTranslateBottom }; } /** * This method expands the view that was clicked and animates all the views * around it to make room for the expanding view. There are several steps * required to do this which are outlined below. * <p/> * 1. Store the current top and bottom bounds of each visible item in the * listview. 2. Update the layout parameters of the selected view. In the * context of this method, the view should be originally collapsed and set * to some custom height. The layout parameters are updated so as to wrap * the content of the additional text that is to be displayed. * <p/> * After invoking a layout to take place, the listview will order all the * items such that there is space for each view. This layout will be * independent of what the bounds of the items were prior to the layout so * two pre-draw passes will be made. This is necessary because after the * layout takes place, some views that were visible before the layout may * now be off bounds but a reference to these views is required so the * animation completes as intended. * <p/> * 3. The first predraw pass will set the bounds of all the visible items to * their original location before the layout took place and then force * another layout. Since the bounds of the cells cannot be set directly, the * method setSelectionFromTop can be used to achieve a very similar effect. * 4. The expanding view's bounds are animated to what the final values * should be from the original bounds. 5. The bounds above the expanding * view are animated upwards while the bounds below the expanding view are * animated downwards. 6. The extra text is faded in as its contents become * visible throughout the animation process. * <p/> * It is important to note that the listview is disabled during the * animation because the scrolling behaviour is unpredictable if the bounds * of the items within the listview are not constant during the scroll. */ private void expandView(final View view) { /* Store the original top and bottom bounds of all the cells. */ final int oldTop = view.getTop(); final int oldBottom = view.getBottom(); final HashMap<View, int[]> oldCoordinates = new HashMap<View, int[]>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View v = getChildAt(i); ViewCompat.setHasTransientState(v, true); oldCoordinates.put(v, new int[] { v.getTop(), v.getBottom() }); } /* Update the layout so the extra content becomes visible. */ final View expandingLayout = view.findViewById(R.id.expanding_layout); expandingLayout.setVisibility(View.VISIBLE); /* * Add an onPreDraw Listener to the listview. onPreDraw will get invoked * after onLayout and onMeasure have run but before anything has been * drawn. This means that the final post layout properties for all the * items have already been determined, but still have not been rendered * onto the screen. */ final ViewTreeObserver observer = getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { /* Determine if this is the first or second pass. */ if (!mShouldRemoveObserver) { mShouldRemoveObserver = true; /* * Calculate what the parameters should be for * setSelectionFromTop. The ListView must be offset in a * way, such that after the animation takes place, all the * cells that remain visible are rendered completely by the * ListView. */ int newTop = view.getTop(); int newBottom = view.getBottom(); int newHeight = newBottom - newTop; int oldHeight = oldBottom - oldTop; int delta = newHeight - oldHeight; mTranslate = getTopAndBottomTranslations(oldTop, oldBottom, delta, true); int currentTop = view.getTop(); int futureTop = oldTop - mTranslate[0]; int firstChildStartTop = getChildAt(0).getTop(); int firstVisiblePosition = getFirstVisiblePosition(); int deltaTop = currentTop - futureTop; int i; int childCount = getChildCount(); for (i = 0; i < childCount; i++) { View v = getChildAt(i); int height = v.getBottom() - Math.max(0, v.getTop()); if (deltaTop - height > 0) { firstVisiblePosition++; deltaTop -= height; } else { break; } } if (i > 0) { firstChildStartTop = 0; } setSelectionFromTop(firstVisiblePosition, firstChildStartTop - deltaTop); /* * Request another layout to update the layout parameters of * the cells. */ requestLayout(); /* * Return false such that the ListView does not redraw its * contents on this layout but only updates all the * parameters associated with its children. */ return false; } /* * Remove the predraw listener so this method does not keep * getting called. */ mShouldRemoveObserver = false; observer.removeOnPreDrawListener(this); int yTranslateTop = mTranslate[0]; int yTranslateBottom = mTranslate[1]; ArrayList<Animator> animations = new ArrayList<Animator>(); int index = indexOfChild(view); /* * Loop through all the views that were on the screen before the * cell was expanded. Some cells will still be children of the * ListView while others will not. The cells that remain * children of the ListView simply have their bounds animated * appropriately. The cells that are no longer children of the * ListView also have their bounds animated, but must also be * added to a list of views which will be drawn in dispatchDraw. */ for (View v : oldCoordinates.keySet()) { int[] old = oldCoordinates.get(v); v.setTop(old[0]); v.setBottom(old[1]); if (v.getParent() == null) { mViewsToDraw.add(v); int delta = old[0] < oldTop ? -yTranslateTop : yTranslateBottom; animations.add(getAnimation(v, delta, delta)); } else { int i = indexOfChild(v); if (v != view) { int delta = i > index ? yTranslateBottom : -yTranslateTop; animations.add(getAnimation(v, delta, delta)); } ViewCompat.setHasTransientState(v, false); } } /* Adds animation for expanding the cell that was clicked. */ animations.add(getAnimation(view, -yTranslateTop, yTranslateBottom)); /* Adds an animation for fading in the extra content. */ animations.add(ObjectAnimator.ofFloat(view.findViewById(R.id.expanding_layout), View.ALPHA, 0, 1)); /* Disabled the ListView for the duration of the animation. */ setEnabled(false); setClickable(false); /* * Play all the animations created above together at the same * time. */ AnimatorSet s = new AnimatorSet(); s.playTogether(animations); s.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { viewObject.setExpanded(true); setEnabled(true); setClickable(true); if (mViewsToDraw.size() > 0) { for (View v : mViewsToDraw) { ViewCompat.setHasTransientState(v, false); } } mViewsToDraw.clear(); } }); s.start(); return true; } }); } /** * By overriding dispatchDraw, we can draw the cells that disappear during * the expansion process. When the cell expands, some items below or above * the expanding cell may be moved off screen and are thus no longer * children of the ListView's layout. By storing a reference to these views * prior to the layout, and guaranteeing that these cells do not get * recycled, the cells can be drawn directly onto the canvas during the * animation process. After the animation completes, the references to the * extra views can then be discarded. */ @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mViewsToDraw.size() == 0) { return; } for (View v : mViewsToDraw) { canvas.translate(0, v.getTop()); v.draw(canvas); canvas.translate(0, -v.getTop()); } } /** * This method collapses the view that was clicked and animates all the * views around it to close around the collapsing view. There are several * steps required to do this which are outlined below. * <p/> * 1. Update the layout parameters of the view clicked so as to minimize its * height to the original collapsed (default) state. 2. After invoking a * layout, the listview will shift all the cells so as to display them most * efficiently. Therefore, during the first predraw pass, the listview must * be offset by some amount such that given the custom bound change upon * collapse, all the cells that need to be on the screen after the layout * are rendered by the listview. 3. On the second predraw pass, all the * items are first returned to their original location (before the first * layout). 4. The collapsing view's bounds are animated to what the final * values should be. 5. The bounds above the collapsing view are animated * downwards while the bounds below the collapsing view are animated * upwards. 6. The extra text is faded out as its contents become visible * throughout the animation process. */ private void collapseView(final View view) { /* Store the original top and bottom bounds of all the cells. */ final int oldTop = view.getTop(); final int oldBottom = view.getBottom(); final HashMap<View, int[]> oldCoordinates = new HashMap<View, int[]>(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View v = getChildAt(i); ViewCompat.setHasTransientState(v, true); oldCoordinates.put(v, new int[] { v.getTop(), v.getBottom() }); } /* Update the layout so the extra content becomes invisible. */ view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, viewObject.mCollapsedHeight)); /* Add an onPreDraw listener. */ final ViewTreeObserver observer = getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (!mShouldRemoveObserver) { /* * Same as for expandingView, the parameters for * setSelectionFromTop must be determined such that the * necessary cells of the ListView are rendered and added to * it. */ mShouldRemoveObserver = true; int newTop = view.getTop(); int newBottom = view.getBottom(); int newHeight = newBottom - newTop; int oldHeight = oldBottom - oldTop; int deltaHeight = oldHeight - newHeight; mTranslate = getTopAndBottomTranslations(oldTop, oldBottom, deltaHeight, false); int currentTop = view.getTop(); int futureTop = oldTop + mTranslate[0]; int firstChildStartTop = getChildAt(0).getTop(); int firstVisiblePosition = getFirstVisiblePosition(); int deltaTop = currentTop - futureTop; int i; int childCount = getChildCount(); for (i = 0; i < childCount; i++) { View v = getChildAt(i); int height = v.getBottom() - Math.max(0, v.getTop()); if (deltaTop - height > 0) { firstVisiblePosition++; deltaTop -= height; } else { break; } } if (i > 0) { firstChildStartTop = 0; } setSelectionFromTop(firstVisiblePosition, firstChildStartTop - deltaTop); requestLayout(); return false; } mShouldRemoveObserver = false; observer.removeOnPreDrawListener(this); int yTranslateTop = mTranslate[0]; int yTranslateBottom = mTranslate[1]; int index = indexOfChild(view); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View v = getChildAt(i); int[] old = oldCoordinates.get(v); if (old != null) { /* * If the cell was present in the ListView before the * collapse and after the collapse then the bounds are * reset to their old values. */ v.setTop(old[0]); v.setBottom(old[1]); ViewCompat.setHasTransientState(v, false); } else { /* * If the cell is present in the ListView after the * collapse but not before the collapse then the bounds * are calculated using the bottom and top translation * of the collapsing cell. */ int delta = i > index ? yTranslateBottom : -yTranslateTop; v.setTop(v.getTop() + delta); v.setBottom(v.getBottom() + delta); } } final View expandingLayout = view.findViewById(R.id.expanding_layout); /* * Animates all the cells present on the screen after the * collapse. */ ArrayList<Animator> animations = new ArrayList<Animator>(); for (int i = 0; i < childCount; i++) { View v = getChildAt(i); if (v != view) { float diff = i > index ? -yTranslateBottom : yTranslateTop; animations.add(getAnimation(v, diff, diff)); } } /* Adds animation for collapsing the cell that was clicked. */ animations.add(getAnimation(view, yTranslateTop, -yTranslateBottom)); /* Adds an animation for fading out the extra content. */ animations.add(ObjectAnimator.ofFloat(expandingLayout, View.ALPHA, 1, 0)); /* Disabled the ListView for the duration of the animation. */ setEnabled(false); setClickable(false); /* * Play all the animations created above together at the same * time. */ AnimatorSet s = new AnimatorSet(); s.playTogether(animations); s.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { expandingLayout.setVisibility(View.GONE); view.setLayoutParams( new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); viewObject.setExpanded(false); setEnabled(true); setClickable(true); /* * Note that alpha must be set back to 1 in case this * view is reused by a cell that was expanded, but not * yet collapsed, so its state should persist in an * expanded state with the extra content visible. */ expandingLayout.setAlpha(1); } }); s.start(); return true; } }); } /** * This method takes some view and the values by which its top and bottom * bounds should be changed by. Given these params, an animation which will * animate these bound changes is created and returned. */ private Animator getAnimation(final View view, float translateTop, float translateBottom) { int top = view.getTop(); int bottom = view.getBottom(); int endTop = (int) (top + translateTop); int endBottom = (int) (bottom + translateBottom); PropertyValuesHolder translationTop = PropertyValuesHolder.ofInt("top", top, endTop); PropertyValuesHolder translationBottom = PropertyValuesHolder.ofInt("bottom", bottom, endBottom); return ObjectAnimator.ofPropertyValuesHolder(view, translationTop, translationBottom); } }