14c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein/*
24c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Copyright (C) 2012 The Android Open Source Project
34c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
44c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
54c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * you may not use this file except in compliance with the License.
64c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * You may obtain a copy of the License at
74c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
84c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
94c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Unless required by applicable law or agreed to in writing, software
114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * See the License for the specific language governing permissions and
144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * limitations under the License.
154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein */
164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinpackage com.android.deskclock.widget.sgv;
184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.animation.Animator;
204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.animation.AnimatorListenerAdapter;
214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.animation.AnimatorSet;
224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.animation.ValueAnimator;
234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.annotation.SuppressLint;
244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.content.Context;
254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.content.res.TypedArray;
264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.database.DataSetObserver;
274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.graphics.Bitmap;
284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.graphics.Canvas;
294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.graphics.PixelFormat;
304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.graphics.Point;
314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.graphics.Rect;
324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.os.Handler;
334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.os.Parcel;
344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.os.Parcelable;
354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.support.v4.util.SparseArrayCompat;
364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.support.v4.view.MotionEventCompat;
374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.support.v4.view.VelocityTrackerCompat;
384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.support.v4.view.ViewCompat;
394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.support.v4.widget.EdgeEffectCompat;
404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.util.AttributeSet;
414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.util.Log;
424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.util.SparseArray;
434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.DragEvent;
444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.Gravity;
454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.MotionEvent;
464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.VelocityTracker;
474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.View;
484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.ViewConfiguration;
494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.ViewGroup;
504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.WindowManager;
514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.widget.GridView;
524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.widget.ImageView;
534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.widget.ScrollView;
544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport com.android.deskclock.widget.sgv.SgvAnimationHelper.AnimationIn;
564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport com.android.deskclock.widget.sgv.SgvAnimationHelper.AnimationOut;
574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport java.util.ArrayList;
594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport java.util.Arrays;
604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport java.util.HashMap;
614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport java.util.List;
624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport java.util.Map;
634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein/**
654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Temporarily copied from support v4 library so that StaggeredGridView can access
664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * animation APIs on the current SDK version.
674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein */
684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein/**
694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * ListView and GridView just not complex enough? Try StaggeredGridView!
704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * <p>StaggeredGridView presents a multi-column grid with consistent column sizes
724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * but varying row sizes between the columns. Each successive item from a
734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * {@link android.widget.ListAdapter ListAdapter} will be arranged from top to bottom,
744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * left to right. The largest vertical gap is always filled first.</p>
754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * <p>Item views may span multiple columns as specified by their {@link LayoutParams}.
774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * The attribute <code>android:layout_span</code> may be used when inflating
784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * item views from xml.</p>
794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein */
804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinpublic class StaggeredGridView extends ViewGroup {
814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
82d73459eee307459d478983e00b2a4084c6e8273bIsaac Katzenelson    private static final String TAG = "Clock-" + StaggeredGridView.class.getSimpleName();
834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /*
854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * There are a few things you should know if you're going to make modifications
864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * to StaggeredGridView.
874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Like ListView, SGV populates from an adapter and recycles views that fall out
894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * of the visible boundaries of the grid. A few invariants always hold:
904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * - mFirstPosition is the adapter position of the View returned by getChildAt(0).
924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * - Any child index can be translated to an adapter position by adding mFirstPosition.
934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * - Any adapter position can be translated to a child index by subtracting mFirstPosition.
944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * - Views for items in the range [mFirstPosition, mFirstPosition + getChildCount()) are
954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *   currently attached to the grid as children. All other adapter positions do not have
964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *   active views.
974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * This means a few things thanks to the staggered grid's nature. Some views may stay attached
994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * long after they have scrolled offscreen if removing and recycling them would result in
1004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * breaking one of the invariants above.
1014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * LayoutRecords are used to track data about a particular item's layout after the associated
1034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * view has been removed. These let positioning and the choice of column for an item
1044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * remain consistent even though the rules for filling content up vs. filling down vary.
1054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Whenever layout parameters for a known LayoutRecord change, other LayoutRecords before
1074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * or after it may need to be invalidated. e.g. if the item's height or the number
1084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * of columns it spans changes, all bets for other items in the same direction are off
1094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * since the cached information no longer applies.
1104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private GridAdapter mAdapter;
1134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public static final int COLUMN_COUNT_AUTO = -1;
1154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The window size to search for a specific item when restoring scroll position.
1184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final int SCROLL_RESTORE_WINDOW_SIZE = 10;
1204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int CHILD_TO_REORDER_AREA_RATIO = 4;
1224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int SINGLE_COL_REORDERING_AREA_SIZE = 30;
1244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // Time delay in milliseconds between posting each scroll runnables.
1264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int SCROLL_HANDLER_DELAY = 5;
1274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // The default rate of pixels to scroll by when a child view is dragged towards the
1294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // upper and lower bound of this view.
1304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int DRAG_SCROLL_RATE = 10;
1314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public static final int ANIMATION_DELAY_IN_MS = 50;
1334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private AnimationIn mAnimationInMode = AnimationIn.NONE;
1354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private AnimationOut mAnimationOutMode = AnimationOut.NONE;
1364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private AnimatorSet mCurrentRunningAnimatorSet = null;
1384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Flag to indicate whether the current running animator set was canceled before it reaching
1414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * the end of the animations.  This flag is used to help indicate whether the next set of
1424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * animators should resume from where the last animator set left off.
1434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    boolean mIsCurrentAnimationCanceled = false;
1454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mColCountSetting = 2;
1474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mColCount = 2;
1484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mMinColWidth = 0;
1494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mItemMargin = 0;
1504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int[] mItemTops;
1524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int[] mItemBottoms;
1534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final Rect mTempRect = new Rect();
1554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mFastChildLayout;
1574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mPopulating;
1584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mInLayout;
1594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mIsRtlLayout;
1614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final RecycleBin mRecycler = new RecycleBin();
1634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final AdapterDataSetObserver mObserver = new AdapterDataSetObserver();
1654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mDataChanged;
1674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mItemCount;
1684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * After data set change, we ask adapter the first view that changed.
1714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Any view from 0 to mFirstChangedPosition - 1 is not changed.
1724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mFirstChangedPosition;
1744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * If set to true, then we guard against jagged edges in the grid by doing expensive
1774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * computation. Otherwise if this is false, we skip the computation.
1784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mGuardAgainstJaggedEdges;
1804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mHasStableIds;
1824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * List of all views to animate out.  This is used when we need to animate out stale views.
1854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final List<View> mViewsToAnimateOut = new ArrayList<View>();
1874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mFirstPosition;
1894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private long mFocusedChildIdToScrollIntoView;
1914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private ScrollState mCurrentScrollState;
1924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final int mTouchSlop;
1944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final int mMaximumVelocity;
1954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final int mFlingVelocity;
1964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private float mLastTouchY = 0;
1974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private float mTouchRemainderY;
1984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mActivePointerId;
1994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int TOUCH_MODE_IDLE = 0;
2014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int TOUCH_MODE_DRAGGING = 1;
2024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int TOUCH_MODE_FLINGING = 2;
2034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int TOUCH_MODE_OVERFLING = 3;
2044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // Value used to estimate the range of scroll and scroll position
2064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final static int SCROLLING_ESTIMATED_ITEM_HEIGHT = 100;
2074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mTouchMode;
2094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
2104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final OverScrollerSGV mScroller;
2114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final EdgeEffectCompat mTopEdge;
2134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final EdgeEffectCompat mBottomEdge;
2144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mIsDragReorderingEnabled;
2164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private ScrollListener mScrollListener;
2184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private OnSizeChangedListener mOnSizeChangedListener;
2194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // The view to show when the adapter is empty.
2214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private View mEmptyView;
2224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // The size of the region at location relative to the child's edges where reordering
2244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // can happen if another child view is dragged and dropped over it.
2254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mHorizontalReorderingAreaSize;
2264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // TODO: Put these states into a ReorderingParam object for maintainability.
2284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private ImageView mDragView;
2294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // X and Y positions of the touch down event that started the drag
2314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mTouchDownForDragStartX;
2324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mTouchDownForDragStartY;
2334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // X and Y offsets inside the item from where the user grabbed to the
2354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // child's left coordinate.
2364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // This is used to aid in the drawing of the drag shadow.
2374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mTouchOffsetToChildLeft;
2384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mTouchOffsetToChildTop;
2394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // Difference between screen coordinates and coordinates in this view.
2414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mOffsetToAbsoluteX;
2424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mOffsetToAbsoluteY;
2434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // the cached positions of the drag view when released.
2454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private Rect mCachedDragViewRect;
2464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // the current drag state
2484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mDragState;
2494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // the height of this view
2514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mHeight;
2524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // The bounds of the screen that should initiate scrolling when a view
2544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // is dragged past these positions.
2554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mUpperScrollBound;
2564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mLowerScrollBound;
2574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // The Bitmap that contains the drag shadow.
2594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private Bitmap mDragBitmap;
2604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final int mOverscrollDistance;
2614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final WindowManager mWindowManager;
2634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private WindowManager.LayoutParams mWindowParams;
2644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int mWindowManagerLayoutFlags =
2654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
2664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
2674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
2684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
2694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
2704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private ReorderHelper mReorderHelper;
2724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Indicates whether to use pixels-based or position-based scrollbar
2754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * properties.
2764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * This property is borrow from AbsListView
2774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mSmoothScrollbarEnabled = false;
2794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final class LayoutRecord {
2814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int column;
2824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public long id = -1;
2834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int height;
2844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int span;
2854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int[] mMargins;
2864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private final void ensureMargins() {
2884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mMargins == null) {
2894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Don't need to confirm length;
2904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // all layoutrecords are purged when column count changes.
2914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mMargins = new int[span * 2];
2924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
2934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
2944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public final int getMarginAbove(int col) {
2964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mMargins == null) {
2974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return 0;
2984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
2994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mMargins[col * 2];
3004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public final int getMarginBelow(int col) {
3034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mMargins == null) {
3044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return 0;
3054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
3064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mMargins[col * 2 + 1];
3074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public final void setMarginAbove(int col, int margin) {
3104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mMargins == null && margin == 0) {
3114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
3124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
3134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            ensureMargins();
3144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mMargins[col * 2] = margin;
3154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public final void setMarginBelow(int col, int margin) {
3184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mMargins == null && margin == 0) {
3194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
3204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
3214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            ensureMargins();
3224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mMargins[col * 2 + 1] = margin;
3234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
3264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public String toString() {
3274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            String result = "LayoutRecord{c=" + column + ", id=" + id + " h=" + height +
3284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    " s=" + span;
3294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mMargins != null) {
3304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                result += " margins[above, below](";
3314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int i = 0; i < mMargins.length; i += 2) {
3324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    result += "[" + mMargins[i] + ", " + mMargins[i+1] + "]";
3334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
3344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                result += ")";
3354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
3364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return result + "}";
3374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
3394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final Map<Long, ViewRectPair> mChildRectsForAnimation =
3414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            new HashMap<Long, ViewRectPair>();
3424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final SparseArrayCompat<LayoutRecord> mLayoutRecords =
3444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            new SparseArrayCompat<LayoutRecord>();
3454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // Handler for executing the scroll runnable
3474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private Handler mScrollHandler;
3484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // Boolean is true when the {@link #mDragScroller} scroll runanbled has been kicked off.
3504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // This is set back to false when it is removed from the handler.
3514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean mIsDragScrollerRunning;
3524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
3544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Scroller runnable to invoke scrolling when user is holding a dragged view over the upper
3554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * or lower bounds of the screen.
3564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
3574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final Runnable mDragScroller = new Runnable() {
3584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
3594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void run() {
3604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mDragState == ReorderUtils.DRAG_STATE_NONE) {
3614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
3624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
3634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean enableUpdate = true;
3654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mLastTouchY >= mLowerScrollBound) {
3664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // scroll the list up a bit if we're past the lower bound, and the direction
3674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // of the movement is towards the bottom of the view.
3684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (trackMotionScroll(-DRAG_SCROLL_RATE, false)) {
3694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Disable reordering if the view is scrolling
3704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    enableUpdate = false;
3714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
3724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else if (mLastTouchY <= mUpperScrollBound) {
3734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // scroll the list down a bit if we're past the upper bound, and the direction
3744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // of the movement is towards the top of the view.
3754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (trackMotionScroll(DRAG_SCROLL_RATE, false)) {
3764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Disable reordering if the view is scrolling
3774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    enableUpdate = false;
3784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
3794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
3804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.enableUpdatesOnDrag(enableUpdate);
3824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mScrollHandler.postDelayed(this, SCROLL_HANDLER_DELAY);
3844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    };
3864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public StaggeredGridView(Context context) {
3884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        this(context, null);
3894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
3904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public StaggeredGridView(Context context, AttributeSet attrs) {
3924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        this(context, attrs, 0);
3934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
3944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public StaggeredGridView(Context context, AttributeSet attrs, int defStyle) {
3964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        super(context, attrs, defStyle);
3974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final ViewConfiguration vc = ViewConfiguration.get(context);
3994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTouchSlop = vc.getScaledTouchSlop();
4004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mMaximumVelocity = vc.getScaledMaximumFlingVelocity();
4014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFlingVelocity = vc.getScaledMinimumFlingVelocity();
4024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScroller = new OverScrollerSGV(context);
4034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTopEdge = new EdgeEffectCompat(context);
4054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mBottomEdge = new EdgeEffectCompat(context);
4064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setWillNotDraw(false);
4074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setClipToPadding(false);
4084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        SgvAnimationHelper.initialize(context);
4104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDragState = ReorderUtils.DRAG_STATE_NONE;
4124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mIsDragReorderingEnabled = true;
4134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
4144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final ViewConfiguration configuration = ViewConfiguration.get(context);
4154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mOverscrollDistance = configuration.getScaledOverflingDistance();
4164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Disable splitting event. Only one of the children can handle motion event.
4174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setMotionEventSplittingEnabled(false);
4184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Check to see if the current layout is Right-to-Left.  This check is only supported for
4224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * API 17+.  For earlier versions, this method will just return false.
4234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE:  This is based on the private API method in {@link View} class.
4254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return boolean Boolean indicating whether the currently locale is RTL.
4274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @SuppressLint("NewApi")
4294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean isLayoutRtl() {
4304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
4314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return View.LAYOUT_DIRECTION_RTL == getLayoutDirection();
4324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
4334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return false;
4344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
4354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Set a fixed number of columns for this grid. Space will be divided evenly
4394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * among all columns, respecting the item margin between columns.
4404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The default is 2. (If it were 1, perhaps you should be using a
4414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link android.widget.ListView ListView}.)
4424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param colCount Number of columns to display.
4444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setMinColumnWidth(int)
4454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setColumnCount(int colCount) {
4474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (colCount < 1 && colCount != COLUMN_COUNT_AUTO) {
4484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            throw new IllegalArgumentException("Column count must be at least 1 - received " +
4494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    colCount);
4504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
4514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean needsPopulate = colCount != mColCount;
4524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mColCount = mColCountSetting = colCount;
4534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (needsPopulate) {
4544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // When switching column count, for now, don't restore scroll position, and just
4554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // start layout fresh again.
4564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            clearAllState();
4574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mHorizontalReorderingAreaSize = 0;
4594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            populate();
4604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
4614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public int getColumnCount() {
4644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mColCount;
4654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Set whether or not to explicitly guard against "jagged edges" in the grid
4694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * (meaning that the top edge of the children views in the first row of the grid can be
4704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * horizontally misaligned).
4714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * If guardAgainstJaggedEdges is true, then we prevent jagged edges by computing the heights of
4734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * all views starting at the 0th position of the adapter to figure out the proper offset of the
4744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * views currently on screen. This is an expensive operation and should be avoided if possible.
4754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * If guardAgainstJaggedEdges is false, then we can skip the expensive computation that
4774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * guards against jagged edges and just layout views on the screen starting from mFirstPosition
4784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * (ignoring what came before it).
4794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setGuardAgainstJaggedEdges(boolean guardAgainstJaggedEdges) {
4814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mGuardAgainstJaggedEdges = guardAgainstJaggedEdges;
4824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Set a minimum column width for
4864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param minColWidth
4874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setMinColumnWidth(int minColWidth) {
4894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mMinColWidth = minColWidth;
4904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setColumnCount(COLUMN_COUNT_AUTO);
4914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Set the margin between items in pixels. This margin is applied
4954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * both vertically and horizontally.
4964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param marginPixels Spacing between items in pixels
4984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setItemMargin(int marginPixels) {
5004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // We only need to {@link #populate()} if the margin has been changed.
5014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (marginPixels != mItemMargin) {
5024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemMargin = marginPixels;
5034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            populate();
5044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
5054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public int getItemMargin() {
5084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mItemMargin;
5094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * When smooth scrollbar is enabled, the position and size of the scrollbar thumb
5134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * is computed based on the number of visible pixels in the visible items. This
5144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * however assumes that all list items have the same height. If you use a list in
5154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * which items have different heights, the scrollbar will change appearance as the
5164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * user scrolls through the list. To avoid this issue, you need to disable this
5174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * property.
5184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * When smooth scrollbar is disabled, the position and size of the scrollbar thumb
5204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * is based solely on the number of items in the adapter and the position of the
5214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * visible items inside the adapter. This provides a stable scrollbar as the user
5224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * navigates through a list of items with varying heights.
5234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param enabled Whether or not to enable smooth scrollbar.
5254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setSmoothScrollbarEnabled(boolean)
5274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @attr ref android.R.styleable#AbsListView_smoothScrollbar
5284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setSmoothScrollbarEnabled(boolean enabled) {
5304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mSmoothScrollbarEnabled = enabled;
5314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the current state of the fast scroll feature.
5354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return True if smooth scrollbar is enabled is enabled, false otherwise.
5374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setSmoothScrollbarEnabled(boolean)
5394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean isSmoothScrollbarEnabled() {
5414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mSmoothScrollbarEnabled;
5424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Return the child view specified by the coordinates if
5474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * there exists a child there.
5484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return the child in this StaggeredGridView at the coordinates, null otherwise.
5504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private View getChildAtCoordinate(int x, int y) {
5524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (y < 0) {
5534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: If we've dragged off the screen, return null for now until we know what
5544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // we'd like the experience to be like.
5554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return null;
5564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
5574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final Rect frame = new Rect();
5594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int count = getChildCount();
5604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < count; i++) {
5614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View childView = getChildAt(i);
5634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            childView.getHitRect(frame);
5644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (frame.contains(x, y)) {
5654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return getChildAt(i);
5664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
5674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
5684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // No child view at this coordinate.
5704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return null;
5714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Get the last Y coordinate on this grid where the last touch was made
5754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public float getLastTouchY() {
5774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mLastTouchY;
5784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Enable drag reordering of child items.
5824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void enableDragReordering() {
5844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mIsDragReorderingEnabled = true;
5854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Disable drag reordering of child items.
5894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void disableDragReordering() {
5914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mIsDragReorderingEnabled = false;
5924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Check to see if drag reordering is supported.  The switch must be flipped to true, and there
5964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * must be a {@link ReorderListener} registered to listen for reordering events.
5974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return boolean indicating whether drag reordering is currently supported.
5994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
6004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean isDragReorderingSupported() {
6014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mIsDragReorderingEnabled && mReorderHelper != null &&
6024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mReorderHelper.hasReorderListener();
6034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
6044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
6064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Calculate bounds to assist in scrolling during a drag
6074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param y The y coordinate of the current drag.
6084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
6094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void initializeDragScrollParameters(int y) {
6104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Calculate the upper and lower bound of the screen to support drag scrolling
6114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mHeight = getHeight();
6124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mUpperScrollBound = Math.min(y - mTouchSlop, mHeight / 5);
6134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mLowerScrollBound = Math.max(y + mTouchSlop, mHeight * 4 / 5);
6144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
6154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
6174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Initiate the dragging process. Create a bitmap that is displayed as the dragging event
6184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * happens and is moved around across the screen.  This function is called once for each time
6194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * that a dragging event is initiated.
6204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
6214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The logic to this method was borrowed from the TouchInterceptor.java class from the
6224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * music app.
6234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
6244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param draggedChild The child view being dragged
6254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param x The x coordinate of this view where dragging began
6264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param y The y coordinate of this view where dragging began
6274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
6284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void startDragging(final View draggedChild, final int x, final int y) {
6294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!isDragReorderingSupported()) {
6304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
6314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDragBitmap = createDraggedChildBitmap(draggedChild);
6344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mDragBitmap == null) {
6354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // It appears that creating bitmaps for large views fail. For now, don't allow
6364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // dragging in this scenario.  When using the framework's drag and drop implementation,
6374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // drag shadow also fails with a OutofResourceException when trying to draw the drag
6384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // shadow onto a Surface.
6394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.handleDragCancelled(draggedChild);
6404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
6414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTouchOffsetToChildLeft = x - draggedChild.getLeft();
6434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTouchOffsetToChildTop = y - draggedChild.getTop();
6444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        updateReorderStates(ReorderUtils.DRAG_STATE_DRAGGING);
6454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        initializeDragScrollParameters(y);
6474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final LayoutParams params = (LayoutParams) draggedChild.getLayoutParams();
6494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mReorderHelper.handleDragStart(draggedChild, params.position, params.id,
6504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                new Point(mTouchDownForDragStartX, mTouchDownForDragStartY));
6514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // TODO: Reconsider using the framework's DragShadow support for dragging,
6534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // and only draw the bitmap in onDrop for animation.
6544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final Context context = getContext();
6554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDragView = new ImageView(context);
6564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDragView.setImageBitmap(mDragBitmap);
6574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDragView.setAlpha(160);
6584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowParams = new WindowManager.LayoutParams();
6604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowParams.gravity = Gravity.TOP | Gravity.START;
6614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
6634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
6644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowParams.flags = mWindowManagerLayoutFlags;
6654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowParams.format = PixelFormat.TRANSLUCENT;
6664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Use WindowManager to overlay a transparent image on drag
6674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowManager.addView(mDragView, mWindowParams);
6684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        updateDraggedBitmapLocation(x, y);
6694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
6704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private Bitmap createDraggedChildBitmap(View view) {
6724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        view.setDrawingCacheEnabled(true);
6734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final Bitmap cache = view.getDrawingCache();
6744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        Bitmap bitmap = null;
6764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (cache != null) {
6774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            try {
6784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
6794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } catch (final OutOfMemoryError e) {
6804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.w(TAG, "Failed to copy bitmap from Drawing cache", e);
6814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                bitmap = null;
6824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
6834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        view.destroyDrawingCache();
6864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        view.setDrawingCacheEnabled(false);
6874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return bitmap;
6894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
6904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
6924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Updates the current drag state and the UI appropriately.
6934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param state the new drag state to update to.
6944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
6954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void updateReorderStates(int state) throws IllegalStateException {
6964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean resetDraggedChildView = false;
6974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean resetDragProperties = false;
6984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDragState = state;
7004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch (state) {
7024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case ReorderUtils.DRAG_STATE_NONE:
7034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case ReorderUtils.DRAG_STATE_DRAGGING:
7044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // reset all states when a drag is complete or when we're starting a new drag.
7054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                resetDraggedChildView = true;
7064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                resetDragProperties = true;
7074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
7084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case ReorderUtils.DRAG_STATE_RELEASED_REORDER:
7104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // In a release over a valid reordering zone, don't reset any UI.  Let
7114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // LayoutChildren() take care of doing the appropriate animation
7124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // based on the result
7134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
7144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case ReorderUtils.DRAG_STATE_RELEASED_HOVER:
7164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // When a dragged child is released over another child, the dragged child will
7174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // remain hidden.  It is up to the ReorderListener to refresh the UI state
7184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // of the child if it does not handle the drop.
7194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                resetDragProperties = true;
7204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
7214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            default:
7234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                throw new IllegalStateException("Illegal drag state: " + mDragState);
7244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (resetDraggedChildView && mReorderHelper.getDraggedChild() != null) {
7274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // DraggedChildId and mCachedDragViewRect need to stay around longer than
7284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // the other properties because on the next data change, as we lay out, we'll need
7294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // mCachedDragViewRect to position the view's animation start position, and
7304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // draggedChildId to check if the current was the dragged view.
7314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // For the other properties - DraggedOverChildView, DraggedChildView, etc.,
7324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // as soon as drag is released, we can reset them because they have no impact on the
7334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // next layout pass.
7344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.clearDraggedChildId();
7354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCachedDragViewRect = null;
7364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (resetDragProperties) {
7394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mDragView != null) {
7404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDragView.setVisibility(INVISIBLE);
7414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mWindowManager.removeView(mDragView);
7424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDragView.setImageDrawable(null);
7434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDragView = null;
7444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mDragBitmap != null) {
7464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mDragBitmap.recycle();
7474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mDragBitmap = null;
7484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
7494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // We don't reset DraggedChildId here because it may still be in used.
7524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Let LayoutChildren reset it when it's done with it.
7534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.clearDraggedChild();
7544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.clearDraggedOverChild();
7554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
7574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
7594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Redraw the dragged child's bitmap based on the new coordinates.  If the reordering direction
7604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * is {@link ReorderUtils#REORDER_DIRECTION_VERTICAL}, then ignore the x coordinate, as
7614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * only vertical movement is allowed.  Similarly, if reordering direction is
7624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link ReorderUtils#REORDER_DIRECTION_HORIZONTAL}.  Even though this class does not manage
7634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * drag shadow directly, we need to make sure we position the dragged bitmap at where the
7644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * drag shadow is so that when drag ends, we can swap the shadow and the bitmap to animate
7654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * the view into place.
7664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param x The updated x coordinate of the drag shadow.
7674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param y THe updated y coordinate of the drag shadow.
7684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
7694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void updateDraggedBitmapLocation(int x, int y) {
7704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int direction = mAdapter.getReorderingDirection();
7714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if ((direction & ReorderUtils.REORDER_DIRECTION_HORIZONTAL) ==
7724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                ReorderUtils.REORDER_DIRECTION_HORIZONTAL) {
7734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mDragBitmap != null && mDragBitmap.getWidth() > getWidth()) {
7744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If the bitmap is wider than the width of the screen, then some parts of the view
7754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // are off screen.  In this case, just set the drag shadow to start at x = 0
7764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // (adjusted to the absolute position on screen) so that at least the beginning of
7774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // the drag shadow is guaranteed to be within view.
7784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mWindowParams.x = mOffsetToAbsoluteX;
7794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
7804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // WindowParams is RTL agnostic and operates on raw coordinates.  So in an RTL
7814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // layout, we would still want to find the view's left coordinate for the
7824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // drag shadow, rather than the view's start.
7834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mWindowParams.x = x - mTouchOffsetToChildLeft + mOffsetToAbsoluteX;
7844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
7864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mWindowParams.x = mOffsetToAbsoluteX;
7874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if ((direction & ReorderUtils.REORDER_DIRECTION_VERTICAL) ==
7904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                ReorderUtils.REORDER_DIRECTION_VERTICAL) {
7914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mWindowParams.y = y - mTouchOffsetToChildTop + mOffsetToAbsoluteY;
7924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
7934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mWindowParams.y = mOffsetToAbsoluteY;
7944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mWindowManager.updateViewLayout(mDragView, mWindowParams);
7974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
7984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
8004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Update the visual state of the drag event based on the current drag location.  If the user
8014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * has attempted to re-order by dragging a child over another child's drop zone, call the
8024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * appropriate {@link ReorderListener} callback.
8034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
8044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param x The current x coordinate of the drag event
8054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param y The current y coordinate of the drag event
8064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
8074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void handleDrag(int x, int y) {
8084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mDragState != ReorderUtils.DRAG_STATE_DRAGGING) {
8094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
8104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // TODO: Consider moving drag shadow management logic into mReorderHelper as well, or
8134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // scrap the custom logic and use the framework's drag-and-drop support now that we're not
8144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // doing anything special to the drag shadow.
8154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        updateDraggedBitmapLocation(x, y);
8164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mCurrentRunningAnimatorSet == null) {
8184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If the current animator set is not null, then animation is running, in which case,
8194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // we shouldn't do any reordering processing, as views will be moving around, and
8204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // interfering with drag target calculations.
8214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.handleDrag(new Point(x, y));
8224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
8244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
8264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Check if a view is reorderable.
8274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param i the child index in view group
8284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
8294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean isChildReorderable(int i) {
8304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mAdapter.isDraggable(mFirstPosition + i);
8314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
8324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
8344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Handle the the release of a dragged view.
8354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param x The current x coordinate where the drag was released.
8364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param y The current y coordinate where the drag was released.
8374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
8384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void handleDrop(int x, int y) {
8394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!mReorderHelper.hasReorderListener()) {
8404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            updateReorderStates(ReorderUtils.DRAG_STATE_NONE);
8414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
8424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mReorderHelper.isOverReorderingArea()) {
8454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Store the location of the drag shadow at where dragging stopped
8464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // for animation if a reordering has just happened. Since the drag
8474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // shadow is drawn as a WindowManager view, its coordinates are
8484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // absolute. However, for views inside the grid, we need to operate
8494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // with coordinate values that's relative to this grid, so we need
8504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // to subtract the offset to absolute screen coordinates that have
8514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // been added to mWindowParams.
8524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int left = mWindowParams.x - mOffsetToAbsoluteX;
8534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int top = mWindowParams.y - mOffsetToAbsoluteY;
8544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCachedDragViewRect = new Rect(
8564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    left, top, left + mDragView.getWidth(), top + mDragView.getHeight());
8574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (getChildCount() > 0) {
8584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final View view = getChildAt(0);
8594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
8604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (lp.position > mReorderHelper.getDraggedChildPosition()) {
8614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // If the adapter position of the first child in view is
8624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // greater than the position of the original dragged child,
8634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // this means that the user has scrolled the child out of
8644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // view. Those off screen views would have been recycled. If
8654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // mFirstPosition is currently x, after the reordering
8664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // operation, the child[mFirstPosition] will be
8674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // at mFirstPosition-1. We want to adjust mFirstPosition so
8684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // that we render the view in the correct location after
8694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // reordering completes.
8704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //
8714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // If the user has not scrolled the original dragged child
8724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // out of view, then the view has not been recycled and is
8734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // still in view.
8744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // When onLayout() gets called, we'll automatically fill in
8754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // the empty space that the child leaves behind from the
8764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // reordering operation.
8774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mFirstPosition--;
8784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
8794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
8804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Get the current scroll position so that after reordering
8824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // completes, we can restore the scroll position of mFirstPosition.
8834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentScrollState = getScrollState();
8844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean reordered = mReorderHelper.handleDrop(new Point(x, y));
8874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (reordered) {
8884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            updateReorderStates(ReorderUtils.DRAG_STATE_RELEASED_REORDER);
8894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
8904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            updateReorderStates(ReorderUtils.DRAG_STATE_NONE);
8914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
8934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
8954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean onInterceptTouchEvent(MotionEvent ev) {
8964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mVelocityTracker.addMovement(ev);
8974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
8984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch (action) {
8994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case MotionEvent.ACTION_DOWN: {
9004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mOffsetToAbsoluteX = (int)(ev.getRawX() - ev.getX());
9014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mOffsetToAbsoluteY = (int)(ev.getRawY() - ev.getY());
9024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Per bug 7377413, event.getX() and getY() returns rawX and rawY when accessed in
9044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // dispatchDragEvent, so since an action down is required before a drag can be
9054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // initiated, initialize mTouchDownForDragStartX/Y here for the most accurate value.
9064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchDownForDragStartX = (int) ev.getX();
9074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchDownForDragStartY = (int) ev.getY();
9084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mVelocityTracker.clear();
9104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mScroller.abortAnimation();
9114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mLastTouchY = ev.getY();
9124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
9134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchRemainderY = 0;
9144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mTouchMode == TOUCH_MODE_FLINGING) {
9154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Catch!
9164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_DRAGGING;
9174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return true;
9184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
9204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
9214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case MotionEvent.ACTION_MOVE: {
9224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
9234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (index < 0) {
9244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    Log.e(TAG, "onInterceptTouchEvent could not find pointer with id " +
9254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            mActivePointerId + " - did StaggeredGridView receive an inconsistent " +
9264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            "event stream?");
9274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return false;
9284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float y = MotionEventCompat.getY(ev, index);
9304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float dy = y - mLastTouchY + mTouchRemainderY;
9314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int deltaY = (int) dy;
9324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchRemainderY = dy - deltaY;
9334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (Math.abs(dy) > mTouchSlop) {
9354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_DRAGGING;
9364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return true;
9374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
9394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
9404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return false;
9424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
9434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
9454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean onTouchEvent(MotionEvent ev) {
9464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mVelocityTracker.addMovement(ev);
9474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
9484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch (action) {
9494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case MotionEvent.ACTION_DOWN:
9504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                resetScroller();
9514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mVelocityTracker.clear();
9524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mScroller.abortAnimation();
9534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mLastTouchY = ev.getY();
9544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
9554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchRemainderY = 0;
9564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
9574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case MotionEvent.ACTION_MOVE: {
9594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
9604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (index < 0) {
9614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    Log.e(TAG, "onInterceptTouchEvent could not find pointer with id " +
9624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            mActivePointerId + " - did StaggeredGridView receive an inconsistent " +
9634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            "event stream?");
9644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return false;
9654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float y = MotionEventCompat.getY(ev, index);
9684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float dy = y - mLastTouchY + mTouchRemainderY;
9694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int deltaY = (int) dy;
9704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchRemainderY = dy - deltaY;
9714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (Math.abs(dy) > mTouchSlop) {
9734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_DRAGGING;
9744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mTouchMode == TOUCH_MODE_DRAGGING) {
9774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mLastTouchY = y;
9784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (!trackMotionScroll(deltaY, true)) {
9794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // Break fling velocity if we impacted an edge.
9804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mVelocityTracker.clear();
9814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
9824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
9844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
9854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case MotionEvent.ACTION_CANCEL: {
9874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchMode = TOUCH_MODE_IDLE;
9884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
9894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
9904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case MotionEvent.ACTION_UP: {
9924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
9934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float velocity = VelocityTrackerCompat.getYVelocity(mVelocityTracker,
9944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mActivePointerId);
9954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (Math.abs(velocity) > mFlingVelocity) {
9964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_FLINGING;
9974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    resetScroller();
9984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mScroller.fling(0, 0, 0, (int) velocity, 0, 0,
9994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            Integer.MIN_VALUE, Integer.MAX_VALUE);
10004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mLastTouchY = 0;
10014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    ViewCompat.postInvalidateOnAnimation(this);
10024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
10034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_IDLE;
10044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
10054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
10064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            break;
10074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
10084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return true;
10104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
10114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void resetScroller() {
10134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTouchMode = TOUCH_MODE_IDLE;
10144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTopEdge.finish();
10154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mBottomEdge.finish();
10164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScroller.abortAnimation();
10174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
10184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
10204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean dispatchDragEvent(DragEvent event) {
10214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!isDragReorderingSupported()) {
10224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If the consumer of this StaggeredGridView has not registered a ReorderListener,
10234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // don't bother handling drag events.
10244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return super.dispatchDragEvent(event);
10254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
10264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch(event.getAction()) {
10284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case DragEvent.ACTION_DRAG_STARTED:
10294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Per bug 7071594, we won't be able to catch this event in onDragEvent,
10304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // so we'll handle the event as it is being dispatched on the way down.
10314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mReorderHelper.hasReorderListener() && mIsDragReorderingEnabled) {
10324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final View child = getChildAtCoordinate(
10334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            mTouchDownForDragStartX, mTouchDownForDragStartY);
10344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (child != null) {
10354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // Child can be null if the touch point is not on a child view, but is
10364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // still within the bounds of this StaggeredGridView (i.e., margins
10374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // between cells).
10384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        startDragging(child, mTouchDownForDragStartX, mTouchDownForDragStartY);
10394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // We must return true in order to continue getting future
10404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // {@link DragEvent}s.
10414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        return true;
10424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
10434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
10444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Be sure to return a value here instead of calling super.dispatchDragEvent()
10454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // which will unnecessarily dispatch to all the children (since the
10464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // {@link StaggeredGridView} handles all drag events for our purposes)
10474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return false;
10484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case DragEvent.ACTION_DROP:
10504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case DragEvent.ACTION_DRAG_ENDED:
10514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mDragState == ReorderUtils.DRAG_STATE_DRAGGING) {
10524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    handleDrop((int)event.getX(), (int)event.getY());
10534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
10544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Return early here to avoid calling super.dispatchDragEvent() which dispatches to
10564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // children (since this view already can handle all drag events). The super call
10574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // can also cause a NPE if the view hierarchy changed in the middle of a drag
10584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // and the {@link DragEvent} gets nulled out. This is a workaround for
10594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // a framework bug: 8298439.
10604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Since the {@link StaggeredGridView} handles all drag events for our purposes,
10614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // just manually fire the drag event to ourselves.
10624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return onDragEvent(event);
10634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
10644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // In all other cases, default to the superclass implementation. We need this so that
10664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // the drag/drop framework will fire off {@link #onDragEvent(DragEvent ev)} calls to us.
10674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return super.dispatchDragEvent(event);
10684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
10694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
10714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean onDragEvent(DragEvent ev) {
10724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!isDragReorderingSupported()) {
10734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If the consumer of this StaggeredGridView has not registered a ReorderListener,
10744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // don't bother handling drag events.
10754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return false;
10764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
10774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int x = (int)ev.getX();
10794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int y = (int)ev.getY();
10804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch(ev.getAction()) {
10824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case DragEvent.ACTION_DRAG_LOCATION:
10834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mDragState == ReorderUtils.DRAG_STATE_DRAGGING) {
10844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    handleDrag(x, y);
10854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mLastTouchY = y;
10864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
10874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Kick off the scroll handler on the first drag location event,
10894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // if it's not already running
10904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (!mIsDragScrollerRunning &&
10914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // And if the distance traveled while dragging exceeds the touch slop
10924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        ((Math.abs(x - mTouchDownForDragStartX) >= 4 * mTouchSlop) ||
10934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        (Math.abs(y - mTouchDownForDragStartY) >= 4 * mTouchSlop))) {
10944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Set true because that the scroller is running now
10954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mIsDragScrollerRunning = true;
10964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
10974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mScrollHandler == null) {
10984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mScrollHandler = getHandler();
10994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
11004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY);
11014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
11024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return true;
11044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case DragEvent.ACTION_DROP:
11064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case DragEvent.ACTION_DRAG_ENDED:
11074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // We can either expect to receive:
11084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // 1. Both {@link DragEvent#ACTION_DROP} and then
11094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                //    {@link DragEvent#ACTION_DRAG_ENDED} if the drop is over this view.
11104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // 2. Only {@link DragEvent#ACTION_DRAG_ENDED} if the drop happened over a
11114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                //    different view.
11124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // For this reason, we should always handle the drop. In case #1, if this code path
11134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // gets executed again then nothing will happen because we will have already
11144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // updated {@link #mDragState} to not be {@link ReorderUtils#DRAG_STATE_DRAGGING}.
11154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mScrollHandler != null) {
11164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mScrollHandler.removeCallbacks(mDragScroller);
11174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Scroller is no longer running
11184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mIsDragScrollerRunning = false;
11194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
11204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return true;
11224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
11234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return false;
11254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
11264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
11284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
11294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param deltaY Pixels that content should move by
11304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return true if the movement completed, false if it was stopped prematurely.
11314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
11324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean trackMotionScroll(int deltaY, boolean allowOverScroll) {
11334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean contentFits = contentFits();
11344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int allowOverhang = Math.abs(deltaY);
11354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int overScrolledBy;
11364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int movedBy;
11374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!contentFits) {
11384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int overhang;
11394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final boolean up;
11404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mPopulating = true;
11414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (deltaY > 0) {
11424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                overhang = fillUp(mFirstPosition - 1, allowOverhang);
11434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                up = true;
11444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
11454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                overhang = fillDown(mFirstPosition + getChildCount(), allowOverhang);
11464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (overhang < 0) {
11484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Overhang when filling down indicates how many pixels past the bottom of the
11494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // screen has been filled in.  If this value is negative, it should be set to
11504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // 0 so that we don't allow over scrolling.
11514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    overhang = 0;
11524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
11534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                up = false;
11554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
11564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            movedBy = Math.min(overhang, allowOverhang);
11584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            offsetChildren(up ? movedBy : -movedBy);
11594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            recycleOffscreenViews();
11604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mPopulating = false;
11614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            overScrolledBy = allowOverhang - overhang;
11624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
11634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            overScrolledBy = allowOverhang;
11644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            movedBy = 0;
11654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
11664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (allowOverScroll) {
11684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int overScrollMode = ViewCompat.getOverScrollMode(this);
11694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
11714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits)) {
11724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (overScrolledBy > 0) {
11744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final EdgeEffectCompat edge = deltaY > 0 ? mTopEdge : mBottomEdge;
11754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    edge.onPull((float) Math.abs(deltaY) / getHeight());
11764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    ViewCompat.postInvalidateOnAnimation(this);
11774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
11784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
11794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
11804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        awakenScrollBars(0 /* show immediately */, true /* invalidate */);
11824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return deltaY == 0 || movedBy != 0;
11834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
11844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final boolean contentFits() {
11864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mFirstPosition != 0 || getChildCount() != mItemCount) {
11874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return false;
11884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
11894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
11904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int topmost = Integer.MAX_VALUE;
11914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int bottommost = Integer.MIN_VALUE;
11924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < mColCount; i++) {
11934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mItemTops[i] < topmost) {
11944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                topmost = mItemTops[i];
11954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
11964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mItemBottoms[i] > bottommost) {
11974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                bottommost = mItemBottoms[i];
11984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
11994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return topmost >= getPaddingTop() && bottommost <= getHeight() - getPaddingBottom();
12024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
12034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
12054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Recycle views within the range starting from startIndex (inclusive) until the last
12064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * attached child view.
12074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
12084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void recycleViewsInRange(int startIndex, int endIndex) {
12094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = endIndex; i >= startIndex; i--) {
12104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = getChildAt(i);
12114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mInLayout) {
12134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeViewsInLayout(i, 1);
12144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
12154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeViewAt(i);
12164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
12174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mRecycler.addScrap(child);
12194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
12214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // TODO: Have other overloaded recycle methods call into this one so we would just have one
12234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    // code path.
12244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void recycleView(View view) {
12254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (view == null) {
12264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
12274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mInLayout) {
12304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            removeViewInLayout(view);
12314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            invalidate();
12324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
12334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            removeView(view);
12344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mRecycler.addScrap(view);
12374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
12384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
12404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Important: this method will leave offscreen views attached if they
12414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * are required to maintain the invariant that child view with index i
12424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * is always the view corresponding to position mFirstPosition + i.
12434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
12444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void recycleOffscreenViews() {
12454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (getChildCount() == 0) {
12464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
12474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int height = getHeight();
12504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int clearAbove = -mItemMargin;
12514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int clearBelow = height + mItemMargin;
12524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = getChildCount() - 1; i >= 0; i--) {
12534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = getChildAt(i);
12544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (child.getTop() <= clearBelow)  {
12554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // There may be other offscreen views, but we need to maintain
12564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // the invariant documented above.
12574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
12584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
12594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.clearFocus();
12614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mInLayout) {
12624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeViewsInLayout(i, 1);
12634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
12644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeViewAt(i);
12654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
12664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mRecycler.addScrap(child);
12684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        while (getChildCount() > 0) {
12714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = getChildAt(0);
12724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (child.getBottom() >= clearAbove) {
12734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // There may be other offscreen views, but we need to maintain
12744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // the invariant documented above.
12754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
12764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
12774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.clearFocus();
12794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mInLayout) {
12804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeViewsInLayout(0, 1);
12814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
12824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeViewAt(0);
12834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
12844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mRecycler.addScrap(child);
12864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFirstPosition++;
12874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
12884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
12894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int childCount = getChildCount();
12904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (childCount > 0) {
12914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Repair the top and bottom column boundaries from the views we still have
12924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Arrays.fill(mItemTops, Integer.MAX_VALUE);
12934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Arrays.fill(mItemBottoms, Integer.MIN_VALUE);
12944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < childCount; i++){
12954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final View child = getChildAt(i);
12964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
12974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int top = child.getTop() - mItemMargin;
12984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int bottom = child.getBottom();
12994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                LayoutRecord rec = mLayoutRecords.get(mFirstPosition + i);
13004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
13014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // It's possible the layout record could be null for visible views because
13024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // they are cleared between adapter data set changes, but the views are left
13034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // attached for the purpose of animations. Hence, populate the layout record again.
13044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (rec == null) {
13054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    rec = recreateLayoutRecord(mFirstPosition + i, child, lp);
13064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
13074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
13084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // In LTR layout, iterate across each column that this child is laid out in,
13094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // starting from the child's first column (lp.column).  For each column, update
13104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // mItemTops and mItemBottoms appropriately to take into account this child's
13114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // dimension.  In RTL layout, iterate in reverse, where the child's starting
13124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // column would start from the right-most.
13134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int span = Math.min(mColCount, lp.span);
13144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int spanIndex = 0; spanIndex < span; spanIndex++) {
13154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int col = mIsRtlLayout ? lp.column - spanIndex :
13164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            lp.column + spanIndex;
13174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int colTop = top - rec.getMarginAbove(spanIndex);
13184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int colBottom = bottom + rec.getMarginBelow(spanIndex);
13194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (colTop < mItemTops[col]) {
13204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mItemTops[col] = colTop;
13214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
13224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (colBottom > mItemBottoms[col]) {
13234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mItemBottoms[col] = colBottom;
13244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
13254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
13264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
13274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
13284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int col = 0; col < mColCount; col++) {
13294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mItemTops[col] == Integer.MAX_VALUE) {
13304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // If one was untouched, both were.
13314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int top = getPaddingTop();
13324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mItemTops[col] = top;
13334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mItemBottoms[col] = top;
13344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
13354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
13364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
13374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
13384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mCurrentScrollState = getScrollState();
13394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
13404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
13414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private LayoutRecord recreateLayoutRecord(int position, View child, LayoutParams lp) {
13424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final LayoutRecord rec = new LayoutRecord();
13434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mLayoutRecords.put(position, rec);
13444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rec.column = lp.column;
13454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rec.height = child.getHeight();
13464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rec.id = lp.id;
13474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rec.span = Math.min(mColCount, lp.span);
13484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return rec;
13494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
13504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
13514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
13524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void computeScroll() {
13534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mTouchMode == TOUCH_MODE_OVERFLING) {
13544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            handleOverfling();
13554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else if (mScroller.computeScrollOffset()) {
13564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int overScrollMode = ViewCompat.getOverScrollMode(this);
13574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final boolean supportsOverscroll = overScrollMode != ViewCompat.OVER_SCROLL_NEVER;
13584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int y = mScroller.getCurrY();
13594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int dy = (int) (y - mLastTouchY);
13604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: Figure out why mLastTouchY is being updated here. Consider using a new class
13614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // variable since this value does not represent the last place on the screen where a
13624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // touch occurred.
13634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mLastTouchY = y;
13644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Check if the top of the motion view is where it is
13654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // supposed to be
13664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View motionView = supportsOverscroll &&
13674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    getChildCount() > 0 ? getChildAt(0) : null;
13684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int motionViewPrevTop = motionView != null ? motionView.getTop() : 0;
13694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final boolean stopped = !trackMotionScroll(dy, false);
13704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (!stopped && !mScroller.isFinished()) {
13714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchMode = TOUCH_MODE_IDLE;
13724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                ViewCompat.postInvalidateOnAnimation(this);
13734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else if (stopped && dy != 0 && supportsOverscroll) {
13744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Check to see if we have bumped into the scroll limit
13754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (motionView != null) {
13764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        final int motionViewRealTop = motionView.getTop();
13774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // Apply overscroll
13784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        final int overscroll = -dy - (motionViewRealTop - motionViewPrevTop);
13794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        overScrollBy(0, overscroll, 0, getScrollY(), 0, 0, 0, mOverscrollDistance,
13804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                true);
13814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
13824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final EdgeEffectCompat edge;
13834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (dy > 0) {
13844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        edge = mTopEdge;
13854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mBottomEdge.finish();
13864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    } else {
13874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        edge = mBottomEdge;
13884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mTopEdge.finish();
13894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
13904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    edge.onAbsorb(Math.abs((int) mScroller.getCurrVelocity()));
13914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mScroller.computeScrollOffset()) {
13924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mScroller.notifyVerticalEdgeReached(getScrollY(), 0, mOverscrollDistance);
13934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
13944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_OVERFLING;
13954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    ViewCompat.postInvalidateOnAnimation(this);
13964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
13974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTouchMode = TOUCH_MODE_IDLE;
13984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
13994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
14004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void handleOverfling() {
14034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // If the animation is not finished yet, determine next steps.
14044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mScroller.computeScrollOffset()) {
14054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int scrollY = getScrollY();
14064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int currY = mScroller.getCurrY();
14074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int deltaY = currY - scrollY;
14084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (overScrollBy(0, deltaY, 0, scrollY, 0, 0, 0, mOverscrollDistance, false)) {
14094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final boolean crossDown = scrollY <= 0 && currY > 0;
14104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final boolean crossUp = scrollY >= 0 && currY < 0;
14114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (crossDown || crossUp) {
14124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    int velocity = (int) mScroller.getCurrVelocity();
14134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (crossUp) {
14144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        velocity = -velocity;
14154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
14164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Don't flywheel from this; we're just continuing
14184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // things.
14194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTouchMode = TOUCH_MODE_IDLE;
14204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mScroller.abortAnimation();
14214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
14224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Spring back! We are done overscrolling.
14234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mScroller.springBack(0, scrollY, 0, 0, 0, 0)) {
14244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mTouchMode = TOUCH_MODE_OVERFLING;
14254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        ViewCompat.postInvalidateOnAnimation(this);
14264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    } else {
14274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // If already valid, we are done. Exit overfling mode.
14284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mTouchMode = TOUCH_MODE_IDLE;
14294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
14304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
14314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
14324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Still over-flinging; just post the next frame of the animation.
14334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                ViewCompat.postInvalidateOnAnimation(this);
14344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
14354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
14364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Otherwise, exit overfling mode.
14374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mTouchMode = TOUCH_MODE_IDLE;
14384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mScroller.abortAnimation();
14394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
14404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
14434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
14444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (getScrollY() != scrollY) {
14454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            scrollTo(0, scrollY);
14464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
14474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
14504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void draw(Canvas canvas) {
14514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        super.draw(canvas);
14524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mTopEdge != null) {
14544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean needsInvalidate = false;
14554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (!mTopEdge.isFinished()) {
14564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int restoreCount = canvas.save();
14574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                canvas.translate(0, 0);
14584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTopEdge.draw(canvas);
14594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                canvas.restoreToCount(restoreCount);
14604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                needsInvalidate = true;
14614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
14624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (!mBottomEdge.isFinished()) {
14634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int restoreCount = canvas.save();
14644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int width = getWidth();
14654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                canvas.translate(-width, getHeight());
14664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                canvas.rotate(180, width, 0);
14674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mBottomEdge.draw(canvas);
14684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                canvas.restoreToCount(restoreCount);
14694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                needsInvalidate = true;
14704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
14714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (needsInvalidate) {
14734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                ViewCompat.postInvalidateOnAnimation(this);
14744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
14754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
14764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void beginFastChildLayout() {
14794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFastChildLayout = true;
14804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void endFastChildLayout() {
14834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFastChildLayout = false;
14844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        populate();
14854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
14884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void requestLayout() {
14894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!mPopulating && !mFastChildLayout) {
14904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super.requestLayout();
14914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
14924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
14934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
14944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
14954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Sets the view to show if the adapter is empty
14964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
14974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setEmptyView(View emptyView) {
14984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mEmptyView = emptyView;
14994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        updateEmptyStatus();
15014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
15024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public View getEmptyView() {
15044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mEmptyView;
15054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
15064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
15084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Update the status of the list based on the whether the adapter is empty.  If is it empty and
15094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * we have an empty view, display it.  In all the other cases, make sure that the
15104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * StaggeredGridView is VISIBLE and that the empty view is GONE (if it's not null).
15114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
15124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void updateEmptyStatus() {
15134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mAdapter == null || mAdapter.isEmpty()) {
15144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mEmptyView != null) {
15154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mEmptyView.setVisibility(View.VISIBLE);
15164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                setVisibility(View.GONE);
15174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
15184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                setVisibility(View.VISIBLE);
15194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
15204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
15214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mEmptyView != null) {
15224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mEmptyView.setVisibility(View.GONE);
15234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
15244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            setVisibility(View.VISIBLE);
15254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
15274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
15294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
15304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
15314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
15324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
15334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
15344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (widthMode != MeasureSpec.EXACTLY) {
15364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Log.d(TAG, "onMeasure: must have an exact width or match_parent! " +
15374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    "Using fallback spec of EXACTLY " + widthSize);
15384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            widthMode = MeasureSpec.EXACTLY;
15394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (heightMode != MeasureSpec.EXACTLY) {
15414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Log.d(TAG, "onMeasure: must have an exact height or match_parent! " +
15424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    "Using fallback spec of EXACTLY " + heightSize);
15434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            heightMode = MeasureSpec.EXACTLY;
15444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setMeasuredDimension(widthSize, heightSize);
15474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mColCountSetting == COLUMN_COUNT_AUTO) {
15494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int colCount = widthSize / mMinColWidth;
15504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (colCount != mColCount) {
15514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mColCount = colCount;
15524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
15534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mHorizontalReorderingAreaSize == 0) {
15564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mColCount > 1) {
15574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int totalMarginWidth = mItemMargin * (mColCount + 1);
15584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int singleViewWidth = (widthSize - totalMarginWidth) / mColCount;
15594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mHorizontalReorderingAreaSize = singleViewWidth / CHILD_TO_REORDER_AREA_RATIO;
15604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
15614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mHorizontalReorderingAreaSize = SINGLE_COL_REORDERING_AREA_SIZE;
15624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
15634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
15654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
15674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected void onLayout(boolean changed, int l, int t, int r, int b) {
15684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mIsRtlLayout = isLayoutRtl();
15694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mInLayout = true;
15714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        populate();
15724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mInLayout = false;
15734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int width = r - l;
15744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int height = b - t;
15754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mTopEdge.setSize(width, height);
15764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mBottomEdge.setSize(width, height);
15774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
15784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void populate() {
15804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (getWidth() == 0 || getHeight() == 0 || mAdapter == null) {
15814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
15824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mColCount == COLUMN_COUNT_AUTO) {
15854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int colCount = getWidth() / mMinColWidth;
15864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (colCount != mColCount) {
15874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mColCount = colCount;
15884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
15894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
15904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colCount = mColCount;
15924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mItemTops == null || mItemBottoms == null || mItemTops.length != colCount ||
15934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemBottoms.length != colCount) {
15944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemTops = new int[colCount];
15954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemBottoms = new int[colCount];
15964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
15974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mLayoutRecords.clear();
15984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mInLayout) {
15994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeAllViewsInLayout();
16004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
16014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                removeAllViews();
16024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
16034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Before we do layout, if there are any pending animations and data has changed,
16064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // cancel the animation, as layout on new data will likely trigger another animation
16074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // set to be run.
16084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mDataChanged && mCurrentRunningAnimatorSet != null) {
16094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentRunningAnimatorSet.cancel();
16104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentRunningAnimatorSet = null;
16114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isSelectionAtTop()) {
16144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentScrollState = null;
16154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mCurrentScrollState != null) {
16184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            restoreScrollPosition(mCurrentScrollState);
16194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
16204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            calculateLayoutStartOffsets(getPaddingTop() /* layout start offset */);
16214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mPopulating = true;
16244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFocusedChildIdToScrollIntoView = -1;
16264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final View focusedChild = getFocusedChild();
16274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (focusedChild != null) {
16284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) focusedChild.getLayoutParams();
16294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFocusedChildIdToScrollIntoView = lp.id;
16304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        layoutChildren(mDataChanged);
16334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        fillDown(mFirstPosition + getChildCount(), 0);
16344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        fillUp(mFirstPosition - 1, 0);
16354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isDragReorderingSupported() &&
16374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDragState == ReorderUtils.DRAG_STATE_RELEASED_REORDER ||
16384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDragState == ReorderUtils.DRAG_STATE_RELEASED_HOVER) {
16394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // This child was dragged and dropped with the UI likely
16404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // still showing.  Call updateReorderStates, to update
16414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // all UI appropriately.
16424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.clearDraggedChildId();
16434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            updateReorderStates(ReorderUtils.DRAG_STATE_NONE);
16444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mDataChanged) {
16474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Animation should only play if data has changed since populate() can be called
16484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // multiple times with the same data set (e.g., screen size changed).
16494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            handleLayoutAnimation();
16504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        recycleOffscreenViews();
16534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mPopulating = false;
16554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDataChanged = false;
16564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
16574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
16594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void scrollBy(int x, int y) {
16604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (y != 0) {
16614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: Implement smooth scrolling for this so that scrolling does more than just
16624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // jumping by y pixels.
16634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            trackMotionScroll(y, false /* over scroll */);
16644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
16664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void offsetChildren(int offset) {
16684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int childCount = getChildCount();
16694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < childCount; i++) {
16704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = getChildAt(i);
16714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.offsetTopAndBottom(offset);
16734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // As we're scrolling, we need to make sure the children that are coming into view
16754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // have their reordering area set.
16764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
16774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            setReorderingArea(lp);
16784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colCount = mColCount;
16814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < colCount; i++) {
16824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemTops[i] += offset;
16834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemBottoms[i] += offset;
16844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mScrollListener != null) {
16874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mScrollListener.onScrollChanged(offset, computeVerticalScrollOffset(),
16884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    computeVerticalScrollRange());
16894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
16904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
16914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
16924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
16934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Performs layout animation of child views.
16944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @throws IllegalStateException Exception is thrown of currently set animation mode is
16954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * not recognized.
16964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
16974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void handleLayoutAnimation() throws IllegalStateException {
16984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final List<Animator> animators = new ArrayList<Animator>();
16994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // b/8422632 - Without this dummy first animator, startDelays of subsequent animators won't
17014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // be honored correctly; all animators will block regardless of startDelay until the first
17024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // animator in the AnimatorSet truly starts playing.
17034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
17044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        anim.setDuration(0);
17054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        animators.add(anim);
17064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        addOutAnimatorsForStaleViews(animators, mAnimationOutMode);
17084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Play the In animators at a slight delay after all Out animators have started.
17104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int animationInStartDelay = animators.size() > 0 ?
17114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                (SgvAnimationHelper.getDefaultAnimationDuration() / 2) : 0;
17124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        addInAnimators(animators, mAnimationInMode, animationInStartDelay);
17134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (animators != null && animators.size() > 0) {
17154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final AnimatorSet animatorSet = new AnimatorSet();
17164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            animatorSet.playTogether(animators);
17174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            animatorSet.addListener(new AnimatorListenerAdapter() {
17184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                @Override
17194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                public void onAnimationStart(Animator animation) {
17204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mIsCurrentAnimationCanceled = false;
17214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mCurrentRunningAnimatorSet = animatorSet;
17224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
17234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                @Override
17254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                public void onAnimationCancel(Animator animation) {
17264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mIsCurrentAnimationCanceled = true;
17274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
17284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                @Override
17304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                public void onAnimationEnd(Animator animation) {
17314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (!mIsCurrentAnimationCanceled) {
17324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // If this animation ended naturally, not because it was canceled, then
17334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // reset the animation mode back to ANIMATION_MODE_NONE.  However, if
17344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // the animation was canceled by a data change, then keep the mode as is,
17354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // so that on a re-layout, we can resume animation from the views' current
17364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // positions.
17374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        resetAnimationMode();
17384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
17394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mCurrentRunningAnimatorSet = null;
17404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
17414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            });
17424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Log.v(TAG, "starting");
17444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            animatorSet.start();
17454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
17464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            resetAnimationMode();
17474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
17484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mViewsToAnimateOut.clear();
17504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mChildRectsForAnimation.clear();
17514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
17524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
17544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Reset the current animation mode.
17554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
17564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void resetAnimationMode() {
17574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mAnimationInMode = AnimationIn.NONE;
17584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mAnimationOutMode = AnimationOut.NONE;
17594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
17604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
17624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Add animators for animating in new views as well as updating positions of views that
17634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * should remain on screen.
17644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
17654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void addInAnimators(List<Animator> animators, AnimationIn animationInMode,
17664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int startDelay) {
17674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (animationInMode == AnimationIn.NONE) {
17684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
17694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
17704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch (animationInMode) {
17724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case FLY_UP_ALL_VIEWS:
17734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                addFlyInAllViewsAnimators(animators);
17744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
17754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case EXPAND_NEW_VIEWS:
17774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                addUpdateViewPositionsAnimators(animators, true /* cascade animation */,
17784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        AnimationIn.EXPAND_NEW_VIEWS, startDelay);
17794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
17804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case EXPAND_NEW_VIEWS_NO_CASCADE:
17824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                addUpdateViewPositionsAnimators(animators, false /* cascade animation */,
17834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        AnimationIn.EXPAND_NEW_VIEWS_NO_CASCADE, startDelay);
17844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
17854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case SLIDE_IN_NEW_VIEWS:
17874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                addUpdateViewPositionsAnimators(animators, true /* cascade animation */,
17884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        AnimationIn.SLIDE_IN_NEW_VIEWS, startDelay);
17894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
17904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case FLY_IN_NEW_VIEWS:
17924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                addUpdateViewPositionsAnimators(animators, true /* cascade animation */,
17934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        AnimationIn.FLY_IN_NEW_VIEWS, startDelay);
17944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
17954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
17961aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein            case FADE:
17971aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                addUpdateViewPositionsAnimators(animators, true /* cascade animation */,
17981aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                        AnimationIn.FADE, startDelay);
17991aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                break;
18001aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein
18014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            default:
18024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                throw new IllegalStateException("Unknown animationInMode: " + mAnimationInMode);
18034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
18044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
18054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
18074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Add animators for animating out stale views
18084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param animationOutMode The animation mode to play for stale views
18094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
18104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void addOutAnimatorsForStaleViews(List<Animator> animators,
18114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            AnimationOut animationOutMode) {
18124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (animationOutMode == AnimationOut.NONE) {
18134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
18144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
18154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (final View v : mViewsToAnimateOut) {
18174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // For each stale view to animate out, retrieve the animators for the view, then attach
18184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // the StaleViewAnimationEndListener which checks to see if the view should be recycled
18194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // at the end of the animation.
18204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final List<Animator> viewAnimators = new ArrayList<Animator>();
18214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            switch (animationOutMode) {
18234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case SLIDE:
18244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final LayoutParams lp = (LayoutParams) v.getLayoutParams();
18254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Bias towards sliding right, but depending on the column that this view
18264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // is laid out in, slide towards the nearest side edge.
18274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    int endTranslation = (int)(v.getWidth() * 1.5);
18284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (lp.column < (mColCount / 2)) {
18294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        endTranslation = -endTranslation;
18304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
18314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    SgvAnimationHelper.addSlideOutAnimators(viewAnimators, v,
18324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            (int) v.getTranslationX(), endTranslation);
18334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
18344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case COLLAPSE:
18364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    SgvAnimationHelper.addCollapseOutAnimators(viewAnimators, v);
18374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
18384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case FLY_DOWN:
18404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    SgvAnimationHelper.addFlyOutAnimators(viewAnimators, v,
18414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            (int) v.getTranslationY(), getHeight());
18424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
18434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case FADE:
18454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    SgvAnimationHelper.addFadeAnimators(viewAnimators, v, v.getAlpha(),
18464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            0 /* end alpha */);
18474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
18484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                default:
18504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    throw new IllegalStateException("Unknown animationOutMode: " +
18514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            animationOutMode);
18524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
18534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (viewAnimators.size() > 0) {
18554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                addStaleViewAnimationEndListener(v, viewAnimators);
18564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                animators.addAll(viewAnimators);
18574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
18584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
18594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
18604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
18624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Handle setting up the animators of child views when the animation is invoked by a change
18634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * in the adapter.  This method has a side effect of translating view positions in preparation
18644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * for the animations.
18654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
18664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private List<Animator> addFlyInAllViewsAnimators(List<Animator> animators) {
18674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int childCount = getChildCount();
18684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (childCount == 0) {
18694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return null;
18704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
18714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (animators == null) {
18734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            animators = new ArrayList<Animator>();
18744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
18754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < childCount; i++) {
18774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int animationDelay = i * ANIMATION_DELAY_IN_MS;
18784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View childToAnimate = getChildAt(i);
18794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Start all views from below the bottom of this grid and animate them upwards. This
18814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // is done simply by translating the current view's vertical position by the height
18824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // of the entire grid.
18834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float yTranslation = getHeight();
18844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float rotation = SgvAnimationHelper.ANIMATION_ROTATION_DEGREES;
18854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mIsCurrentAnimationCanceled) {
18864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If mIsAnimationCanceled is true, then this is not the first time that this
18874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // animation is running.  For this particular case, we should resume from where
18884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // the previous animation left off, rather than resetting translation and rotation.
18894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                yTranslation = childToAnimate.getTranslationY();
18904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rotation = childToAnimate.getRotation();
18914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
18924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            SgvAnimationHelper.addTranslationRotationAnimators(animators, childToAnimate,
18944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    0 /* xTranslation */, (int) yTranslation, rotation, animationDelay);
18954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
18964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
18974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return animators;
18984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
18994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
19014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Animations to update the views on screen to their new positions.  For new views that aren't
19024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * currently on screen, animate them in using the specified animationInMode.
19034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
19044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private List<Animator> addUpdateViewPositionsAnimators(List<Animator> animators,
19054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean cascadeAnimation, AnimationIn animationInMode, int startDelay) {
19064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int childCount = getChildCount();
19074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (childCount == 0) {
19084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return null;
19094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
19104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (animators == null) {
19124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            animators = new ArrayList<Animator>();
19134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
19144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int viewsAnimated = 0;
19164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < childCount; i++) {
19174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View childToAnimate = getChildAt(i);
19184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mViewsToAnimateOut.contains(childToAnimate)) {
19204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If the stale views are still animating, then they are still laid out, so
19214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // getChildCount() would've accounted for them.  Since they have their own set
19224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // of animations to play, we'll skip over them in this loop.
19234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                continue;
19244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
19254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Use progressive animation delay to create the staggered effect of animating
19274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // views.  This is done by having each view delay their animation by
19284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // ANIMATION_DELAY_IN_MS after the animation of the previous view.
19294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int animationDelay = startDelay +
19304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    (cascadeAnimation ? viewsAnimated * ANIMATION_DELAY_IN_MS : 0);
19314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Figure out whether a view with this item ID existed before
19334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) childToAnimate.getLayoutParams();
19344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final ViewRectPair viewRectPair = mChildRectsForAnimation.get(lp.id);
19364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int xTranslation;
19384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int yTranslation;
19394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If there is a valid {@link Rect} for the view with this newId, then
19414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // setup an animation.
19424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (viewRectPair != null && viewRectPair.rect != null) {
19431aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                // In the special case where the items are explicitly fading, we don't want to do
19441aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                // any of the translations.
19451aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                if (animationInMode == AnimationIn.FADE) {
19461aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                    SgvAnimationHelper.addFadeAnimators(animators, childToAnimate,
19471aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                            0 /* start alpha */, 1.0f /* end alpha */, animationDelay);
19481aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                    continue;
19491aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                }
19501aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein
19514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final Rect oldRect = viewRectPair.rect;
19524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Since the view already exists, translate it to its new position.
19534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Reset the child back to its previous position given by oldRect if the child
19544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // has not already been translated.  If the child has been translated, use the
19554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // current translated values, as this child may be in the middle of a previous
19564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // animation, so we don't want to simply force it to new location.
19574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                xTranslation = oldRect.left - childToAnimate.getLeft();
19594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                yTranslation = oldRect.top - childToAnimate.getTop();
19604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float rotation = childToAnimate.getRotation();
19614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // First set the translation X and Y. The current translation might be out of date.
19634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childToAnimate.setTranslationX(xTranslation);
19644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childToAnimate.setTranslationY(yTranslation);
19654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (xTranslation == 0 && yTranslation == 0 && rotation == 0) {
19674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Bail early if this view doesn't need to be translated.
19684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    continue;
19694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
19704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                SgvAnimationHelper.addTranslationRotationAnimators(animators, childToAnimate,
19724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        xTranslation, yTranslation, rotation, animationDelay);
19734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
19744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If this view was not present before the data updated, rather than just flashing
19754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // the view into its designated position, fly it up from the bottom.
19764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                xTranslation = 0;
19774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                yTranslation = (animationInMode == AnimationIn.FLY_IN_NEW_VIEWS) ? getHeight() : 0;
19784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Since this is a new view coming in, add additional delays so that these IN
19804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // animations start after all the OUT animations have been played.
19814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                animationDelay += SgvAnimationHelper.getDefaultAnimationDuration();
19824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childToAnimate.setTranslationX(xTranslation);
19844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childToAnimate.setTranslationY(yTranslation);
19854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                switch (animationInMode) {
19874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    case FLY_IN_NEW_VIEWS:
19884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        SgvAnimationHelper.addTranslationRotationAnimators(animators,
19894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                childToAnimate, xTranslation, yTranslation,
19904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                SgvAnimationHelper.ANIMATION_ROTATION_DEGREES, animationDelay);
19914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        break;
19924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
19934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    case SLIDE_IN_NEW_VIEWS:
19944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // Bias towards sliding right, but depending on the column that this view
19954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // is laid out in, slide towards the nearest side edge.
19964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        int startTranslation = (int)(childToAnimate.getWidth() * 1.5);
19974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        if (lp.column < (mColCount / 2)) {
19984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            startTranslation = -startTranslation;
19994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        }
20004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        SgvAnimationHelper.addSlideInFromRightAnimators(animators,
20024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                childToAnimate, startTranslation,
20034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                animationDelay);
20044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        break;
20054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    case EXPAND_NEW_VIEWS:
20074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    case EXPAND_NEW_VIEWS_NO_CASCADE:
20084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        if (i == 0) {
20094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            // Initially set the alpha of this view to be invisible, then fade in.
20104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            childToAnimate.setAlpha(0);
20114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            // Create animators that translate the view back to translation = 0
20134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            // which would be its new layout position
20144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            final int offset = -1 * childToAnimate.getHeight();
20154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            SgvAnimationHelper.addXYTranslationAnimators(animators,
20164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                    childToAnimate, 0 /* xTranslation */, offset, animationDelay);
20174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            SgvAnimationHelper.addFadeAnimators(animators, childToAnimate,
20194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                    0 /* start alpha */, 1.0f /* end alpha */, animationDelay);
20204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        } else {
20214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            SgvAnimationHelper.addExpandInAnimators(animators,
20224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                                    childToAnimate, animationDelay);
20234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        }
20244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        break;
20251aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                    case FADE:
20261aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                        SgvAnimationHelper.addFadeAnimators(animators, childToAnimate,
20271aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                                0 /* start alpha */, 1.0f /* end alpha */, animationDelay);
20281aa24a276dd7aa4f92f6e443bcd8279cf70d14a8Sam Blitzstein                        break;
20294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    default:
20314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        continue;
20324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
20334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
20344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            viewsAnimated++;
20364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
20374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return animators;
20394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
20404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void addStaleViewAnimationEndListener(final View view, List<Animator> viewAnimators) {
20424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (viewAnimators == null) {
20434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
20444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
20454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (final Animator animator : viewAnimators) {
20474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            animator.addListener(new AnimatorListenerAdapter() {
20484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                @Override
20494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                public void onAnimationEnd(Animator animation) {
20504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // In the event that onChanged is called before this animation finishes,
20514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // we would have mistakenly cached a view that would be recycled.  So
20524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // check if it's there, and remove it so that obtainView() doesn't
20534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // accidentally use the cached view later when it's already been
20544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // moved to the recycler.
20554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final LayoutParams lp = (LayoutParams) view.getLayoutParams();
20564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mChildRectsForAnimation.containsKey(lp.id)) {
20574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mChildRectsForAnimation.remove(lp.id);
20584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
20594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    recycleView(view);
20614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
20624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            });
20634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
20644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
20654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
20674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Calculate and cache the {@link LayoutRecord}s for all positions up to mFirstPosition.
20684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * mFirstPosition is the position that layout will start from, but we need to know where all
20694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * views preceding it will be laid out so that mFirstPosition will be laid out at the correct
20704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * position.  If this is not done, mFirstPosition will be laid out at the first empty space
20714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * possible (i.e., top left), and this may not be the correct position in the overall layout.
20724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
20734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * This can be optimized if we don't need to guard against jagged edges in the grid or if
20744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * mFirstChangedPosition is set to a non-zero value (so we can skip calculating some views).
20754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
20764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void calculateLayoutStartOffsets(int offset) {
20774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Bail early if we don't guard against jagged edges or if nothing has changed before
20784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // mFirstPosition.
20794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Also check that we're not at the top of the list because sometimes grid padding isn't set
20804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // until after mItemTops and mItemBottoms arrays have been initialized, so we should
20814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // go through and compute the right layout start offset for mFirstPosition = 0.
20824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mFirstPosition != 0 &&
20834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                (!mGuardAgainstJaggedEdges || mFirstPosition < mFirstChangedPosition)) {
20844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // At this time, we know that mItemTops should be the same, because
20854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // nothing has changed before view at mFirstPosition. The only thing
20864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // we need to do is to reset mItemBottoms. The result should be the
20874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // same, if we don't bail early and execute the following code
20884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // again. Notice that mItemBottoms always equal to mItemTops after
20894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // this method.
20904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            System.arraycopy(mItemTops, 0, mItemBottoms, 0, mColCount);
20914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
20924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
20934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colWidth = (getWidth() - getPaddingLeft() - getPaddingRight() -
20954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemMargin * (mColCount - 1)) / mColCount;
20964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
20974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        Arrays.fill(mItemTops, getPaddingTop());
20984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        Arrays.fill(mItemBottoms, getPaddingTop());
20994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Since we will be doing a pass to calculate all views up to mFirstPosition, it is likely
21014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // that all existing {@link LayoutRecord}s will be stale, so clear it out to avoid
21024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // accidentally the re-use of stale values.
21034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        //
21044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Note: We cannot just invalidate all layout records after mFirstPosition because it is
21054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // possible that this layout pass is caused by a down sync from the server that may affect
21064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // the layout of views from position 0 to mFirstPosition - 1.
21074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mDataChanged) {
21084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mLayoutRecords.clear();
21094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
21104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < mFirstPosition; i++) {
21124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            LayoutRecord rec = mLayoutRecords.get(i);
21134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mDataChanged || rec == null) {
21154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final View view = obtainView(i, null);
21164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
21174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int heightSpec;
21194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (lp.height == LayoutParams.WRAP_CONTENT) {
21204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
21214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
21224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
21234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
21244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int span = Math.min(mColCount, lp.span);
21264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int widthSize = colWidth * span + mItemMargin * (span - 1);
21274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
21284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                view.measure(widthSpec, heightSpec);
21304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int height = view.getMeasuredHeight();
21314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (rec == null) {
21334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    rec = new LayoutRecord();
21344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mLayoutRecords.put(i, rec);
21354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
21364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.height = height;
21384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.id = lp.id;
21394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.span = span;
21404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // We're not actually using this view, so add this back to the recycler.
21424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mRecycler.addScrap(view);
21434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
21444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int nextColumn = getNextColumnDown();
21464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Given the span, check if there's enough space to put this view at this column.
21484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // IMPORTANT Use the same logic in {@link #layoutChildren}.
21494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rec.span > 1) {
21504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mIsRtlLayout) {
21514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (nextColumn + 1 < rec.span) {
21524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        nextColumn = mColCount - 1;
21534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
21544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
21554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mColCount - nextColumn < rec.span) {
21564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        nextColumn = 0;
21574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
21584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
21594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
21604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.column = nextColumn;
21614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Place the top of this child beneath the last by finding the lowest coordinate across
21634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // the columns that this child will span.  For LTR layout, we scan across from left to
21644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // right, and for RTL layout, we scan from right to left.
21654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: Consolidate this logic with getNextRecordDown() in the future, as that method
21664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // already calculates the margins for us.  This will keep the implementation consistent
21674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // with layoutChildren(), fillUp() and fillDown().
21684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int lowest = mItemBottoms[nextColumn] + mItemMargin;
21694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rec.span > 1) {
21704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int spanIndex = 0; spanIndex < rec.span; spanIndex++) {
21714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int index = mIsRtlLayout ? nextColumn - spanIndex :
21724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            nextColumn + spanIndex;
21734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int bottom = mItemBottoms[index] + mItemMargin;
21744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (bottom > lowest) {
21754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        lowest = bottom;
21764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
21774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
21784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
21794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int spanIndex = 0; spanIndex < rec.span; spanIndex++) {
21814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int col = mIsRtlLayout ? nextColumn - spanIndex : nextColumn + spanIndex;
21824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemBottoms[col] = lowest + rec.height;
21834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (Log.isLoggable(TAG, Log.VERBOSE)) {
21854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    Log.v(TAG, " position: " + i + " bottoms: ");
21864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    for (int j = 0; j < mColCount; j++) {
21874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        Log.v(TAG, "    mItemBottoms["+j+"]: " + mItemBottoms[j]);
21884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
21894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
21904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
21914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
21924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
21934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // mItemBottoms[] at this point contains the values of all views up to mFirstPosition.  To
21944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // figure out where view at mFirstPosition will be laid out, we'll need to find the column
21954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // that is the highest (i.e., i where mItemBottoms[i] <= mItemBottoms[j] for all j
21964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // from 0 to mColCount.)
21974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int highestValue = Integer.MAX_VALUE;
21984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int k = 0; k < mColCount; k++) {
21994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mItemBottoms[k] < highestValue) {
22004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                highestValue = mItemBottoms[k];
22014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
22024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
22034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Adjust the offsets in each column so that values in mItemTops[] and mItemBottoms[]
22054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // reflect coordinates on screen.  These offsets will be the actual values where layout
22064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // will start from, otherwise, we'd naively start at (leftPadding, topPadding) for
22074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // mFirstPosition.
22084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int k = 0; k < mColCount; k++) {
22094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemBottoms[k] = mItemBottoms[k] - highestValue + offset;
22104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemTops[k] = mItemBottoms[k];
22114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2212d73459eee307459d478983e00b2a4084c6e8273bIsaac Katzenelson            // Log.v(TAG, "Adjusting to offset = mItemBottoms[" + k + "]: " + mItemBottoms[k]);
22134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
22144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
22154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
22174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Measure and layout all currently visible children.
22184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
22194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param queryAdapter true to requery the adapter for view data
22204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
22214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final void layoutChildren(boolean queryAdapter) {
22224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingLeft = getPaddingLeft();
22234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingRight = getPaddingRight();
22244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int itemMargin = mItemMargin;
22254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int availableWidth = (getWidth() - paddingLeft - paddingRight - itemMargin
22264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                * (mColCount - 1));
22274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colWidth = availableWidth / mColCount;
22284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // The availableWidth may not be divisible by mColCount. Keep the
22294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // remainder. It will be added to the width of the last view in the row.
22304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int remainder = availableWidth % mColCount;
22314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean viewsRemovedInLayout = false;
22334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // If we're animating out stale views, then we want to defer recycling of views.
22354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean deferRecyclingForAnimation = mAnimationOutMode != AnimationOut.NONE;
22364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (!deferRecyclingForAnimation) {
22384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childCount = getChildCount();
22394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If the latest data set has fewer data items than mFirstPosition, don't keep any
22404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // views on screen, and just let the layout logic below retrieve appropriate views
22414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // from the recycler.
22424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int viewsToKeepOnScreen = (mItemCount <= mFirstPosition) ? 0 :
22434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemCount - mFirstPosition;
22444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (childCount > viewsToKeepOnScreen) {
22464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If there are more views laid out than the number of data items remaining to be
22474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // laid out, recycle the extraneous views.
22484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                recycleViewsInRange(viewsToKeepOnScreen, childCount - 1);
22494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                viewsRemovedInLayout = true;
22504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
22514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
22524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mViewsToAnimateOut.clear();
22534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
22544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < getChildCount(); i++) {
22564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int position = mFirstPosition + i;
22574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            View child = getChildAt(i);
22584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int highestAvailableLayoutPosition = mItemBottoms[getNextColumnDown()];
22604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (deferRecyclingForAnimation &&
22614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    (position >= mItemCount || highestAvailableLayoutPosition >= getHeight())) {
22624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // For the remainder of views on screen, they should not be on screen, so we can
22634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // skip layout.  Add them to the list of views to animate out.
22644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // We should only get in this position if deferRecyclingForAnimation = true,
22654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // otherwise, we should've recycled all views before getting into this layout loop.
22664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mViewsToAnimateOut.add(child);
22674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                continue;
22684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
22694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            LayoutParams lp = null;
22714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int col = -1;
22724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (child != null) {
22744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lp = (LayoutParams) child.getLayoutParams();
22754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                col = lp.column;
22764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
22774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final boolean needsLayout = queryAdapter || child == null || child.isLayoutRequested();
22794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (queryAdapter) {
22804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                View newView = null;
22814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (deferRecyclingForAnimation) {
22824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // If we are deferring recycling for animation, then we don't want to pass the
22834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // current child in to obtainView for re-use.  obtainView() in this case should
22844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // try to find the view belonging to this item on screen, or populate a fresh
22854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // one from the recycler.
22864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    newView = obtainView(position);
22874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
22884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    newView = obtainView(position, child);
22894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
22904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Update layout params since they may have changed
22924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lp = (LayoutParams) newView.getLayoutParams();
22934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
22944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (newView != child) {
22954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (child != null && !deferRecyclingForAnimation) {
22964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mRecycler.addScrap(child);
22974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        removeViewInLayout(child);
22984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        viewsRemovedInLayout = true;
22994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
23004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // If this view is already in the layout hierarchy, we can just detach it
23024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // from the parent and re-attach it at the correct index.  If the view has
23034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // already been removed from the layout hierarchy, getParent() == null.
23044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (newView.getParent() == this) {
23054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        detachViewFromParent(newView);
23064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        attachViewToParent(newView, i, lp);
23074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    } else {
23084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        addViewInLayout(newView, i, lp);
23094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
23104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
23114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                child = newView;
23134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Since the data has changed, we need to make sure the next child is in the
23154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // right column. We choose the next column down (vs. next column up) because we
23164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // are filling from the top of the screen downwards as we iterate through
23174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // visible children. (We take span into account below.)
23184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lp.column = getNextColumnDown();
23194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                col = lp.column;
23204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
23214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            setReorderingArea(lp);
23234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int span = Math.min(mColCount, lp.span);
23254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Given the span, check if there's enough space to put this view at this column.
23274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // IMPORTANT Propagate the same logic to {@link #calculateLayoutStartOffsets}.
23284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (span > 1) {
23294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mIsRtlLayout) {
23304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // For RTL layout, if the current column index is less than the span of the
23314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // child, then we know that there is not enough room remaining to lay this
23324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // child out (e.g., if col == 0, but span == 2, then laying this child down
23334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // at column = col would put us out of bound into a negative column index.).
23344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // For this scenario, reset the index back to the right-most column, and lay
23354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // out the child at this position where we can ensure that we can display as
23364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // much of the child as possible.
23374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (col + 1 < span) {
23384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        col = mColCount - 1;
23394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
23404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
23414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mColCount - col < span) {
23424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // If not, reset the col to 0.
23434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        col = 0;
23444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
23454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
23464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lp.column = col;
23484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
23494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int widthSize = (colWidth * span + itemMargin * (span - 1));
23514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If it is rtl, we layout the view from col to col - span +
23524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // 1. If it reaches the most left column, i.e. we added the
23534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // additional width. So the check it span == col +1
23544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if ((mIsRtlLayout && span == col + 1)
23554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    || (!mIsRtlLayout && span + col == mColCount)) {
23564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                widthSize += remainder;
23574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
23584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (needsLayout) {
23594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
23604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int heightSpec;
23624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (lp.height == LayoutParams.WRAP_CONTENT) {
23634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
23644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
23654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
23664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
23674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                child.measure(widthSpec, heightSpec);
23694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
23704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Place the top of this child beneath the last by finding the lowest coordinate across
23724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // the columns that this child will span.  For LTR layout, we scan across from left to
23734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // right, and for RTL layout, we scan from right to left.
23744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO:  Consolidate this logic with getNextRecordDown() in the future, as that method
23754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // already calculates the margins for us.  This will keep the implementation consistent
23764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // with fillUp() and fillDown().
23774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childTop = mItemBottoms[col] + mItemMargin;
23784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (span > 1) {
23794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int lowest = childTop;
23804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int spanIndex = 0; spanIndex < span; spanIndex++) {
23814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int index = mIsRtlLayout ? col - spanIndex : col + spanIndex;
23824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int bottom = mItemBottoms[index] + mItemMargin;
23834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (bottom > lowest) {
23844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        lowest = bottom;
23854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
23864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
23874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childTop = lowest;
23894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
23904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
23914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childHeight = child.getMeasuredHeight();
23924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childBottom = childTop + childHeight;
23934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childLeft = 0;
23944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childRight = 0;
23954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mIsRtlLayout) {
23964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childRight = (getWidth() - paddingRight) -
23974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        (mColCount - col - 1) * (colWidth + itemMargin);
23984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childLeft = childRight - child.getMeasuredWidth();
23994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
24004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childLeft = paddingLeft + col * (colWidth + itemMargin);
24014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childRight = childLeft + child.getMeasuredWidth();
24024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
24034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2404d73459eee307459d478983e00b2a4084c6e8273bIsaac Katzenelson        /*    Log.v(TAG, "[layoutChildren] height: " + childHeight
24054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " top: " + childTop + " bottom: " + childBottom
24064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " left: " + childLeft
24074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " column: " + col
24084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " position: " + position
24094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " id: " + lp.id);
2410d73459eee307459d478983e00b2a4084c6e8273bIsaac Katzenelson*/
24114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.layout(childLeft, childTop, childRight, childBottom);
24124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp.id == mFocusedChildIdToScrollIntoView) {
24134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                child.requestFocus();
24144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
24154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int spanIndex = 0; spanIndex < span; spanIndex++) {
24174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = mIsRtlLayout ? col - spanIndex : col + spanIndex;
24184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemBottoms[index] = childBottom;
24194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
24204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Whether or not LayoutRecords may have already existed for the view at this position
24224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // on screen, we'll update it after we lay out to ensure that the LayoutRecord
24234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // has the most updated information about the view at this position.  We can be assured
24244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // that all views before those on screen (views with adapter position < mFirstPosition)
24254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // have the correct LayoutRecords because calculateLayoutStartOffsets() would have
24264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // set them appropriately.
24274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            LayoutRecord rec = mLayoutRecords.get(position);
24284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rec == null) {
24294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = new LayoutRecord();
24304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mLayoutRecords.put(position, rec);
24314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
24324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.column = lp.column;
24344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.height = childHeight;
24354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.id = lp.id;
24364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.span = span;
24374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
24384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // It appears that removeViewInLayout() does not invalidate.  So if we make use of this
24404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // method during layout, we should invalidate explicitly.
24414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (viewsRemovedInLayout || deferRecyclingForAnimation) {
24424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            invalidate();
24434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
24444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
24454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
24474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Set the reordering area for the child layout specified
24484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
24494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void setReorderingArea(LayoutParams childLayoutParams) {
24504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean isLastColumn = childLayoutParams.column == (mColCount - 1);
24514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        childLayoutParams.reorderingArea =
24524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mAdapter.getReorderingArea(childLayoutParams.position, isLastColumn);
24534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
24544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final void invalidateLayoutRecordsBeforePosition(int position) {
24564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int endAt = 0;
24574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        while (endAt < mLayoutRecords.size() && mLayoutRecords.keyAt(endAt) < position) {
24584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            endAt++;
24594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
24604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mLayoutRecords.removeAtRange(0, endAt);
24614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
24624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final void invalidateLayoutRecordsAfterPosition(int position) {
24644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int beginAt = mLayoutRecords.size() - 1;
24654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        while (beginAt >= 0 && mLayoutRecords.keyAt(beginAt) > position) {
24664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            beginAt--;
24674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
24684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        beginAt++;
24694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mLayoutRecords.removeAtRange(beginAt + 1, mLayoutRecords.size() - beginAt);
24704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
24714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
24734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Before doing an animation, map the item IDs for the currently visible children to the
24744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link Rect} that defines their position on the screen so a translation animation
24754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * can be applied to their new layout positions.
24764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
24774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void cacheChildRects() {
24784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int childCount = getChildCount();
24794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mChildRectsForAnimation.clear();
24804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        long originalDraggedChildId = -1;
24824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isDragReorderingSupported()) {
24834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            originalDraggedChildId = mReorderHelper.getDraggedChildId();
24844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mCachedDragViewRect != null && originalDraggedChildId != -1) {
24854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // This child was dragged in a reordering operation.  Use the cached position
24864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // of where the drag event was released as the cached location.
24874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mChildRectsForAnimation.put(originalDraggedChildId,
24884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        new ViewRectPair(mDragView, mCachedDragViewRect));
24894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mCachedDragViewRect = null;
24904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
24914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
24924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < childCount; i++) {
24944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = getChildAt(i);
24954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
24964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
24974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Rect rect;
24984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp.id != originalDraggedChildId) {
24994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int childTop = (int) child.getY();
25004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int childBottom = childTop + child.getHeight();
25014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int childLeft = (int) child.getX();
25024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int childRight = childLeft + child.getWidth();
25034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rect = new Rect(childLeft, childTop, childRight, childBottom);
25044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mChildRectsForAnimation.put(lp.id /* item id */, new ViewRectPair(child, rect));
25054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
25074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
25084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
25104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Should be called with mPopulating set to true
25114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
25124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param fromPosition Position to start filling from
25134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param overhang the number of extra pixels to fill beyond the current top edge
25144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return the max overhang beyond the beginning of the view of any added items at the top
25154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
25164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final int fillUp(int fromPosition, int overhang) {
25174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingLeft = getPaddingLeft();
25184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingRight = getPaddingRight();
25194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int itemMargin = mItemMargin;
25204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int availableWidth = (getWidth() - paddingLeft - paddingRight - itemMargin
25214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                * (mColCount - 1));
25224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colWidth = availableWidth / mColCount;
25234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // The availableWidth may not be divisible by mColCount. Keep the
25244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // remainder. It will be added to the width of the last view in the row.
25254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int remainder = availableWidth % mColCount;
25264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int gridTop = getPaddingTop();
25274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int fillTo = -overhang;
25284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int nextCol = getNextColumnUp();
25294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int position = fromPosition;
25304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        while (nextCol >= 0 && mItemTops[nextCol] > fillTo && position >= 0) {
25324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = obtainView(position, null);
25334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
25344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (child.getParent() != this) {
25364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mInLayout) {
25374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    addViewInLayout(child, 0, lp);
25384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
25394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    addView(child, 0);
25404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
25414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int span = Math.min(mColCount, lp.span);
25444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            LayoutRecord rec;
25464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (span > 1) {
25474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = getNextRecordUp(position, span);
25484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                nextCol = rec.column;
25494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
25504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = mLayoutRecords.get(position);
25514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean invalidateBefore = false;
25544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rec == null) {
25554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = new LayoutRecord();
25564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mLayoutRecords.put(position, rec);
25574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.column = nextCol;
25584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.span = span;
25594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else if (span != rec.span) {
25604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.span = span;
25614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.column = nextCol;
25624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                invalidateBefore = true;
25634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
25644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                nextCol = rec.column;
25654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mHasStableIds) {
25684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.id = lp.id;
25694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            lp.column = nextCol;
25724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            setReorderingArea(lp);
25734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int widthSize = colWidth * span + itemMargin * (span - 1);
25754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If it is rtl, we layout the view from nextCol to nextCol - span +
25764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // 1. If it reaches the most left column, i.e. we added the
25774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // additional width. So the check it span == nextCol + 1
25784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if ((mIsRtlLayout && span == nextCol + 1)
25794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    || (!mIsRtlLayout && span + nextCol == mColCount)) {
25804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                widthSize += remainder;
25814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
25834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int heightSpec;
25844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp.height == LayoutParams.WRAP_CONTENT) {
25854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
25864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
25874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
25884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.measure(widthSpec, heightSpec);
25904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childHeight = child.getMeasuredHeight();
25924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (invalidateBefore || (childHeight != rec.height && rec.height > 0)) {
25934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                invalidateLayoutRecordsBeforePosition(position);
25944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
25954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.height = childHeight;
25964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
25974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Iterate across each column that this child spans and add the margin calculated
25984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // for that column to mItemTops.  getMarginBelow() is expected to give us the correct
25994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // margin values at each column such that mItemTops ends up with a smooth edge across
26004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // the column spans.  We need to do this before actually laying down the child,
26014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // otherwise we risk overlapping one child over another.  mItemTops stores the top
26024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // index for where the next child should be laid out.  For RTL, we do the update
26034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // in reverse order.
26044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < span; i++) {
26054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = mIsRtlLayout ? nextCol - i : nextCol + i;
26064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemTops[index] += rec.getMarginBelow(i);
26074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
26084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int startFrom = mItemTops[nextCol];
26104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childBottom = startFrom;
26114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childTop = childBottom - childHeight;
26124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childLeft = 0;
26144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childRight = 0;
26154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // For LTR layout, the child's left is calculated as the
26164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // (column index from left) * (columnWidth plus item margins).
26174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // For RTL layout, the child's left is relative to its right, and its right coordinate
26184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // is calculated as the difference between the width of this grid and
26194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // (column index from right) * (columnWidth plus item margins).
26204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mIsRtlLayout) {
26214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childRight = (getWidth() - paddingRight) -
26224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        (mColCount - nextCol - 1) * (colWidth + itemMargin);
26234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childLeft = childRight - child.getMeasuredWidth();
26244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
26254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childLeft = paddingLeft + nextCol * (colWidth + itemMargin);
26264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childRight = childLeft + child.getMeasuredWidth();
26274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
26284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.layout(childLeft, childTop, childRight, childBottom);
26294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Log.v(TAG, "[fillUp] position: " + position + " id: " + lp.id
26314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " childLeft: " + childLeft + " childTop: " + childTop
26324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " column: " + rec.column + " childHeight:" + childHeight);
26334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Since we're filling up, once the child is laid out, update mItemTops again
26354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // to reflect the next available top value at this column.  This is simply the child's
26364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // top coordinates, minus any available margins set.  For LTR, we start at the column
26374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // that this child is laid out from (nextCol) and move right for span amount.  For RTL
26384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // layout, we start at the column that this child is laid out from and move left.
26394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < span; i++) {
26404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = mIsRtlLayout ? nextCol - i : nextCol + i;
26414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemTops[index] = childTop - rec.getMarginAbove(i) - itemMargin;
26424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
26434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp.id == mFocusedChildIdToScrollIntoView) {
26454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                child.requestFocus();
26464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
26474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            nextCol = getNextColumnUp();
26494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFirstPosition = position--;
26504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
26514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int highestView = getHeight();
26534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < mColCount; i++) {
26544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mItemTops[i] < highestView) {
26554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                highestView = mItemTops[i];
26564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
26574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
26584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return gridTop - highestView;
26594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
26604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
26624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Should be called with mPopulating set to true
26634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
26644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param fromPosition Position to start filling from
26654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param overhang the number of extra pixels to fill beyond the current bottom edge
26664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return the max overhang beyond the end of the view of any added items at the bottom
26674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
26684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final int fillDown(int fromPosition, int overhang) {
26694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingLeft = getPaddingLeft();
26704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingRight = getPaddingRight();
26714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int itemMargin = mItemMargin;
26724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int availableWidth = (getWidth() - paddingLeft - paddingRight - itemMargin
26734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                * (mColCount - 1));
26744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colWidth = availableWidth / mColCount;
26754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // The availableWidth may not be divisible by mColCount. Keep the
26764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // remainder. It will be added to the width of the last view in the row.
26774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int remainder = availableWidth % mColCount;
26784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int gridBottom = getHeight() - getPaddingBottom();
26794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int fillTo = gridBottom + overhang;
26804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int nextCol = getNextColumnDown();
26814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int position = fromPosition;
26824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        while (nextCol >= 0 && mItemBottoms[nextCol] < fillTo && position < mItemCount) {
26844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View child = obtainView(position, null);
26854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
26864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (child.getParent() != this) {
26874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mInLayout) {
26884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    addViewInLayout(child, -1, lp);
26894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
26904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    addView(child);
26914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
26924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
26934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int span = Math.min(mColCount, lp.span);
26954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
26964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            LayoutRecord rec;
26974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (span > 1) {
26984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = getNextRecordDown(position, span);
26994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                nextCol = rec.column;
27004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
27014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = mLayoutRecords.get(position);
27024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean invalidateAfter = false;
27054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rec == null) {
27064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec = new LayoutRecord();
27074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mLayoutRecords.put(position, rec);
27084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.column = nextCol;
27094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.span = span;
27104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else if (span != rec.span) {
27114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.span = span;
27124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.column = nextCol;
27134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                invalidateAfter = true;
27144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
27154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                nextCol = rec.column;
27164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mHasStableIds) {
27194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                rec.id = lp.id;
27204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            lp.column = nextCol;
27234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            setReorderingArea(lp);
27244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int widthSize = colWidth * span + itemMargin * (span - 1);
27274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If it is rtl, we layout the view from nextCol to nextCol - span +
27284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // 1. If it reaches the most left column, i.e. we added the
27294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // additional width. So the check it span == nextCol +1
27304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if ((mIsRtlLayout && span == nextCol + 1)
27314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    || (!mIsRtlLayout && span + nextCol == mColCount)) {
27324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                widthSize += remainder;
27334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
27354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int heightSpec;
27364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp.height == LayoutParams.WRAP_CONTENT) {
27374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
27384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
27394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
27404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.measure(widthSpec, heightSpec);
27424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childHeight = child.getMeasuredHeight();
27444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (invalidateAfter || (childHeight != rec.height && rec.height > 0)) {
27454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                invalidateLayoutRecordsAfterPosition(position);
27464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.height = childHeight;
27494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Before laying out the child, we need to make sure mItemBottoms is updated with the
27514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // correct values such that there is a smooth edge across the child's span.
27524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // getMarginAbove() is expected to give us these values.  For LTR layout, we start at
27534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // nextCol, and update forward for the number of columns this child spans.  For RTL
27544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // layout, we start at nextCol and update backwards for the same number of columns.
27554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < span; i++) {
27564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = mIsRtlLayout ? nextCol - i : nextCol + i;
27574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemBottoms[index] += rec.getMarginAbove(i);
27584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int startFrom = mItemBottoms[nextCol];
27614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childTop = startFrom + itemMargin;
27624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childBottom = childTop + childHeight;
27634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childLeft = 0;
27644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int childRight = 0;
27654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mIsRtlLayout) {
27664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childRight = (getWidth() - paddingRight) -
27674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        (mColCount - nextCol - 1) * (colWidth + itemMargin);
27684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childLeft = childRight - child.getMeasuredWidth();
27694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
27704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childLeft = paddingLeft + nextCol * (colWidth + itemMargin);
27714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                childRight = childLeft + child.getMeasuredWidth();
27724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            Log.v(TAG, "[fillDown] position: " + position + " id: " + lp.id
27754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " childLeft: " + childLeft + " childTop: " + childTop
27764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    + " column: " + rec.column + " childHeight:" + childHeight);
27774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            child.layout(childLeft, childTop, childRight, childBottom);
27794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Once we've laid down the child, update mItemBottoms again to reflect the next
27814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // available set of bottom values for the next child.
27824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < span; i++) {
27834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int index = mIsRtlLayout ? nextCol - i : nextCol + i;
27844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemBottoms[index] = childBottom + rec.getMarginBelow(i);
27854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp.id == mFocusedChildIdToScrollIntoView) {
27884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                child.requestFocus();
27894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
27904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            nextCol = getNextColumnDown();
27924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            position++;
27934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
27944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
27954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int lowestView = 0;
27964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < mColCount; i++) {
27974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int index = mIsRtlLayout ? mColCount - (i + 1) : i;
27984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mItemBottoms[index] > lowestView) {
27994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lowestView = mItemBottoms[index];
28004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
28014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
28024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return lowestView - gridBottom;
28044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
28054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
28074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return column that the next view filling upwards should occupy. This is the bottom-most
28084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *         position available for a single-column item.
28094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
28104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final int getNextColumnUp() {
28114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int result = -1;
28124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int bottomMost = Integer.MIN_VALUE;
28134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colCount = mColCount;
28154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = colCount - 1; i >= 0; i--) {
28164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int index = mIsRtlLayout ? colCount - (i + 1) : i;
28174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int top = mItemTops[index];
28184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (top > bottomMost) {
28194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                bottomMost = top;
28204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                result = index;
28214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
28224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
28234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return result;
28254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
28264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
28284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Return a LayoutRecord for the given position
28294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param position
28304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param span
28314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return
28324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
28334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final LayoutRecord getNextRecordUp(int position, int span) {
28344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        LayoutRecord rec = mLayoutRecords.get(position);
28354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (rec == null || rec.span != span) {
28364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (span > mColCount) {
28374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                throw new IllegalStateException("Span larger than column count! Span:" + span
28384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        + " ColumnCount:" + mColCount);
28394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
28404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec = new LayoutRecord();
28414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.span = span;
28424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mLayoutRecords.put(position, rec);
28434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
28444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int targetCol = -1;
28454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int bottomMost = Integer.MIN_VALUE;
28464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // For LTR layout, we start from the bottom-right corner upwards when we need to find the
28484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // NextRecordUp.  For RTL, we will start from bottom-left.
28494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colCount = mColCount;
28504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mIsRtlLayout) {
28514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = span - 1; i < colCount; i++) {
28524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int top = Integer.MAX_VALUE;
28534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int j = i; j > i - span; j--) {
28544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int singleTop = mItemTops[j];
28554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (singleTop < top) {
28564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        top = singleTop;
28574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
28584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
28594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (top > bottomMost) {
28604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    bottomMost = top;
28614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    targetCol = i;
28624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
28634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
28644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
28654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = colCount - span; i >= 0; i--) {
28664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int top = Integer.MAX_VALUE;
28674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int j = i; j < i + span; j++) {
28684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int singleTop = mItemTops[j];
28694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (singleTop < top) {
28704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        top = singleTop;
28714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
28724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
28734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (top > bottomMost) {
28744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    bottomMost = top;
28754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    targetCol = i;
28764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
28774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
28784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
28794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rec.column = targetCol;
28814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Once we've found the target column for the view at this position, we update mItemTops
28834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // for all columns that this view will occupy.  We set the margin such that mItemTops is
28844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // equal for all columns in the view's span.  For LTR layout, we start at targetCol and
28854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // move right, and for RTL, we start at targetCol and move left.
28864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < span; i++) {
28874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int nextCol = mIsRtlLayout ? targetCol - i : targetCol + i;
28884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.setMarginBelow(i, mItemTops[nextCol] - bottomMost);
28894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
28904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return rec;
28924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
28934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
28944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
28954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return column that the next view filling downwards should occupy. This is the top-most
28964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *         position available.
28974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
28984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final int getNextColumnDown() {
28994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int topMost = Integer.MAX_VALUE;
29004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int result = 0;
29014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colCount = mColCount;
29024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < colCount; i++) {
29044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int index = mIsRtlLayout ? colCount - (i + 1) : i;
29054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int bottom = mItemBottoms[index];
29064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (bottom < topMost) {
29074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                topMost = bottom;
29084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                result = index;
29094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
29104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
29114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return result;
29134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
29144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final LayoutRecord getNextRecordDown(int position, int span) {
29164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        LayoutRecord rec = mLayoutRecords.get(position);
29174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (rec == null || rec.span != span) {
29184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (span > mColCount) {
29194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                throw new IllegalStateException("Span larger than column count! Span:" + span
29204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        + " ColumnCount:" + mColCount);
29214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
29224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec = new LayoutRecord();
29244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.span = span;
29254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mLayoutRecords.put(position, rec);
29264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
29274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int targetCol = -1;
29294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int topMost = Integer.MAX_VALUE;
29304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colCount = mColCount;
29324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // For LTR layout, we start from the top-left corner and move right-downwards, when we
29344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // need to find the NextRecordDown.  For RTL we will start from Top-Right corner, and move
29354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // left-downwards.
29364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mIsRtlLayout) {
29374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = colCount - 1; i >= span - 1; i--) {
29384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int bottom = Integer.MIN_VALUE;
29394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int j = i; j > i - span; j--) {
29404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int singleBottom = mItemBottoms[j];
29414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (singleBottom > bottom) {
29424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        bottom = singleBottom;
29434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
29444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
29454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (bottom < topMost) {
29464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    topMost = bottom;
29474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    targetCol = i;
29484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
29494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
29504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
29514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i <= colCount - span; i++) {
29524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int bottom = Integer.MIN_VALUE;
29534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int j = i; j < i + span; j++) {
29544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int singleBottom = mItemBottoms[j];
29554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (singleBottom > bottom) {
29564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        bottom = singleBottom;
29574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
29584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
29594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (bottom < topMost) {
29604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    topMost = bottom;
29614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    targetCol = i;
29624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
29634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
29644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
29654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rec.column = targetCol;
29674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Once we've found the target column for the view at this position, we update mItemBottoms
29694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // for all columns that this view will occupy.  We set the margins such that mItemBottoms
29704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // is equal for all columns in the view's span.  For LTR layout, we start at targetCol and
29714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // move right, and for RTL, we start at targetCol and move left.
29724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < span; i++) {
29734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int nextCol = mIsRtlLayout ? targetCol - i : targetCol + i;
29744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rec.setMarginAbove(i, topMost - mItemBottoms[nextCol]);
29754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
29764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return rec;
29784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
29794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int getItemWidth(int itemColumnSpan) {
29814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int colWidth = (getWidth() - getPaddingLeft() - getPaddingRight() -
29824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mItemMargin * (mColCount - 1)) / mColCount;
29834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return colWidth * itemColumnSpan + mItemMargin * (itemColumnSpan - 1);
29844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
29854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
29864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
29874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Obtain a populated view from the adapter.  This method checks to see if the view to populate
29884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * is already laid out on screen somewhere by comparing the item ids.
29894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
29904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * If the view is already laid out, and the view type has not changed, populate the contents
29914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * and return.
29924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
29934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * If the view is not laid out on screen somewhere, grab a view from the recycler and populate.
29944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
29954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE: This method should be called during layout.
29964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
29974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * TODO: This can probably be consolidated with the overloaded {@link #obtainView(int, View)}.
29984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
29994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param position Position to get the view for.
30004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
30014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final View obtainView(int position) {
30024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // TODO: This method currently does not support transient state views.
30034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final Object item = mAdapter.getItem(position);
30054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        View scrap = null;
30074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int positionViewType = mAdapter.getItemViewType(item, position);
30084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final long id = mAdapter.getItemId(item, position);
30104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final ViewRectPair viewRectPair = mChildRectsForAnimation.get(id);
30114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (viewRectPair != null) {
30124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            scrap = viewRectPair.view;
30134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: Make use of stable ids by retrieving the cached views using stable ids.  In
30154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // theory, we should maintain a list of active views, and then fetch the views
30164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // from that list.  If that fails, then we should go to the recycler.
30174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // For the collection holding stable ids, we must ensure that those views don't get
30184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // repurposed for other items at different positions.
30194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
30204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int scrapViewType = scrap != null &&
30224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                (scrap.getLayoutParams() instanceof LayoutParams) ?
30234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                ((LayoutParams) scrap.getLayoutParams()).viewType : -1;
30244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (scrap == null || scrapViewType != positionViewType) {
30264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // If there is no cached view or the cached view's type no longer match the type
30274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // of the item at the specified position, retrieve a new view from the recycler and
30284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // recycle the cached view.
30294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (scrap != null) {
30304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // The cached view we had is not valid, so add it to the recycler and
30314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // remove it from the current layout.
30324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                recycleView(scrap);
30334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
30344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            scrap = mRecycler.getScrapView(positionViewType);
30364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
30374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int itemColumnSpan = mAdapter.getItemColumnSpan(item, position);
30394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int itemWidth = getItemWidth(itemColumnSpan);
30404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final View view = mAdapter.getView(item, position, scrap, this, itemWidth);
30414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        ViewGroup.LayoutParams lp = view.getLayoutParams();
30434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (view.getParent() != this) {
30444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (lp == null) {
30454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lp = generateDefaultLayoutParams();
30464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else if (!checkLayoutParams(lp)) {
30474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                lp = generateLayoutParams(lp);
30484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
30494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            view.setLayoutParams(lp);
30514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
30524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final LayoutParams sglp = (LayoutParams) view.getLayoutParams();
30544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.position = position;
30554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.viewType = positionViewType;
30564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.id = id;
30574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.span = itemColumnSpan;
30584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // When the view at the positions we are tracking update, make sure to
30604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // update our views as well. That way, we have the correct
30614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // rectangle for comparing when the drag target enters/ leaves the
30624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // placeholder view.
30634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isDragReorderingSupported() && mReorderHelper.getDraggedChildId() == id) {
30644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.updateDraggedChildView(view);
30654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.updateDraggedOverChildView(view);
30664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
30674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return view;
30684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
30694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
30714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Obtain a populated view from the adapter. If optScrap is non-null and is not
30724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * reused it will be placed in the recycle bin.
30734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
30744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param position position to get view for
30754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param optScrap Optional scrap view; will be reused if possible
30764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return A new view, a recycled view from mRecycler, or optScrap
30774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
30784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    final View obtainView(int position, View optScrap) {
30794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        View view = mRecycler.getTransientStateView(position);
30804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final Object item = mAdapter.getItem(position);
30814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int positionViewType = mAdapter.getItemViewType(item, position);
30824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (view == null) {
30844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Reuse optScrap if it's of the right type (and not null)
30854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int optType = optScrap != null ?
30864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    ((LayoutParams) optScrap.getLayoutParams()).viewType : -1;
30874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View scrap = optType == positionViewType ?
30894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    optScrap : mRecycler.getScrapView(positionViewType);
30904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int itemColumnSpan = mAdapter.getItemColumnSpan(item, position);
30924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int itemWidth = getItemWidth(itemColumnSpan);
30934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            view = mAdapter.getView(item, position, scrap, this, itemWidth);
30944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
30954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (view != scrap && scrap != null) {
30964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // The adapter didn't use it; put it back.
30974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mRecycler.addScrap(scrap);
30984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
30994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            ViewGroup.LayoutParams lp = view.getLayoutParams();
31014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (view.getParent() != this) {
31034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (lp == null) {
31044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    lp = generateDefaultLayoutParams();
31054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else if (!checkLayoutParams(lp)) {
31064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    lp = generateLayoutParams(lp);
31074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
31084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                view.setLayoutParams(lp);
31104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
31114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
31124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final LayoutParams sglp = (LayoutParams) view.getLayoutParams();
31144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.position = position;
31154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.viewType = positionViewType;
31164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final long id = mAdapter.getItemIdFromView(view, position);
31174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.id = id;
31184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        sglp.span = mAdapter.getItemColumnSpan(item, position);
31194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // When the view at the positions we are tracking update, make sure to
31214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // update our views as well. That way, we have the correct
31224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // rectangle for comparing when the drag target enters/ leaves the
31234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // placeholder view.
31244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isDragReorderingSupported() && mReorderHelper.getDraggedChildId() == id) {
31254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.updateDraggedChildView(view);
31264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mReorderHelper.updateDraggedOverChildView(view);
31274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
31284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return view;
31304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
31334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Animation mode to play for new data coming in as well as the stale data that should be
31344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * animated out.
31354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param animationIn The animation to play to introduce new or updated data into view
31364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param animationOut The animation to play to transition stale data out of view.
31374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
31384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setAnimationMode(AnimationIn animationIn, AnimationOut animationOut) {
31394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mAnimationInMode = animationIn;
31404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mAnimationOutMode = animationOut;
31414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public AnimationIn getAnimationInMode() {
31444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mAnimationInMode;
31454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public AnimationOut getAnimationOutMode() {
31484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mAnimationOutMode;
31494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public GridAdapter getAdapter() {
31524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mAdapter;
31534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setAdapter(GridAdapter adapter) {
31564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mAdapter != null) {
31574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mAdapter.unregisterDataSetObserver(mObserver);
31584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
31594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        clearAllState();
31614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mAdapter = adapter;
31634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDataChanged = true;
31644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mItemCount = adapter != null ? adapter.getCount() : 0;
31654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (adapter != null) {
31674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            adapter.registerDataSetObserver(mObserver);
31684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mRecycler.setViewTypeCount(adapter.getViewTypeCount());
31694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mHasStableIds = adapter.hasStableIds();
31704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
31714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mHasStableIds = false;
31724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
31734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isDragReorderingSupported()) {
31754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            updateReorderStates(ReorderUtils.DRAG_STATE_NONE);
31764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
31774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        updateEmptyStatus();
31794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setAdapter(GridAdapter adapter, ScrollState scrollState) {
31824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setAdapter(adapter);
31834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mCurrentScrollState = scrollState;
31844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
31854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
31874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Clear all state because the grid will be used for a completely different set of data.
31884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
31894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void clearAllState() {
31904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Clear all layout records and views
31914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mLayoutRecords.clear();
31924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        removeAllViews();
31934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mItemTops = null;
31954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mItemBottoms = null;
31964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setSelectionToTop();
31984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
31994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Clear recycler because there could be different view types now
32004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mRecycler.clear();
32014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Reset the last touch y coordinate so that any animation/events won't use stale values.
32034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mLastTouchY = 0;
32044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Reset the first changed position to 0. At least we will update all views.
32064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFirstChangedPosition = 0;
32074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
32084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
32104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Scroll the list so the first visible position in the grid is the first item in the adapter.
32114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
32124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setSelectionToTop() {
32134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mCurrentScrollState = null;
32144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setFirstPositionAndOffsets(0 /* position */, getPaddingTop() /* offset */);
32154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
32164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
32184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Get {@link #mFirstPosition}, which is the adapter position of the View
32194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * returned by getChildAt(0).
32204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
32214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public int getCurrentFirstPosition() {
32224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mFirstPosition;
32234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
32244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
32264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Indicate whether the scrolling state is currently at the topmost of this grid
32274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return boolean Indicates whether the current view is the top most of this grid.
32284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
32294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean isSelectionAtTop() {
32304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mCurrentScrollState != null && mCurrentScrollState.getAdapterPosition() == 0) {
32314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // ScrollState is how far the top of the first child is from the top of the screen, and
32324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // does not include top padding when the adapter position is the first child. If the
32334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // vertical offset of the scroll state is exactly equal to {@link #mItemMargin}, then
32344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // the first item, and therefore the view of the grid, is at the top.
32354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mCurrentScrollState.getVerticalOffset() == mItemMargin;
32364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
32374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return false;
32394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
32404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
32424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Set the first position and offset so that on layout, we would start laying out starting
32434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * with the specified position at the top of the view.
32444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param position The child position to place at the top of this view.
32454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param offset The vertical layout offset of the view at the specified position.
32464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
32474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setFirstPositionAndOffsets(int position, int offset) {
32484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Reset the first visible position in the grid to be item 0
32494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFirstPosition = position;
32504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mItemTops == null || mItemBottoms == null) {
32514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemTops = new int[mColCount];
32524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemBottoms = new int[mColCount];
32534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
32544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        calculateLayoutStartOffsets(offset);
32564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
32574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
32594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Restore the view to the states specified by the {@link ScrollState}.
32604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param scrollState {@link ScrollState} containing the scroll states to restore to.
32614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
32624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private void restoreScrollPosition(ScrollState scrollState) {
32634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mAdapter == null || scrollState == null || mAdapter.getCount() == 0) {
32644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
32654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
32664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        Log.v(TAG, "[restoreScrollPosition] " + scrollState);
32684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int targetPosition = 0;
32704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        long itemId = -1;
32714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int originalPosition = scrollState.getAdapterPosition();
32734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int adapterCount = mAdapter.getCount();
32744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // ScrollState is defined as the vertical offset of the first item that is laid out
32754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // on screen.  To restore scroll state, we check within a window to see if we can
32764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // find that original first item in this new data set.  If we can, restore that item
32774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // to the first position on screen, offset by its previous vertical offset.  If we
32784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // cannot find that item, then we'll simply layout out everything from the beginning
32794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // again.
32804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // TODO:  Perhaps it is more efficient if we check the cursor in one direction first
32824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // before going backwards, rather than jumping back and forth as we are doing now.
32834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        for (int i = 0; i < SCROLL_RESTORE_WINDOW_SIZE; i++) {
32844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (originalPosition + i < adapterCount) {
32854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                itemId = mAdapter.getItemId(originalPosition + i);
32864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (itemId != -1 && itemId == scrollState.getItemId()) {
32874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    targetPosition = originalPosition + i;
32884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
32894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
32904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
32914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
32924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (originalPosition - i >= 0 && originalPosition - i < adapterCount) {
32934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                itemId = mAdapter.getItemId(originalPosition - i);
32944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (itemId != -1 && itemId == scrollState.getItemId()) {
32954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    targetPosition = originalPosition - i;
32964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
32974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
32984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
32994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // layoutChildren(), fillDown() and fillUp() always apply mItemMargin when laying out
33024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // views.  Since restoring scroll position is effectively laying out a particular child
33034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // as the first child, we need to ensure we strip mItemMargin from the offset, as it
33044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // will be re-applied when the view is laid out.
33054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        //
33064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Since top padding varies with screen orientation and is not stored in the scroll
33074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // state when the scroll adapter position is the first child, we add it here.
33084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int offset = scrollState.getVerticalOffset() - mItemMargin;
33094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (targetPosition == 0) {
33104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            offset += getPaddingTop();
33114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        setFirstPositionAndOffsets(targetPosition, offset);
33144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mCurrentScrollState = null;
33154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
33164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
33184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Return the current scroll state of this view.
33194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return {@link ScrollState} The current scroll state
33204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
33214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public ScrollState getScrollState() {
33224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final View v = getChildAt(0);
33234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (v == null) {
33244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return null;
33254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final LayoutParams lp = (LayoutParams) v.getLayoutParams();
33284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Since top padding varies with screen orientation, it is not stored in the scroll state
33294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // when the scroll adapter position is the first child.
33304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int offset = (lp.position == 0 ? v.getTop() - getPaddingTop() : v.getTop());
33314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return new ScrollState(lp.id, lp.position, offset);
33324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
33334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
33354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE This method is borrowed from {@link ScrollView}.
33364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
33374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
33384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
33394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean immediate) {
33404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // offset into coordinate space of this scroll view
33414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        rectangle.offset(child.getLeft() - child.getScrollX(),
33424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                child.getTop() - child.getScrollY());
33434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return scrollToChildRect(rectangle, immediate);
33454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
33464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
33484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * If rect is off screen, scroll just enough to get it (or at least the
33494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * first screen size chunk of it) on screen.
33504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE This method is borrowed from {@link ScrollView}.
33514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
33524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param rect      The rectangle.
33534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param immediate True to scroll immediately without animation. Not used here.
33544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return true if scrolling was performed
33554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
33564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean scrollToChildRect(Rect rect, boolean immediate) {
33574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
33584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean scroll = delta != 0;
33594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (scroll) {
33604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO smoothScrollBy if immediate is false.
33614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            scrollBy(0, delta);
33624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return scroll;
33644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
33654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
33674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
33684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        super.onSizeChanged(w, h, oldw, oldh);
33694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mOnSizeChangedListener != null) {
33714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mOnSizeChangedListener.onSizeChanged(w, h, oldw, oldh);
33724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // NOTE Below is borrowed from {@link ScrollView}.
33754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final View currentFocused = findFocus();
33764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (null == currentFocused || this == currentFocused) {
33774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return;
33784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // If the currently-focused view was visible on the screen when the
33814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // screen was at the old height, then scroll the screen to make that
33824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // view visible with the new screen height.
33834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
33844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            currentFocused.getDrawingRect(mTempRect);
33854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
33864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            scrollBy(0, computeScrollDeltaToGetChildRectOnScreen(mTempRect));
33874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
33884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
33894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
33904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
33914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
33924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE This method is borrowed from {@link ScrollView}.
33934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
33944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return whether the descendant of this scroll view is within delta
33954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *  pixels of being on the screen.
33964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
33974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
33984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        descendant.getDrawingRect(mTempRect);
33994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        offsetDescendantRectToMyCoords(descendant, mTempRect);
34004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return (mTempRect.bottom + delta) >= getScrollY()
34024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                && (mTempRect.top - delta) <= (getScrollY() + height);
34034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
34044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
34064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE: borrowed from {@link GridView}
34074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Comments from {@link View}
34084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
34104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * This value is used to compute the length of the thumb within the scrollbar's track.
34114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The range is expressed in arbitrary units that must be the same as the units used by
34124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link #computeVerticalScrollRange} and {@link #computeVerticalScrollOffset}.
34134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The default extent is the drawing height of this view.
34154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return the vertical extent of the scrollbar's thumb
34174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
34184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
34194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected int computeVerticalScrollExtent() {
34204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int count = getChildCount();
34224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (count > 0) {
34234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mSmoothScrollbarEnabled) {
34244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int rowCount = (count + mColCount - 1) / mColCount;
34254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int extent = rowCount * SCROLLING_ESTIMATED_ITEM_HEIGHT;
34264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                View view = getChildAt(0);
34284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int top = view.getTop();
34294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int height = view.getHeight();
34304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (height > 0) {
34314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    extent += (top * SCROLLING_ESTIMATED_ITEM_HEIGHT) / height;
34324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
34334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                view = getChildAt(count - 1);
34354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int bottom = view.getBottom();
34364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                height = view.getHeight();
34374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (height > 0) {
34384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    extent -= ((bottom - getHeight()) * SCROLLING_ESTIMATED_ITEM_HEIGHT) / height;
34394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
34404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return extent;
34424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
34434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return 1;
34444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
34454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
34464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return 0;
34474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
34484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
34504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE: borrowed from {@link GridView} and altered as appropriate to accommodate for
34514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link StaggeredGridView}
34524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Comments from {@link View}
34544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Compute the vertical offset of the vertical scrollbar's thumb within the horizontal range.
34564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * This value is used to compute the position of the thumb within the scrollbar's track.
34574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The range is expressed in arbitrary units that must be the same as the units used by
34584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.
34594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The default offset is the scroll offset of this view.
34614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
34624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return the vertical offset of the scrollbar's thumb
34634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
34644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
34654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected int computeVerticalScrollOffset() {
34664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int firstPosition = mFirstPosition;
34674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int childCount = getChildCount();
34684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int paddingTop = getPaddingTop();
34694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (firstPosition >= 0 && childCount > 0) {
34714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mSmoothScrollbarEnabled) {
34724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final View view = getChildAt(0);
34734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int top = view.getTop();
34744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int currentTopViewHeight = view.getHeight();
34754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (currentTopViewHeight > 0) {
34764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // In an ideal world, all items would have a fixed height that we would know
34774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // a priori, calculating the scroll offset would simply be:
34784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //     [A] (mFirstPosition * fixedHeight) - childView[0].top
34794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //         where childView[0] is the first view on screen.
34804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //
34814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // However, given that we do not know the height ahead of time, and that each
34824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // item in this grid can have varying heights, we'd need to assign an arbitrary
34834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // item height (SCROLLING_ESTIMATED_ITEM_HEIGHT) in order to estimate the scroll
34844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // offset.  The previous equation thus transforms to:
34854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //     [B] (mFirstPosition * SCROLLING_ESTIMATED_ITEM_HEIGHT) -
34864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //         ((childView[0].top * SCROLLING_ESTIMATED_ITEM_HEIGHT) /
34874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //          childView[0].height)
34884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //
34894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Equation [B] gives a pretty good calculation of the offset if this were a
34904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // single column grid view, for a multi-column grid, one slight modification is
34914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // needed:
34924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //     [C] ((mFirstPosition * SCROLLING_ESTIMATED_ITEM_HEIGHT) / mColCount) -
34934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //         ((childView[0].top * SCROLLING_ESTIMATED_ITEM_HEIGHT) /
34944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    //          childView[0].height)
34954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int estimatedScrollOffset =
34964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            ((firstPosition * SCROLLING_ESTIMATED_ITEM_HEIGHT) / mColCount) -
34974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            ((top * SCROLLING_ESTIMATED_ITEM_HEIGHT) / currentTopViewHeight);
34984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
34994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int rowCount = (mItemCount + mColCount - 1) / mColCount;
35004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int overScrollCompensation = (int) ((float) getScrollY() / getHeight() *
35014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            rowCount * SCROLLING_ESTIMATED_ITEM_HEIGHT);
35024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    int val = Math.max(estimatedScrollOffset + overScrollCompensation, 0);
35044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // If mFirstPosition is currently the very first item in the adapter, check to
35054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // see if we need to take into account any top padding.  This is so that we
35064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // don't return 0 when in fact the user may still be scrolling through some
35074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // top padding.
35084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (firstPosition == 0 && paddingTop > 0) {
35094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        val += paddingTop - top + mItemMargin;
35104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
35114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return val;
35124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
35134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
35144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                int index;
35154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int count = mItemCount;
35164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (firstPosition == 0) {
35174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    index = 0;
35184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else if (firstPosition + childCount == count) {
35194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    index = count;
35204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
35214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    index = firstPosition + childCount / 2;
35224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
35234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return (int) (firstPosition + childCount * (index / (float) count));
35244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
35254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
35264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return paddingTop;
35284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
35294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
35314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE: borrowed from {@link GridView} and altered as appropriate to accommodate for
35324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link StaggeredGridView}
35334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
35344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Comments from {@link View}
35354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
35364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Compute the vertical range that the vertical scrollbar represents.
35374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The range is expressed in arbitrary units that must be the same as the units used by
35384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link #computeVerticalScrollExtent} and {@link #computeVerticalScrollOffset}.
35394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
35404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The default range is the drawing height of this view.
35414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
35424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return the total vertical range represented by the vertical scrollbar
35434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
35444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
35454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected int computeVerticalScrollRange() {
35464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int rowCount = (mItemCount + mColCount - 1) / mColCount;
35474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int result = Math.max(rowCount * SCROLLING_ESTIMATED_ITEM_HEIGHT, 0);
35484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mSmoothScrollbarEnabled) {
35504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (getScrollY() != 0) {
35514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Compensate for overscroll
35524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                result += Math.abs((int) ((float) getScrollY() / getHeight() * rowCount
35534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        * SCROLLING_ESTIMATED_ITEM_HEIGHT));
35544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
35554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else {
35564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            result = mItemCount;
35574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
35584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return result;
35604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
35614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
35634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Compute the amount to scroll in the Y direction in order to get
35644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * a rectangle completely on the screen (or, if taller than the screen,
35654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * at least the first screen size chunk of it).
35664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
35674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * NOTE This method is borrowed from {@link ScrollView}.
35684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
35694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param rect The rect.
35704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The scroll delta.
35714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
35724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
35734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (getChildCount() == 0) {
35744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return 0;
35754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
35764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int height = getHeight();
35784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int fadingEdge = getVerticalFadingEdgeLength();
35794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int screenTop = getScrollY();
35814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int screenBottom = screenTop + height;
35824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // leave room for top fading edge as long as rect isn't at very top
35844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (rect.top > 0) {
35854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            screenTop += fadingEdge;
35864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
35874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // leave room for bottom fading edge as long as rect isn't at very bottom
35894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (rect.bottom < getHeight()) {
35904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            screenBottom -= fadingEdge;
35914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
35924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int scrollYDelta = 0;
35944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
35954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (rect.bottom > screenBottom && rect.top > screenTop) {
35964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // need to move down to get it in view: move down just enough so
35974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // that the entire rectangle is in view (or at least the first
35984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // screen size chunk).
35994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rect.height() > height) {
36014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // just enough to get screen size chunk on
36024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                scrollYDelta = screenTop - rect.top;
36034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
36044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // get entire rect at bottom of screen
36054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                scrollYDelta = screenBottom - rect.bottom;
36064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
36074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        } else if (rect.top < screenTop && rect.bottom < screenBottom) {
36084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // need to move up to get it in view: move up just enough so that
36094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // entire rectangle is in view (or at least the first screen
36104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // size chunk of it).
36114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (rect.height() > height) {
36134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // screen size chunk
36144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                scrollYDelta = screenBottom - rect.bottom;
36154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
36164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // entire rect at top
36174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                scrollYDelta = screenTop - rect.top;
36184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
36194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
36204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return scrollYDelta;
36214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
36244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected LayoutParams generateDefaultLayoutParams() {
36254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return new LayoutParams(LayoutParams.WRAP_CONTENT);
36264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
36294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
36304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return new LayoutParams(lp);
36314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
36344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    protected boolean checkLayoutParams(ViewGroup.LayoutParams lp) {
36354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return lp instanceof LayoutParams;
36364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
36394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
36404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return new LayoutParams(getContext(), attrs);
36414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
36444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public Parcelable onSaveInstanceState() {
36454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final Parcelable superState = super.onSaveInstanceState();
36464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final SavedState ss = new SavedState(superState);
36474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int position = mFirstPosition;
36484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        ss.position = position;
36494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (position >= 0 && mAdapter != null && position < mAdapter.getCount()) {
36504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            ss.firstId = mAdapter.getItemId(position);
36514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
36524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (getChildCount() > 0) {
36534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Since top padding varies with screen orientation, it is not stored in the scroll
36544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // state when the scroll adapter position is the first child.
36554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            ss.topOffset = position == 0 ?
36564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    getChildAt(0).getTop() - getPaddingTop() : getChildAt(0).getTop();
36574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
36584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return ss;
36594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Override
36624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void onRestoreInstanceState(Parcelable state) {
36634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final SavedState ss = (SavedState) state;
36644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        super.onRestoreInstanceState(ss.getSuperState());
36654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mDataChanged = true;
36664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFirstPosition = ss.position;
36674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mCurrentScrollState = new ScrollState(ss.firstId, ss.position, ss.topOffset);
36684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        requestLayout();
36694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
36704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public static class LayoutParams extends ViewGroup.LayoutParams {
36724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final int[] LAYOUT_ATTRS = new int[] {
36734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            android.R.attr.layout_span
36744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        };
36754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final int SPAN_INDEX = 0;
36774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
36794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * The number of columns this item should span
36804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
36814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int span = 1;
36824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
36844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Item position this view represents
36854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
36864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int position = -1;
36874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
36894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Type of this view as reported by the adapter
36904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
36914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int viewType;
36924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
36944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * The column this view is occupying
36954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
36964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int column;
36974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
36984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
36994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * The stable ID of the item this view displays
37004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
37014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        long id = -1;
37024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
37044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * The position where reordering can happen for this view
37054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
37064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int reorderingArea = ReorderUtils.REORDER_AREA_NONE;
37074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public LayoutParams(int height) {
37094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super(MATCH_PARENT, height);
37104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (this.height == MATCH_PARENT) {
37124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.w(TAG, "Constructing LayoutParams with height FILL_PARENT - " +
37134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        "impossible! Falling back to WRAP_CONTENT");
37144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                this.height = WRAP_CONTENT;
37154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
37174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public LayoutParams(Context c, AttributeSet attrs) {
37194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super(c, attrs);
37204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (this.width != MATCH_PARENT) {
37224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.w(TAG, "Inflation setting LayoutParams width to " + this.width +
37234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        " - must be MATCH_PARENT");
37244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                this.width = MATCH_PARENT;
37254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (this.height == MATCH_PARENT) {
37274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.w(TAG, "Inflation setting LayoutParams height to MATCH_PARENT - " +
37284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        "impossible! Falling back to WRAP_CONTENT");
37294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                this.height = WRAP_CONTENT;
37304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
37334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            span = a.getInteger(SPAN_INDEX, 1);
37344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            a.recycle();
37354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
37364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public LayoutParams(ViewGroup.LayoutParams other) {
37384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super(other);
37394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (this.width != MATCH_PARENT) {
37414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.w(TAG, "Constructing LayoutParams with width " + this.width +
37424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        " - must be MATCH_PARENT");
37434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                this.width = MATCH_PARENT;
37444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (this.height == MATCH_PARENT) {
37464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.w(TAG, "Constructing LayoutParams with height MATCH_PARENT - " +
37474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        "impossible! Falling back to WRAP_CONTENT");
37484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                this.height = WRAP_CONTENT;
37494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
37514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
37524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private class RecycleBin {
37544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private ArrayList<View>[] mScrapViews;
37554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mViewTypeCount;
37564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mMaxScrap;
37574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private SparseArray<View> mTransientStateViews;
37594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void setViewTypeCount(int viewTypeCount) {
37614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (viewTypeCount < 1) {
37624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                throw new IllegalArgumentException("Must have at least one view type (" +
37634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        viewTypeCount + " types reported)");
37644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (viewTypeCount == mViewTypeCount) {
37664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
37674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
37704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < viewTypeCount; i++) {
37714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                scrapViews[i] = new ArrayList<View>();
37724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mViewTypeCount = viewTypeCount;
37744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mScrapViews = scrapViews;
37754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
37764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void clear() {
37784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int typeCount = mViewTypeCount;
37794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < typeCount; i++) {
37804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mScrapViews[i].clear();
37814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mTransientStateViews != null) {
37834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTransientStateViews.clear();
37844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
37864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void clearTransientViews() {
37884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mTransientStateViews != null) {
37894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTransientStateViews.clear();
37904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
37924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void addScrap(View v) {
37944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (!(v.getLayoutParams() instanceof LayoutParams)) {
37954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
37964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
37974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
37984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final LayoutParams lp = (LayoutParams) v.getLayoutParams();
37994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (ViewCompat.hasTransientState(v)) {
38004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (mTransientStateViews == null) {
38014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mTransientStateViews = new SparseArray<View>();
38024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
38034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTransientStateViews.put(lp.position, v);
38044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
38054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int childCount = getChildCount();
38084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (childCount > mMaxScrap) {
38094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mMaxScrap = childCount;
38104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Clear possible modified states applied to the view when adding to the recycler.
38134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // This view may have been part of a cancelled animation, so clear that state so that
38144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // future consumer of this view won't have to deal with states from its past life.
38154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            v.setTranslationX(0);
38164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            v.setTranslationY(0);
38174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            v.setRotation(0);
38184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            v.setAlpha(1.0f);
38194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            v.setScaleY(1.0f);
38204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final ArrayList<View> scrap = mScrapViews[lp.viewType];
38224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (scrap.size() < mMaxScrap) {
38234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // The number of scraps have not yet exceeded our limit, check to see that this
38244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // view does not already exist in the recycler.  This can happen if a caller
38254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // mistakenly calls addScrap(view) multiple times for the same view.
38264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (!scrap.contains(v)) {
38274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    scrap.add(v);
38284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
38294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
38314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public View getTransientStateView(int position) {
38334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mTransientStateViews == null) {
38344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return null;
38354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View result = mTransientStateViews.get(position);
38384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (result != null) {
38394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mTransientStateViews.remove(position);
38404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return result;
38424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
38434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public View getScrapView(int type) {
38454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final ArrayList<View> scrap = mScrapViews[type];
38464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (scrap.isEmpty()) {
38474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return null;
38484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int index = scrap.size() - 1;
38514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final View result = scrap.remove(index);
38524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return result;
38544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
38554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // TODO: Implement support to maintain a list of active views so that we can make use of
38574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // stable ids to retrieve the same view that is currently laid out for a particular item.
38584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Currently, all views "recycled" are shoved into the same collection, this may not be
38594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // the most effective way.  Refer to the RecycleBin as implemented for AbsListView.
38604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public View getView(int type, long stableId) {
38614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final ArrayList<View> scrap = mScrapViews[type];
38624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (scrap.isEmpty()) {
38634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return null;
38644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < scrap.size(); i++) {
38674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final View v = scrap.get(i);
38684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final LayoutParams lp = (LayoutParams) v.getLayoutParams();
38694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (lp.id == stableId) {
38704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    scrap.remove(i);
38714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return v;
38724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
38734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return null;
38764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
38774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
38784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private class AdapterDataSetObserver extends DataSetObserver {
38804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
38814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void onChanged() {
38824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDataChanged = true;
38834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemCount = mAdapter.getCount();
38854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFirstChangedPosition = mAdapter.getFirstChangedPosition();
38864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mFirstPosition >= mItemCount) {
38874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If the latest data set has fewer data items than mFirstPosition, we will not be
38884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // able to accurately restore scroll state, so just reset to the top.
38894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mFirstPosition = 0;
38904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mCurrentScrollState = null;
38914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
38924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: Consider matching these back up if we have stable IDs.
38944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mRecycler.clearTransientViews();
38954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
38964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mHasStableIds) {
38974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // If we will animate the transition to the new layout, cache the current positions
38984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // of the visible children. This is before any views get removed below.
38994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                cacheChildRects();
39004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
39014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Clear all layout records
39024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mLayoutRecords.clear();
39034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Reset item bottoms to be equal to item tops
39054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int colCount = mColCount;
39064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                for (int i = 0; i < colCount; i++) {
39074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mItemBottoms[i] = mItemTops[i];
39084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
39094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
39104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            updateEmptyStatus();
39124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: consider repopulating in a deferred runnable instead
39144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // (so that successive changes may still be batched)
39154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            requestLayout();
39164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
39194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void onInvalidated() {
39204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
39224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    static class SavedState extends BaseSavedState {
39244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        long firstId = -1;
39254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int position;
39264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // topOffset is the vertical value that the view specified by position should
39284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // start rendering from.  If it is 0, the view would be at the top of the grid.
39294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        int topOffset;
39304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        SavedState(Parcelable superState) {
39324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super(superState);
39334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private SavedState(Parcel in) {
39364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super(in);
39374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            firstId = in.readLong();
39384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            position = in.readInt();
39394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            topOffset = in.readInt();
39404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
39434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void writeToParcel(Parcel out, int flags) {
39444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            super.writeToParcel(out, flags);
39454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            out.writeLong(firstId);
39464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            out.writeInt(position);
39474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            out.writeInt(topOffset);
39484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
39514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public String toString() {
39524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return "StaggereGridView.SavedState{"
39534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        + Integer.toHexString(System.identityHashCode(this))
39544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        + " firstId=" + firstId
39554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        + " position=" + position + "}";
39564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public static final Parcelable.Creator<SavedState> CREATOR
39594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                = new Parcelable.Creator<SavedState>() {
39604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            @Override
39614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            public SavedState createFromParcel(Parcel in) {
39624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return new SavedState(in);
39634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
39644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            @Override
39664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            public SavedState[] newArray(int size) {
39674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return new SavedState[size];
39684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
39694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        };
39704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
39714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setDropListener(ReorderListener listener) {
39734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mReorderHelper = new ReorderHelper(listener, this);
39744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
39754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setScrollListener(ScrollListener listener) {
39774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollListener = listener;
39784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
39794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setOnSizeChangedListener(OnSizeChangedListener listener) {
39814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mOnSizeChangedListener = listener;
39824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
39834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
39854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Helper class to store a {@link View} with its corresponding layout positions
39864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * as a {@link Rect}.
39874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
39884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static class ViewRectPair {
39894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public final View view;
39904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public final Rect rect;
39914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public ViewRectPair(View v, Rect r) {
39934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            view = v;
39944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            rect = r;
39954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
39964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
39974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
39984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public static class ScrollState implements Parcelable {
39994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private final long mItemId;
40004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private final int mAdapterPosition;
40014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // The offset that the view specified by mAdapterPosition should start rendering from.  If
40034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // this value is 0, then the view would be rendered from the very top of this grid.
40044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mVerticalOffset;
40054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public ScrollState(long itemId, int adapterPosition, int offset) {
40074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemId = itemId;
40084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mAdapterPosition = adapterPosition;
40094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVerticalOffset = offset;
40104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private ScrollState(Parcel in) {
40134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mItemId = in.readLong();
40144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mAdapterPosition = in.readInt();
40154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVerticalOffset = in.readInt();
40164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public long getItemId() {
40194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mItemId;
40204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int getAdapterPosition() {
40234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mAdapterPosition;
40244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void setVerticalOffset(int offset) {
40274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVerticalOffset = offset;
40284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int getVerticalOffset() {
40314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mVerticalOffset;
40324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
40354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public int describeContents() {
40364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return 0;
40374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
40404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public void writeToParcel(Parcel dest, int flags) {
40414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            dest.writeLong(mItemId);
40424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            dest.writeInt(mAdapterPosition);
40434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            dest.writeInt(mVerticalOffset);
40444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public static final Parcelable.Creator<ScrollState> CREATOR =
40474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                new Parcelable.Creator<ScrollState>() {
40484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            @Override
40494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            public ScrollState createFromParcel(Parcel source) {
40504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return new ScrollState(source);
40514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
40524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            @Override
40544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            public ScrollState[] newArray(int size) {
40554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return new ScrollState[size];
40564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
40574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        };
40584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        @Override
40604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        public String toString() {
40614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return "ScrollState {mItemId=" + mItemId +
40624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    " mAdapterPosition=" + mAdapterPosition +
40634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    " mVerticalOffset=" + mVerticalOffset + "}";
40644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
40654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
40664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
40684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Listener of {@Link StaggeredGridView} for grid size change.
40694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
40704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public interface OnSizeChangedListener {
40714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onSizeChanged(int width, int height, int oldWidth, int oldHeight);
40724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
40734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
40754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Listener of {@Link StaggeredGridView} for scroll change.
40764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
40774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public interface ScrollListener {
40784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
40804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Called when scroll happens on this view.
40814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         *
40824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param offset The scroll offset amount.
40834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param currentScrollY The current y position of this view.
40844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param maxScrollY The maximum amount of scroll possible in this view.
40854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
40864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onScrollChanged(int offset, int currentScrollY, int maxScrollY);
40874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
40884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
40894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
40904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Listener of {@link StaggeredGridView} for animations.  This listener is responsible
40914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * for playing all animations created by this {@link StaggeredGridView}
40924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
40934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public interface AnimationListener {
40944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
40954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Called when animations are ready to be played
40964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param animationMode The current animation mode based on the state of the data.  Valid
40974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * animation modes are {@link ANIMATION_MODE_NONE}, {@link ANIMATION_MODE_NEW_DATA}, and
40984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * {@link ANIMATION_MODE_UPDATE_DATA}.
40994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param animators The list of animators to be played
41004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
41014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onAnimationReady(int animationMode, List<Animator> animators);
41024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
41034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
41044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
41054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Listener of {@link StaggeredGridView} for drag and drop reordering of child views.
41064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
41074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public interface ReorderListener {
41084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
41094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
41104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * onPickedUp is called to notify listeners that an item has been picked up for reordering.
41114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param draggedChild the original child view that picked up.
41124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
41134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onPickedUp(View draggedChild);
41144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
41154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
41164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * onDrop is called to notify listeners that an intent to drop the
41174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * item at position "from" over the position "target"
41184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param draggedView the original child view that was dropped
41194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param sourcePosition the original position where the item was dragged from
41204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param targetPosition the target position where the item is dropped at
41214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
41224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onDrop(View draggedView, int sourcePosition, int targetPosition);
41234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
41244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
41254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * onCancelDrag is called to notify listeners that the drag event has been cancelled.
41264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param draggediew the original child view that was dragged.
41274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
41284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onCancelDrag(View draggediew);
41294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
41304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
41314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * onReorder is called to notify listeners that an intent to move the
41324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * item at position "from" to position "to"
41334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param draggedView the original child view that was dragged
41344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param id id of the original item that was picked up
41354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param from
41364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param to the target position where the item is dropped at
41374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
41384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean onReorder(View draggedView, long id, int from, int to);
41394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
41404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /**
41414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Event handler for a drag entering the {@link StaggeredGridView} element's
41424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * reordering area.
41434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param view The child view that just received an enter event on the reordering area.
41444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * @param position The adapter position of the view that just received an enter event.
41454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
41464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void onEnterReorderArea(View view, int position);
41474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
41484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein}
4149