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