|
19 | 19 | public class GalleryLayoutManager extends RecyclerView.LayoutManager { |
20 | 20 | private static final String TAG = "GalleryLayoutManager"; |
21 | 21 | private final Context mContext; |
22 | | - /* Consistent size applied to all child views */ |
23 | | -// private int mDecoratedChildWidth; |
24 | | -// private int mDecoratedChildHeight; |
25 | | - |
26 | | - // private int mSelectionPosition = 0; |
27 | 22 | private int mFirstVisiblePosition = -1; |
28 | 23 | private int mLastVisiblePos = -1; |
29 | | -// private int mSelectedIndex; |
30 | 24 |
|
31 | 25 |
|
32 | 26 | public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; |
@@ -103,11 +97,103 @@ private void fillCover(RecyclerView.Recycler recycler, RecyclerView.State state, |
103 | 97 |
|
104 | 98 | } |
105 | 99 |
|
106 | | - |
| 100 | + /** |
| 101 | + * @param recycler |
| 102 | + * @param state |
| 103 | + * @param dy |
| 104 | + */ |
107 | 105 | private void fillWithVertical(RecyclerView.Recycler recycler, RecyclerView.State state, int dy) { |
108 | | - // TODO: 2016/11/19 |
| 106 | + int topEdge = getOrientationHelper().getStartAfterPadding(); |
| 107 | + int bottomEdge = getOrientationHelper().getEndAfterPadding(); |
| 108 | + |
| 109 | + //1.根据滑动方向,回收越界子View |
| 110 | + View child; |
| 111 | + if (getChildCount() > 0) { |
| 112 | + if (dy >= 0) { |
| 113 | + for (int i = 0; i < getChildCount(); i++) { |
| 114 | + child = getChildAt(i); |
| 115 | + if (getDecoratedBottom(child) - dy < topEdge) { |
| 116 | + removeAndRecycleView(child, recycler); |
| 117 | + mFirstVisiblePosition++; |
| 118 | + } else { |
| 119 | + break; |
| 120 | + } |
| 121 | + } |
| 122 | + } else { //dy<0 |
| 123 | + for (int i = getChildCount() - 1; i >= 0; i--) { |
| 124 | + child = getChildAt(i); |
| 125 | + if (getDecoratedTop(child) - dy > bottomEdge) { |
| 126 | + removeAndRecycleView(child, recycler); |
| 127 | + mLastVisiblePos--; |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + } |
| 133 | + //开始进行布局的位置 |
| 134 | + int startPosition = mFirstVisiblePosition; |
| 135 | + int startOffset = -1; |
| 136 | + int scrapWidth, scrapHeight; |
| 137 | + Rect scrapRect = new Rect(); |
| 138 | + int width = getHorizontalSpace(); |
| 139 | + int leftOffset; |
| 140 | + View scrap; |
| 141 | + //2.根据不同滑动方向,为空余位置进行布局 |
| 142 | + if (dy >= 0) { |
| 143 | + if (getChildCount() != 0) { |
| 144 | + View lastView = getChildAt(getChildCount() - 1); |
| 145 | + startPosition = getPosition(lastView) + 1; //下一个View的Position |
| 146 | + startOffset = getDecoratedBottom(lastView); |
| 147 | + } |
| 148 | + //考虑边界和数量 |
| 149 | + for (int i = startPosition; i < getItemCount() && startOffset < bottomEdge; i++) { |
| 150 | + scrap = recycler.getViewForPosition(i); |
| 151 | + addView(scrap); |
| 152 | + measureChildWithMargins(scrap, 0, 0); |
| 153 | + scrapWidth = getDecoratedMeasuredWidth(scrap); |
| 154 | + scrapHeight = getDecoratedMeasuredHeight(scrap); |
| 155 | + leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f); |
| 156 | + if (startOffset == -1 && startPosition == 0) { |
| 157 | + //第0个View,居中处理 |
| 158 | + int top = (int) (getPaddingLeft() + (getHorizontalSpace() - scrapWidth) / 2.f); |
| 159 | + scrapRect.set(leftOffset, top, leftOffset + scrapWidth, top + scrapHeight); |
| 160 | + } else { |
| 161 | + scrapRect.set(leftOffset, startOffset, leftOffset + scrapWidth, startOffset + scrapHeight); |
| 162 | + } |
| 163 | + layoutDecorated(scrap, scrapRect.left, scrapRect.top, scrapRect.right, scrapRect.bottom); |
| 164 | + startOffset = scrapRect.bottom; |
| 165 | + ((LayoutParams) scrap.getLayoutParams()).mPosition = i; |
| 166 | + mLastVisiblePos = i; |
| 167 | + getState().mItemsFrames.put(i, scrapRect); |
| 168 | + if (BuildConfig.DEBUG) { |
| 169 | + Log.d(TAG, "fillWithHorizontal,layout:mLastVisiblePos: " + mLastVisiblePos); |
| 170 | + } |
| 171 | + } |
| 172 | + } else { |
| 173 | + //dy<0 |
| 174 | + if (getChildCount() > 0) { |
| 175 | + View firstView = getChildAt(0); |
| 176 | + startPosition = getPosition(firstView) - 1; //前一个View的position |
| 177 | + startOffset = getDecoratedTop(firstView); |
| 178 | + } |
| 179 | + //考虑边界和数量 |
| 180 | + for (int i = startPosition; i >= 0 && startOffset > topEdge; i--) { |
| 181 | + scrapRect = getState().mItemsFrames.get(i); |
| 182 | + scrap = recycler.getViewForPosition(i); |
| 183 | + addView(scrap, 0); |
| 184 | + measureChildWithMargins(scrap, 0, 0); |
| 185 | + scrapWidth = scrapRect.right - scrapRect.left; |
| 186 | + scrapHeight = scrapRect.bottom - scrapRect.top; |
| 187 | + leftOffset = (int) (getPaddingLeft() + (width - scrapWidth) / 2.0f); |
| 188 | + layoutDecorated(scrap, leftOffset, startOffset - scrapHeight, leftOffset + scrapWidth, startOffset); |
| 189 | + startOffset -= scrapHeight; |
| 190 | + ((LayoutParams) scrap.getLayoutParams()).mPosition = i; |
| 191 | + mFirstVisiblePosition = i; |
| 192 | + } |
| 193 | + } |
109 | 194 | } |
110 | 195 |
|
| 196 | + |
111 | 197 | /** |
112 | 198 | * @param recycler |
113 | 199 | * @param state |
@@ -247,13 +333,9 @@ class State { |
247 | 333 | */ |
248 | 334 | int mScrollDelta; |
249 | 335 |
|
250 | | - int mInitOffset; |
251 | | - |
252 | | - |
253 | 336 | public State() { |
254 | 337 | mItemsFrames = new SparseArray<Rect>(); |
255 | 338 | mScrollDelta = 0; |
256 | | - mInitOffset = 0; |
257 | 339 | } |
258 | 340 | } |
259 | 341 |
|
@@ -303,17 +385,27 @@ public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, Recycler |
303 | 385 |
|
304 | 386 | @Override |
305 | 387 | public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { |
306 | | - if (getChildCount() == 0) { |
| 388 | + if (getChildCount() == 0 || dy == 0) { |
307 | 389 | return 0; |
308 | 390 | } |
309 | | - final View topView = getChildAt(0); |
310 | | - final View bottomView = getChildAt(getChildCount() - 1); |
311 | | - |
312 | | - int viewSpan = getDecoratedBottom(bottomView) - getDecoratedTop(topView); |
313 | | - if (viewSpan < getVerticalSpace()) { |
314 | | - return 0; |
| 391 | + int delta = -dy; |
| 392 | + int parentCenter = (getOrientationHelper().getEndAfterPadding() - getOrientationHelper().getStartAfterPadding()) / 2 + getOrientationHelper().getStartAfterPadding(); |
| 393 | + View child; |
| 394 | + if (dy > 0) { //手势左滑 |
| 395 | + if (((LayoutParams) getChildAt(getChildCount() - 1).getLayoutParams()).mPosition == getItemCount() - 1) { |
| 396 | + child = getChildAt(getChildCount() - 1); |
| 397 | + delta = -Math.max(0, Math.min(dy, (child.getBottom() - child.getTop()) / 2 + child.getTop() - parentCenter)); |
| 398 | + } |
| 399 | + } else { |
| 400 | + if (mFirstVisiblePosition == 0) { |
| 401 | + child = getChildAt(0); |
| 402 | + delta = -Math.min(0, Math.max(dy, ((child.getBottom() - child.getTop()) / 2 + child.getTop()) - parentCenter)); |
| 403 | + } |
315 | 404 | } |
316 | | - return super.scrollVerticallyBy(dy, recycler, state); |
| 405 | + getState().mScrollDelta = -delta; |
| 406 | + fillCover(recycler, state, -delta); |
| 407 | + offsetChildrenVertical(delta); |
| 408 | + return -delta; |
317 | 409 | } |
318 | 410 |
|
319 | 411 | private OrientationHelper getOrientationHelper() { |
|
0 commit comments