225 | * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
226 | * this flag to true
so that views will be available to other RecyclerViews
227 | * immediately.
228 | *
229 | * Note that, setting this flag will result in a performance drop if RecyclerView 230 | * is restored. 231 | * 232 | * @param recycleChildrenOnDetach Whether children should be recycled in detach or not. 233 | */ 234 | public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) { 235 | mRecycleChildrenOnDetach = recycleChildrenOnDetach; 236 | } 237 | 238 | @Override 239 | public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { 240 | super.onDetachedFromWindow(view, recycler); 241 | if (mRecycleChildrenOnDetach) { 242 | removeAndRecycleAllViews(recycler); 243 | recycler.clear(); 244 | } 245 | } 246 | 247 | @Override 248 | public Parcelable onSaveInstanceState() { 249 | if (mPendingSavedState != null) { 250 | return new SavedState(mPendingSavedState); 251 | } 252 | SavedState savedState = new SavedState(); 253 | savedState.position = mPendingScrollPosition; 254 | savedState.offset = mOffset; 255 | savedState.isReverseLayout = mShouldReverseLayout; 256 | return savedState; 257 | } 258 | 259 | @Override 260 | public void onRestoreInstanceState(Parcelable state) { 261 | if (state instanceof SavedState) { 262 | mPendingSavedState = new SavedState((SavedState) state); 263 | requestLayout(); 264 | } 265 | } 266 | 267 | /** 268 | * @return true if {@link #getOrientation()} is {@link #HORIZONTAL} 269 | */ 270 | @Override 271 | public boolean canScrollHorizontally() { 272 | return mOrientation == HORIZONTAL; 273 | } 274 | 275 | /** 276 | * @return true if {@link #getOrientation()} is {@link #VERTICAL} 277 | */ 278 | @Override 279 | public boolean canScrollVertically() { 280 | return mOrientation == VERTICAL; 281 | } 282 | 283 | /** 284 | * Returns the current orientation of the layout. 285 | * 286 | * @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL} 287 | * @see #setOrientation(int) 288 | */ 289 | public int getOrientation() { 290 | return mOrientation; 291 | } 292 | 293 | /** 294 | * Sets the orientation of the layout. {@link BannerLayoutManager} 295 | * will do its best to keep scroll position. 296 | * 297 | * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} 298 | */ 299 | public void setOrientation(int orientation) { 300 | if (orientation != HORIZONTAL && orientation != VERTICAL) { 301 | throw new IllegalArgumentException("invalid orientation:" + orientation); 302 | } 303 | assertNotInLayoutOrScroll(null); 304 | if (orientation == mOrientation) { 305 | return; 306 | } 307 | mOrientation = orientation; 308 | mOrientationHelper = null; 309 | mDistanceToBottom = INVALID_SIZE; 310 | removeAllViews(); 311 | } 312 | 313 | /** 314 | * Returns the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now 315 | * And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range 316 | * 317 | * @return Max visible item count 318 | */ 319 | public int getMaxVisibleItemCount() { 320 | return mMaxVisibleItemCount; 321 | } 322 | 323 | /** 324 | * Set the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now 325 | * And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range 326 | * 327 | * @param mMaxVisibleItemCount Max visible item count 328 | */ 329 | public void setMaxVisibleItemCount(int mMaxVisibleItemCount) { 330 | assertNotInLayoutOrScroll(null); 331 | if (this.mMaxVisibleItemCount == mMaxVisibleItemCount) return; 332 | this.mMaxVisibleItemCount = mMaxVisibleItemCount; 333 | removeAllViews(); 334 | } 335 | 336 | /** 337 | * Calculates the view layout order. (e.g. from end to start or start to end) 338 | * RTL layout support is applied automatically. So if layout is RTL and 339 | * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left. 340 | */ 341 | private void resolveShouldLayoutReverse() { 342 | if (mOrientation == HORIZONTAL && getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) { 343 | mReverseLayout = !mReverseLayout; 344 | } 345 | } 346 | 347 | /** 348 | * Returns if views are laid out from the opposite direction of the layout. 349 | * 350 | * @return If layout is reversed or not. 351 | * @see #setReverseLayout(boolean) 352 | */ 353 | public boolean getReverseLayout() { 354 | return mReverseLayout; 355 | } 356 | 357 | /** 358 | * Used to reverse item traversal and layout order. 359 | * This behaves similar to the layout change for RTL views. When set to true, first item is 360 | * laid out at the end of the UI, second item is laid out before it etc. 361 | *
362 | * For horizontal layouts, it depends on the layout direction.
363 | * When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will
364 | * layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
365 | * from LTR.
366 | */
367 | public void setReverseLayout(boolean reverseLayout) {
368 | assertNotInLayoutOrScroll(null);
369 | if (reverseLayout == mReverseLayout) {
370 | return;
371 | }
372 | mReverseLayout = reverseLayout;
373 | removeAllViews();
374 | }
375 |
376 | public void setSmoothScrollInterpolator(Interpolator smoothScrollInterpolator) {
377 | this.mSmoothScrollInterpolator = smoothScrollInterpolator;
378 | }
379 |
380 | @Override
381 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
382 | final int offsetPosition = getOffsetToPosition(position);
383 | if (mOrientation == VERTICAL) {
384 | recyclerView.smoothScrollBy(0, offsetPosition, mSmoothScrollInterpolator);
385 | } else {
386 | recyclerView.smoothScrollBy(offsetPosition, 0, mSmoothScrollInterpolator);
387 | }
388 | }
389 | @Override
390 | public void scrollToPosition(int position) {
391 | if (!mInfinite && (position < 0 || position >= getItemCount())) return;
392 | mPendingScrollPosition = position;
393 | mOffset = mShouldReverseLayout ? position * -mInterval : position * mInterval;
394 | requestLayout();
395 | }
396 |
397 | @Override
398 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
399 | if (state.getItemCount() == 0) {
400 | removeAndRecycleAllViews(recycler);
401 | mOffset = 0;
402 | return;
403 | }
404 |
405 | ensureLayoutState();
406 | resolveShouldLayoutReverse();
407 |
408 | //make sure properties are correct while measure more than once
409 | View scrap = recycler.getViewForPosition(0);
410 | measureChildWithMargins(scrap, 0, 0);
411 | mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap);
412 | mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap);
413 | mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2;
414 | if (mDistanceToBottom == INVALID_SIZE) {
415 | mSpaceInOther = (getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2;
416 | } else {
417 | mSpaceInOther =getTotalSpaceInOther() - mDecoratedMeasurementInOther - mDistanceToBottom;
418 | }
419 |
420 | mInterval = setInterval();
421 | setUp();
422 | mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1;
423 | mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1;
424 |
425 | if (mPendingSavedState != null) {
426 | mShouldReverseLayout = mPendingSavedState.isReverseLayout;
427 | mPendingScrollPosition = mPendingSavedState.position;
428 | mOffset = mPendingSavedState.offset;
429 | }
430 |
431 | if (mPendingScrollPosition != NO_POSITION) {
432 | mOffset = mShouldReverseLayout ?
433 | mPendingScrollPosition * -mInterval : mPendingScrollPosition * mInterval;
434 | }
435 |
436 | detachAndScrapAttachedViews(recycler);
437 | layoutItems(recycler);
438 | }
439 | public int getTotalSpaceInOther() {
440 | if (mOrientation == HORIZONTAL) {
441 | return getHeight() - getPaddingTop()
442 | - getPaddingBottom();
443 | } else {
444 | return getWidth() - getPaddingLeft()
445 | - getPaddingRight();
446 | }
447 | }
448 | @Override
449 | public void onLayoutCompleted(RecyclerView.State state) {
450 | super.onLayoutCompleted(state);
451 | mPendingSavedState = null;
452 | mPendingScrollPosition = NO_POSITION;
453 | }
454 |
455 | @Override
456 | public boolean onAddFocusables(RecyclerView recyclerView, ArrayList
884 | * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
885 | * solely on the number of items in the adapter and the position of the visible items inside
886 | * the adapter. This provides a stable scrollbar as the user navigates through a list of items
887 | * with varying widths / heights.
888 | *
889 | * @param enabled Whether or not to enable smooth scrollbar.
890 | * @see #setSmoothScrollbarEnabled(boolean)
891 | */
892 | public void setSmoothScrollbarEnabled(boolean enabled) {
893 | mSmoothScrollbarEnabled = enabled;
894 | }
895 |
896 | public void setEnableBringCenterToFront(boolean bringCenterToTop) {
897 | assertNotInLayoutOrScroll(null);
898 | if (mEnableBringCenterToFront == bringCenterToTop) {
899 | return;
900 | }
901 | this.mEnableBringCenterToFront = bringCenterToTop;
902 | requestLayout();
903 | }
904 |
905 | public boolean getEnableBringCenterToFront() {
906 | return mEnableBringCenterToFront;
907 | }
908 |
909 | /**
910 | * Returns the current state of the smooth scrollbar feature. It is enabled by default.
911 | *
912 | * @return True if smooth scrollbar is enabled, false otherwise.
913 | * @see #setSmoothScrollbarEnabled(boolean)
914 | */
915 | public boolean getSmoothScrollbarEnabled() {
916 | return mSmoothScrollbarEnabled;
917 | }
918 |
919 | private static class SavedState implements Parcelable {
920 | int position;
921 | float offset;
922 | boolean isReverseLayout;
923 |
924 | SavedState() {
925 |
926 | }
927 |
928 | SavedState(Parcel in) {
929 | position = in.readInt();
930 | offset = in.readFloat();
931 | isReverseLayout = in.readInt() == 1;
932 | }
933 |
934 | public SavedState(SavedState other) {
935 | position = other.position;
936 | offset = other.offset;
937 | isReverseLayout = other.isReverseLayout;
938 | }
939 |
940 | @Override
941 | public int describeContents() {
942 | return 0;
943 | }
944 |
945 | @Override
946 | public void writeToParcel(Parcel dest, int flags) {
947 | dest.writeInt(position);
948 | dest.writeFloat(offset);
949 | dest.writeInt(isReverseLayout ? 1 : 0);
950 | }
951 |
952 | public static final Parcelable.Creator
13 | * The implementation will snap the center of the target child view to the center of
14 | * the attached {@link RecyclerView}.
15 | */
16 | public class CenterSnapHelper extends RecyclerView.OnFlingListener {
17 |
18 | RecyclerView mRecyclerView;
19 | Scroller mGravityScroller;
20 |
21 | /**
22 | * when the dataSet is extremely large
23 | * {@link #snapToCenterView(BannerLayoutManager, BannerLayoutManager.OnPageChangeListener)}
24 | * may keep calling itself because the accuracy of float
25 | */
26 | private boolean snapToCenter = false;
27 |
28 | // Handles the snap on scroll case.
29 | private final RecyclerView.OnScrollListener mScrollListener =
30 | new RecyclerView.OnScrollListener() {
31 |
32 | boolean mScrolled = false;
33 |
34 | @Override
35 | public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
36 | super.onScrollStateChanged(recyclerView, newState);
37 |
38 | final BannerLayoutManager layoutManager =
39 | (BannerLayoutManager) recyclerView.getLayoutManager();
40 | final BannerLayoutManager.OnPageChangeListener onPageChangeListener =
41 | layoutManager.onPageChangeListener;
42 | if (onPageChangeListener != null) {
43 | onPageChangeListener.onPageScrollStateChanged(newState);
44 | }
45 |
46 | if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
47 | mScrolled = false;
48 | if (!snapToCenter) {
49 | snapToCenter = true;
50 | snapToCenterView(layoutManager, onPageChangeListener);
51 | } else {
52 | snapToCenter = false;
53 | }
54 | }
55 | }
56 |
57 | @Override
58 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
59 | if (dx != 0 || dy != 0) {
60 | mScrolled = true;
61 | }
62 | }
63 | };
64 |
65 | @Override
66 | public boolean onFling(int velocityX, int velocityY) {
67 | BannerLayoutManager layoutManager = (BannerLayoutManager) mRecyclerView.getLayoutManager();
68 | if (layoutManager == null) {
69 | return false;
70 | }
71 | RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
72 | if (adapter == null) {
73 | return false;
74 | }
75 |
76 | if (!layoutManager.getInfinite() &&
77 | (layoutManager.mOffset == layoutManager.getMaxOffset()
78 | || layoutManager.mOffset == layoutManager.getMinOffset())) {
79 | return false;
80 | }
81 |
82 | final int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
83 | mGravityScroller.fling(0, 0, velocityX, velocityY,
84 | Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
85 |
86 | if (layoutManager.mOrientation == BannerLayoutManager.VERTICAL
87 | && Math.abs(velocityY) > minFlingVelocity) {
88 | final int currentPosition = layoutManager.getCurrentPosition();
89 | final int offsetPosition = (int) (mGravityScroller.getFinalY() /
90 | layoutManager.mInterval / layoutManager.getDistanceRatio());
91 | mRecyclerView.smoothScrollToPosition(layoutManager.getReverseLayout() ?
92 | currentPosition - offsetPosition : currentPosition + offsetPosition);
93 | return true;
94 | } else if (layoutManager.mOrientation == BannerLayoutManager.HORIZONTAL
95 | && Math.abs(velocityX) > minFlingVelocity) {
96 | final int currentPosition = layoutManager.getCurrentPosition();
97 | final int offsetPosition = (int) (mGravityScroller.getFinalX() /
98 | layoutManager.mInterval / layoutManager.getDistanceRatio());
99 | mRecyclerView.smoothScrollToPosition(layoutManager.getReverseLayout() ?
100 | currentPosition - offsetPosition : currentPosition + offsetPosition);
101 | return true;
102 | }
103 |
104 | return true;
105 | }
106 |
107 | /**
108 | * Please attach after {{@link LayoutManager} is setting}
109 | * Attaches the {@link CenterSnapHelper} to the provided RecyclerView, by calling
110 | * {@link RecyclerView#setOnFlingListener(RecyclerView.OnFlingListener)}.
111 | * You can call this method with {@code null} to detach it from the current RecyclerView.
112 | *
113 | * @param recyclerView The RecyclerView instance to which you want to add this helper or
114 | * {@code null} if you want to remove CenterSnapHelper from the current
115 | * RecyclerView.
116 | * @throws IllegalArgumentException if there is already a {@link RecyclerView.OnFlingListener}
117 | * attached to the provided {@link RecyclerView}.
118 | */
119 | public void attachToRecyclerView(@Nullable RecyclerView recyclerView)
120 | throws IllegalStateException {
121 | if (mRecyclerView == recyclerView) {
122 | return; // nothing to do
123 | }
124 | if (mRecyclerView != null) {
125 | destroyCallbacks();
126 | }
127 | mRecyclerView = recyclerView;
128 | if (mRecyclerView != null) {
129 | final LayoutManager layoutManager = mRecyclerView.getLayoutManager();
130 | if (!(layoutManager instanceof BannerLayoutManager)) return;
131 |
132 | setupCallbacks();
133 | mGravityScroller = new Scroller(mRecyclerView.getContext(),
134 | new DecelerateInterpolator());
135 |
136 | snapToCenterView((BannerLayoutManager) layoutManager,
137 | ((BannerLayoutManager) layoutManager).onPageChangeListener);
138 | }
139 | }
140 |
141 | void snapToCenterView(BannerLayoutManager layoutManager,
142 | BannerLayoutManager.OnPageChangeListener listener) {
143 | final int delta = layoutManager.getOffsetToCenter();
144 | if (delta != 0) {
145 | if (layoutManager.getOrientation()
146 | == BannerLayoutManager.VERTICAL)
147 | mRecyclerView.smoothScrollBy(0, delta);
148 | else
149 | mRecyclerView.smoothScrollBy(delta, 0);
150 | } else {
151 | // set it false to make smoothScrollToPosition keep trigger the listener
152 | snapToCenter = false;
153 | }
154 |
155 | if (listener != null)
156 | listener.onPageSelected(layoutManager.getCurrentPosition());
157 | }
158 |
159 | /**
160 | * Called when an instance of a {@link RecyclerView} is attached.
161 | */
162 | void setupCallbacks() throws IllegalStateException {
163 | if (mRecyclerView.getOnFlingListener() != null) {
164 | throw new IllegalStateException("An instance of OnFlingListener already set.");
165 | }
166 | mRecyclerView.addOnScrollListener(mScrollListener);
167 | mRecyclerView.setOnFlingListener(this);
168 | }
169 |
170 | /**
171 | * Called when the instance of a {@link RecyclerView} is detached.
172 | */
173 | void destroyCallbacks() {
174 | mRecyclerView.removeOnScrollListener(mScrollListener);
175 | mRecyclerView.setOnFlingListener(null);
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/library/src/main/java/com/example/library/banner/layoutmanager/OverFlyingLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.example.library.banner.layoutmanager;
2 |
3 | import android.content.Context;
4 | import android.graphics.PointF;
5 | import android.os.Build;
6 | import android.os.Parcel;
7 | import android.os.Parcelable;
8 | import android.support.v4.view.ViewCompat;
9 | import android.support.v7.widget.LinearLayoutManager;
10 | import android.support.v7.widget.LinearSmoothScroller;
11 | import android.support.v7.widget.OrientationHelper;
12 | import android.support.v7.widget.RecyclerView;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 |
16 | import static android.support.v7.widget.RecyclerView.NO_POSITION;
17 |
18 | /**
19 | * An implementation of {@link RecyclerView.LayoutManager} which behaves like view pager.
20 | * Please make sure your child view have the same size.
21 | */
22 |
23 | @SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue"})
24 | public class OverFlyingLayoutManager extends RecyclerView.LayoutManager
25 | implements RecyclerView.SmoothScroller.ScrollVectorProvider {
26 | private float minScale = 0.75f;//两侧图片缩放比
27 | private float angle = 8f;//翻转角度
28 | private int itemSpace = 385;
29 | private boolean mInfinite = true;
30 | public static final int DETERMINE_BY_MAX_AND_MIN = -1;
31 |
32 | public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
33 |
34 | public static final int VERTICAL = OrientationHelper.VERTICAL;
35 |
36 | protected int mDecoratedMeasurement;
37 |
38 | protected int mDecoratedMeasurementInOther;
39 |
40 | public float getMinScale() {
41 | return minScale;
42 | }
43 |
44 | public void setMinScale(float minScale) {
45 | this.minScale = minScale;
46 | }
47 |
48 | public float getAngle() {
49 | return angle;
50 | }
51 |
52 | public void setAngle(float angle) {
53 | this.angle = angle;
54 | }
55 |
56 | public int getItemSpace() {
57 | return itemSpace;
58 | }
59 |
60 | public void setItemSpace(int itemSpace) {
61 | this.itemSpace = itemSpace;
62 | }
63 |
64 | /**
65 | * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
66 | */
67 | int mOrientation;
68 |
69 | protected int mSpaceMain;
70 |
71 | protected int mSpaceInOther;
72 |
73 | /**
74 | * The offset of property which will change while scrolling
75 | */
76 | protected float mOffset;
77 |
78 | /**
79 | * Many calculations are made depending on orientation. To keep it clean, this interface
80 | * helps {@link LinearLayoutManager} make those decisions.
81 | * Based on {@link #mOrientation}, an implementation is lazily created in
82 | * {@link #ensureLayoutState} method.
83 | */
84 | protected OrientationHelper mOrientationHelper;
85 |
86 | /**
87 | * Defines if layout should be calculated from end to start.
88 | */
89 | private boolean mReverseLayout = false;
90 |
91 | /**
92 | * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
93 | * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
94 | */
95 | private boolean mSmoothScrollbarEnabled = true;
96 |
97 | /**
98 | * When LayoutManager needs to scroll to a position, it sets this variable and requests a
99 | * layout which will check this variable and re-layout accordingly.
100 | */
101 | private int mPendingScrollPosition = RecyclerView.NO_POSITION;
102 |
103 | private SavedState mPendingSavedState = null;
104 |
105 | protected float mInterval; //the mInterval of each item's mOffset
106 |
107 | /* package */ OnPageChangeListener onPageChangeListener;
108 |
109 | private boolean mRecycleChildrenOnDetach;
110 |
111 |
112 | private boolean mEnableBringCenterToFront;
113 |
114 | /**
115 | * ugly code for fix bug caused by float
116 | */
117 | private boolean mIntegerDy = false;
118 |
119 | private int mLeftItems;
120 |
121 | private int mRightItems;
122 |
123 | /**
124 | * max visible item count
125 | */
126 | private int mMaxVisibleItemCount = DETERMINE_BY_MAX_AND_MIN;
127 |
128 | /**
129 | * @return the mInterval of each item's mOffset
130 | */
131 | protected float setInterval() {
132 | return mDecoratedMeasurement - itemSpace;
133 | }
134 |
135 | protected void setItemViewProperty(View itemView, float targetOffset) {
136 | float scale = calculateScale(targetOffset + mSpaceMain);
137 | itemView.setScaleX(scale);
138 | itemView.setScaleY(scale);
139 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
140 | itemView.setElevation(0);
141 | }
142 | final float rotation = calRotation(targetOffset);
143 | if (getOrientation() == HORIZONTAL) {
144 | itemView.setRotationY(rotation);
145 | } else {
146 | itemView.setRotationX(-rotation);
147 | }
148 | }
149 |
150 | private float calRotation(float targetOffset) {
151 | return -angle / mInterval * targetOffset;
152 | }
153 |
154 | private float calculateScale(float x) {
155 | float deltaX = Math.abs(x - (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2f);
156 | return (minScale - 1) * deltaX / (mOrientationHelper.getTotalSpace() / 2f) + 1f;
157 | }
158 |
159 | /**
160 | * cause elevation is not support below api 21,
161 | * so you can set your elevation here for supporting it below api 21
162 | * or you can just setElevation in {@link #setItemViewProperty(View, float)}
163 | */
164 | protected float setViewElevation(View itemView, float targetOffset) {
165 | return itemView.getScaleX() * 5;
166 | }
167 |
168 | /**
169 | * Creates a horizontal ViewPagerLayoutManager
170 | */
171 | public OverFlyingLayoutManager(Context context) {
172 | this(HORIZONTAL, false);
173 | }
174 |
175 | /**
176 | * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
177 | * @param reverseLayout When set to true, layouts from end to start
178 | */
179 | public OverFlyingLayoutManager(int orientation, boolean reverseLayout) {
180 | setOrientation(orientation);
181 | setReverseLayout(reverseLayout);
182 | setAutoMeasureEnabled(true);
183 | setEnableBringCenterToFront(true);
184 | setIntegerDy(true);
185 | }
186 |
187 | public OverFlyingLayoutManager(float minScale, int itemSpace, int orientation) {
188 | this(orientation, false);
189 | this.minScale = minScale;
190 | this.itemSpace = itemSpace;
191 | mOrientation = orientation;
192 | }
193 |
194 | @Override
195 | public RecyclerView.LayoutParams generateDefaultLayoutParams() {
196 | return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
197 | ViewGroup.LayoutParams.WRAP_CONTENT);
198 | }
199 |
200 | /**
201 | * Returns whether LayoutManager will recycle its children when it is detached from
202 | * RecyclerView.
203 | *
204 | * @return true if LayoutManager will recycle its children when it is detached from
205 | * RecyclerView.
206 | */
207 | public boolean getRecycleChildrenOnDetach() {
208 | return mRecycleChildrenOnDetach;
209 | }
210 |
211 | /**
212 | * Set whether LayoutManager will recycle its children when it is detached from
213 | * RecyclerView.
214 | *
215 | * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
216 | * this flag to
219 | * Note that, setting this flag will result in a performance drop if RecyclerView
220 | * is restored.
221 | *
222 | * @param recycleChildrenOnDetach Whether children should be recycled in detach or not.
223 | */
224 | public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
225 | mRecycleChildrenOnDetach = recycleChildrenOnDetach;
226 | }
227 |
228 | @Override
229 | public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
230 | super.onDetachedFromWindow(view, recycler);
231 | if (mRecycleChildrenOnDetach) {
232 | removeAndRecycleAllViews(recycler);
233 | recycler.clear();
234 | }
235 | }
236 |
237 | @Override
238 | public Parcelable onSaveInstanceState() {
239 | if (mPendingSavedState != null) {
240 | return new SavedState(mPendingSavedState);
241 | }
242 | SavedState savedState = new SavedState();
243 | savedState.position = mPendingScrollPosition;
244 | savedState.offset = mOffset;
245 | savedState.isReverseLayout = mReverseLayout;
246 | return savedState;
247 | }
248 |
249 | @Override
250 | public void onRestoreInstanceState(Parcelable state) {
251 | if (state instanceof SavedState) {
252 | mPendingSavedState = new SavedState((SavedState) state);
253 | requestLayout();
254 | }
255 | }
256 |
257 | /**
258 | * @return true if {@link #getOrientation()} is {@link #HORIZONTAL}
259 | */
260 | @Override
261 | public boolean canScrollHorizontally() {
262 | return mOrientation == HORIZONTAL;
263 | }
264 |
265 | /**
266 | * @return true if {@link #getOrientation()} is {@link #VERTICAL}
267 | */
268 | @Override
269 | public boolean canScrollVertically() {
270 | return mOrientation == VERTICAL;
271 | }
272 |
273 | /**
274 | * Returns the current orientation of the layout.
275 | *
276 | * @return Current orientation, either {@link #HORIZONTAL} or {@link #VERTICAL}
277 | * @see #setOrientation(int)
278 | */
279 | public int getOrientation() {
280 | return mOrientation;
281 | }
282 |
283 | /**
284 | * will do its best to keep scroll position.
285 | *
286 | * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
287 | */
288 | public void setOrientation(int orientation) {
289 | if (orientation != HORIZONTAL && orientation != VERTICAL) {
290 | throw new IllegalArgumentException("invalid orientation:" + orientation);
291 | }
292 | assertNotInLayoutOrScroll(null);
293 | if (orientation == mOrientation) {
294 | return;
295 | }
296 | mOrientation = orientation;
297 | mOrientationHelper = null;
298 | removeAllViews();
299 | }
300 |
301 | /**
302 | * Returns the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now
303 | * And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range
304 | *
305 | * @return Max visible item count
306 | */
307 | public int getMaxVisibleItemCount() {
308 | return mMaxVisibleItemCount;
309 | }
310 |
311 | /**
312 | * Set the max visible item count, {@link #DETERMINE_BY_MAX_AND_MIN} means it haven't been set now
313 | * And it will use {@link #maxRemoveOffset()} and {@link #minRemoveOffset()} to handle the range
314 | *
315 | * @param mMaxVisibleItemCount Max visible item count
316 | */
317 | public void setMaxVisibleItemCount(int mMaxVisibleItemCount) {
318 | assertNotInLayoutOrScroll(null);
319 | if (this.mMaxVisibleItemCount == mMaxVisibleItemCount) return;
320 | this.mMaxVisibleItemCount = mMaxVisibleItemCount;
321 | removeAllViews();
322 | }
323 |
324 | /**
325 | * see {@link #mIntegerDy}
326 | */
327 | public boolean isIntegerDy() {
328 | return mIntegerDy;
329 | }
330 |
331 | /**
332 | * see {@link #mIntegerDy}
333 | */
334 | public void setIntegerDy(boolean mIntegerDy) {
335 | this.mIntegerDy = mIntegerDy;
336 | }
337 |
338 | /**
339 | * Calculates the view layout order. (e.g. from end to start or start to end)
340 | * RTL layout support is applied automatically. So if layout is RTL and
341 | * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
342 | */
343 | private void resolveShouldLayoutReverse() {
344 | if (mOrientation == HORIZONTAL && getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
345 | mReverseLayout = !mReverseLayout;
346 | }
347 | }
348 |
349 | /**
350 | * Returns if views are laid out from the opposite direction of the layout.
351 | *
352 | * @return If layout is reversed or not.
353 | * @see #setReverseLayout(boolean)
354 | */
355 | public boolean getReverseLayout() {
356 | return mReverseLayout;
357 | }
358 |
359 | /**
360 | * Used to reverse item traversal and layout order.
361 | * This behaves similar to the layout change for RTL views. When set to true, first item is
362 | * laid out at the end of the UI, second item is laid out before it etc.
363 | *
364 | * For horizontal layouts, it depends on the layout direction.
365 | * When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will
366 | * layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
367 | * from LTR.
368 | */
369 | public void setReverseLayout(boolean reverseLayout) {
370 | assertNotInLayoutOrScroll(null);
371 | if (reverseLayout == mReverseLayout) {
372 | return;
373 | }
374 | mReverseLayout = reverseLayout;
375 | removeAllViews();
376 | }
377 |
378 | @Override
379 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
380 | LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext());
381 | linearSmoothScroller.setTargetPosition(position);
382 | startSmoothScroll(linearSmoothScroller);
383 | }
384 |
385 | public PointF computeScrollVectorForPosition(int targetPosition) {
386 | if (getChildCount() == 0) {
387 | return null;
388 | }
389 | final int firstChildPos = getPosition(getChildAt(0));
390 | final float direction = targetPosition < firstChildPos == !mReverseLayout ?
391 | -1 / getDistanceRatio() : 1 / getDistanceRatio();
392 | if (mOrientation == HORIZONTAL) {
393 | return new PointF(direction, 0);
394 | } else {
395 | return new PointF(0, direction);
396 | }
397 | }
398 |
399 | @Override
400 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
401 | if (state.getItemCount() == 0) {
402 | removeAndRecycleAllViews(recycler);
403 | mOffset = 0;
404 | return;
405 | }
406 |
407 | ensureLayoutState();
408 | resolveShouldLayoutReverse();
409 |
410 | //make sure properties are correct while measure more than once
411 | View scrap = recycler.getViewForPosition(0);
412 | measureChildWithMargins(scrap, 0, 0);
413 | mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap);
414 | mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap);
415 | mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2;
416 | mSpaceInOther = (getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2;
417 | mInterval = setInterval();
418 | setUp();
419 | mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1;
420 | mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1;
421 |
422 | if (mPendingSavedState != null) {
423 | mReverseLayout = mPendingSavedState.isReverseLayout;
424 | mPendingScrollPosition = mPendingSavedState.position;
425 | mOffset = mPendingSavedState.offset;
426 | }
427 |
428 | if (mPendingScrollPosition != RecyclerView.NO_POSITION) {
429 | mOffset = mReverseLayout ?
430 | mPendingScrollPosition * -mInterval : mPendingScrollPosition * mInterval;
431 | }
432 |
433 | detachAndScrapAttachedViews(recycler);
434 | layoutItems(recycler);
435 | }
436 |
437 | public int getTotalSpaceInOther() {
438 | if (mOrientation == HORIZONTAL) {
439 | return getHeight() - getPaddingTop()
440 | - getPaddingBottom();
441 | } else {
442 | return getWidth() - getPaddingLeft()
443 | - getPaddingRight();
444 | }
445 | }
446 |
447 | @Override
448 | public void onLayoutCompleted(RecyclerView.State state) {
449 | super.onLayoutCompleted(state);
450 | mPendingSavedState = null;
451 | mPendingScrollPosition = RecyclerView.NO_POSITION;
452 | }
453 |
454 | void ensureLayoutState() {
455 | if (mOrientationHelper == null) {
456 | mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
457 | }
458 | }
459 |
460 | /**
461 | * You can set up your own properties here or change the exist properties like mSpaceMain and mSpaceInOther
462 | */
463 | protected void setUp() {
464 |
465 | }
466 |
467 | private float getProperty(int position) {
468 | return mReverseLayout ? position * -mInterval : position * mInterval;
469 | }
470 |
471 | @Override
472 | public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {
473 | removeAllViews();
474 | mOffset = 0;
475 | }
476 |
477 | @Override
478 | public void scrollToPosition(int position) {
479 | mPendingScrollPosition = position;
480 | mOffset = mReverseLayout ? position * -mInterval : position * mInterval;
481 | requestLayout();
482 | }
483 |
484 | @Override
485 | public int computeHorizontalScrollOffset(RecyclerView.State state) {
486 | return computeScrollOffset();
487 | }
488 |
489 | @Override
490 | public int computeVerticalScrollOffset(RecyclerView.State state) {
491 | return computeScrollOffset();
492 | }
493 |
494 | @Override
495 | public int computeHorizontalScrollExtent(RecyclerView.State state) {
496 | return computeScrollExtent();
497 | }
498 |
499 | @Override
500 | public int computeVerticalScrollExtent(RecyclerView.State state) {
501 | return computeScrollExtent();
502 | }
503 |
504 | @Override
505 | public int computeHorizontalScrollRange(RecyclerView.State state) {
506 | return computeScrollRange();
507 | }
508 |
509 | @Override
510 | public int computeVerticalScrollRange(RecyclerView.State state) {
511 | return computeScrollRange();
512 | }
513 |
514 | private int computeScrollOffset() {
515 | if (getChildCount() == 0) {
516 | return 0;
517 | }
518 |
519 | if (!mSmoothScrollbarEnabled) {
520 | return !mReverseLayout ?
521 | getCurrentPosition() : getItemCount() - getCurrentPosition() - 1;
522 | }
523 |
524 | final float realOffset = getOffsetOfRightAdapterPosition();
525 | return !mReverseLayout ? (int) realOffset : (int) ((getItemCount() - 1) * mInterval + realOffset);
526 | }
527 |
528 | private int computeScrollExtent() {
529 | if (getChildCount() == 0) {
530 | return 0;
531 | }
532 |
533 | if (!mSmoothScrollbarEnabled) {
534 | return 1;
535 | }
536 |
537 | return (int) mInterval;
538 | }
539 |
540 | private int computeScrollRange() {
541 | if (getChildCount() == 0) {
542 | return 0;
543 | }
544 |
545 | if (!mSmoothScrollbarEnabled) {
546 | return getItemCount();
547 | }
548 |
549 | return (int) (getItemCount() * mInterval);
550 | }
551 |
552 | @Override
553 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
554 | if (mOrientation == VERTICAL) {
555 | return 0;
556 | }
557 | return scrollBy(dx, recycler, state);
558 | }
559 |
560 | @Override
561 | public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
562 | if (mOrientation == HORIZONTAL) {
563 | return 0;
564 | }
565 | return scrollBy(dy, recycler, state);
566 | }
567 |
568 | private int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
569 | if (getChildCount() == 0 || dy == 0) {
570 | return 0;
571 | }
572 | ensureLayoutState();
573 | int willScroll = dy;
574 |
575 | float realDx = dy / getDistanceRatio();
576 | if (Math.abs(realDx) < 0.00000001f) {
577 | return 0;
578 | }
579 | float targetOffset = mOffset + realDx;
580 |
581 | //handle the boundary
582 | if (!mInfinite && targetOffset < getMinOffset()) {
583 | willScroll -= (targetOffset - getMinOffset()) * getDistanceRatio();
584 | } else if (!mInfinite && targetOffset > getMaxOffset()) {
585 | willScroll = (int) ((getMaxOffset() - mOffset) * getDistanceRatio());
586 | }
587 |
588 | if (mIntegerDy) {
589 | realDx = (int) (willScroll / getDistanceRatio());
590 | } else {
591 | realDx = willScroll / getDistanceRatio();
592 | }
593 |
594 | mOffset += realDx;
595 |
596 | // we re-layout all current views in the right place
597 | for (int i = 0; i < getChildCount(); i++) {
598 | final View scrap = getChildAt(i);
599 | final float delta = propertyChangeWhenScroll(scrap) - realDx;
600 | layoutScrap(scrap, delta);
601 | }
602 |
603 | //handle recycle
604 | layoutItems(recycler);
605 |
606 | return willScroll;
607 | }
608 |
609 | private void layoutItems(RecyclerView.Recycler recycler) {
610 | detachAndScrapAttachedViews(recycler);
611 |
612 | // make sure that current position start from 0 to 1
613 | final int currentPos = mReverseLayout ?
614 | -getCurrentPositionOffset() : getCurrentPositionOffset();
615 | int start = currentPos - mLeftItems;
616 | int end = currentPos + mRightItems;
617 |
618 | // handle max visible count
619 | if (useMaxVisibleCount()) {
620 | boolean isEven = mMaxVisibleItemCount % 2 == 0;
621 | if (isEven) {
622 | int offset = mMaxVisibleItemCount / 2;
623 | start = currentPos - offset + 1;
624 | end = currentPos + offset + 1;
625 | } else {
626 | int offset = (mMaxVisibleItemCount - 1) / 2;
627 | start = currentPos - offset;
628 | end = currentPos + offset + 1;
629 | }
630 | }
631 |
632 | final int itemCount = getItemCount();
633 | if (!mInfinite) {
634 | if (start < 0) {
635 | start = 0;
636 | if (useMaxVisibleCount()) end = mMaxVisibleItemCount;
637 | }
638 | if (end > itemCount) end = itemCount;
639 | }
640 |
641 | float lastOrderWeight = Float.MIN_VALUE;
642 | for (int i = start; i < end; i++) {
643 | if (useMaxVisibleCount() || !removeCondition(getProperty(i) - mOffset)) {
644 | // start and end base on current position,
645 | // so we need to calculate the adapter position
646 | int adapterPosition = i;
647 | if (i >= itemCount) {
648 | adapterPosition %= itemCount;
649 | } else if (i < 0) {
650 | int delta = (-adapterPosition) % itemCount;
651 | if (delta == 0) delta = itemCount;
652 | adapterPosition = itemCount - delta;
653 | }
654 | final View scrap = recycler.getViewForPosition(adapterPosition);
655 | measureChildWithMargins(scrap, 0, 0);
656 | resetViewProperty(scrap);
657 | // we need i to calculate the real offset of current view
658 | final float targetOffset = getProperty(i) - mOffset;
659 | layoutScrap(scrap, targetOffset);
660 | final float orderWeight = mEnableBringCenterToFront ?
661 | setViewElevation(scrap, targetOffset) : adapterPosition;
662 | if (orderWeight > lastOrderWeight) {
663 | addView(scrap);
664 | } else {
665 | addView(scrap, 0);
666 | }
667 | lastOrderWeight = orderWeight;
668 | }
669 | }
670 | }
671 |
672 | private boolean useMaxVisibleCount() {
673 | return mMaxVisibleItemCount != DETERMINE_BY_MAX_AND_MIN;
674 | }
675 |
676 | private boolean removeCondition(float targetOffset) {
677 | return targetOffset > maxRemoveOffset() || targetOffset < minRemoveOffset();
678 | }
679 |
680 | private void resetViewProperty(View v) {
681 | v.setRotation(0);
682 | v.setRotationY(0);
683 | v.setRotationX(0);
684 | v.setScaleX(1f);
685 | v.setScaleY(1f);
686 | v.setAlpha(1f);
687 | }
688 |
689 | private float getMaxOffset() {
690 | return !mReverseLayout ? (getItemCount() - 1) * mInterval : 0;
691 | }
692 |
693 | private float getMinOffset() {
694 | return !mReverseLayout ? 0 : -(getItemCount() - 1) * mInterval;
695 | }
696 |
697 | private void layoutScrap(View scrap, float targetOffset) {
698 | final int left = calItemLeft(scrap, targetOffset);
699 | final int top = calItemTop(scrap, targetOffset);
700 | if (mOrientation == VERTICAL) {
701 | layoutDecorated(scrap, mSpaceInOther + left, mSpaceMain + top,
702 | mSpaceInOther + left + mDecoratedMeasurementInOther, mSpaceMain + top + mDecoratedMeasurement);
703 | } else {
704 | layoutDecorated(scrap, mSpaceMain + left, mSpaceInOther + top,
705 | mSpaceMain + left + mDecoratedMeasurement, mSpaceInOther + top + mDecoratedMeasurementInOther);
706 | }
707 | setItemViewProperty(scrap, targetOffset);
708 | }
709 |
710 | protected int calItemLeft(View itemView, float targetOffset) {
711 | return mOrientation == VERTICAL ? 0 : (int) targetOffset;
712 | }
713 |
714 | protected int calItemTop(View itemView, float targetOffset) {
715 | return mOrientation == VERTICAL ? (int) targetOffset : 0;
716 | }
717 |
718 | /**
719 | * when the target offset reach this,
720 | * the view will be removed and recycled in {@link #layoutItems(RecyclerView.Recycler)}
721 | */
722 | protected float maxRemoveOffset() {
723 | return mOrientationHelper.getTotalSpace() - mSpaceMain;
724 | }
725 |
726 | /**
727 | * when the target offset reach this,
728 | * the view will be removed and recycled in {@link #layoutItems(RecyclerView.Recycler)}
729 | */
730 | protected float minRemoveOffset() {
731 | return -mDecoratedMeasurement - mOrientationHelper.getStartAfterPadding() - mSpaceMain;
732 | }
733 |
734 | protected float propertyChangeWhenScroll(View itemView) {
735 | if (mOrientation == VERTICAL)
736 | return itemView.getTop() - mSpaceMain;
737 | return itemView.getLeft() - mSpaceMain;
738 | }
739 |
740 | protected float getDistanceRatio() {
741 | return 1;
742 | }
743 |
744 | public int getCurrentPosition() {
745 | int position = getCurrentPositionOffset();
746 | if (!mInfinite) return Math.abs(position);
747 | position = !mReverseLayout ?
748 | //take care of position = getItemCount()
749 | (position >= 0 ?
750 | position % getItemCount() :
751 | getItemCount() + position % getItemCount()) :
752 | (position > 0 ?
753 | getItemCount() - position % getItemCount() :
754 | -position % getItemCount());
755 | return position;
756 | }
757 |
758 | private int getCurrentPositionOffset() {
759 | return Math.round(mOffset / mInterval);
760 | }
761 |
762 | /**
763 | * Sometimes we need to get the right offset of matching adapter position
764 | * cause when {@link #mInfinite} is set true, there will be no limitation of {@link #mOffset}
765 | */
766 | private float getOffsetOfRightAdapterPosition() {
767 | if (mReverseLayout)
768 | return mInfinite ?
769 | (mOffset <= 0 ?
770 | (mOffset % (mInterval * getItemCount())) :
771 | (getItemCount() * -mInterval + mOffset % (mInterval * getItemCount()))) :
772 | mOffset;
773 | else
774 | return mInfinite ?
775 | (mOffset >= 0 ?
776 | (mOffset % (mInterval * getItemCount())) :
777 | (getItemCount() * mInterval + mOffset % (mInterval * getItemCount()))) :
778 | mOffset;
779 | }
780 |
781 | /**
782 | * @return the dy between center and current position
783 | */
784 | public int getOffsetToCenter() {
785 | if (mInfinite)
786 | return (int) ((getCurrentPositionOffset() * mInterval - mOffset) * getDistanceRatio());
787 | return (int) ((getCurrentPosition() *
788 | (!mReverseLayout ? mInterval : -mInterval) - mOffset) * getDistanceRatio());
789 | }
790 |
791 | public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
792 | this.onPageChangeListener = onPageChangeListener;
793 | }
794 |
795 | public void setInfinite(boolean enable) {
796 | assertNotInLayoutOrScroll(null);
797 | if (enable == mInfinite) {
798 | return;
799 | }
800 | mInfinite = enable;
801 | requestLayout();
802 | }
803 |
804 | public boolean getInfinite() {
805 | return mInfinite;
806 | }
807 |
808 | /**
809 | * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
810 | * based on the number of visible pixels in the visible items. This however assumes that all
811 | * list items have similar or equal widths or heights (depending on list orientation).
812 | * If you use a list in which items have different dimensions, the scrollbar will change
813 | * appearance as the user scrolls through the list. To avoid this issue, you need to disable
814 | * this property.
815 | *
816 | * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
817 | * solely on the number of items in the adapter and the position of the visible items inside
818 | * the adapter. This provides a stable scrollbar as the user navigates through a list of items
819 | * with varying widths / heights.
820 | *
821 | * @param enabled Whether or not to enable smooth scrollbar.
822 | * @see #setSmoothScrollbarEnabled(boolean)
823 | */
824 | public void setSmoothScrollbarEnabled(boolean enabled) {
825 | mSmoothScrollbarEnabled = enabled;
826 | }
827 |
828 | public void setEnableBringCenterToFront(boolean bringCenterToTop) {
829 | assertNotInLayoutOrScroll(null);
830 | if (mEnableBringCenterToFront == bringCenterToTop) {
831 | return;
832 | }
833 | this.mEnableBringCenterToFront = bringCenterToTop;
834 | requestLayout();
835 | }
836 |
837 | public boolean getEnableBringCenterToFront() {
838 | return mEnableBringCenterToFront;
839 | }
840 |
841 | /**
842 | * Returns the current state of the smooth scrollbar feature. It is enabled by default.
843 | *
844 | * @return True if smooth scrollbar is enabled, false otherwise.
845 | * @see #setSmoothScrollbarEnabled(boolean)
846 | */
847 | public boolean getSmoothScrollbarEnabled() {
848 | return mSmoothScrollbarEnabled;
849 | }
850 |
851 | private static class SavedState implements Parcelable {
852 | int position;
853 | float offset;
854 | boolean isReverseLayout;
855 |
856 | SavedState() {
857 |
858 | }
859 |
860 | SavedState(Parcel in) {
861 | position = in.readInt();
862 | offset = in.readFloat();
863 | isReverseLayout = in.readInt() == 1;
864 | }
865 |
866 | public SavedState(SavedState other) {
867 | position = other.position;
868 | offset = other.offset;
869 | isReverseLayout = other.isReverseLayout;
870 | }
871 |
872 | @Override
873 | public int describeContents() {
874 | return 0;
875 | }
876 |
877 | @Override
878 | public void writeToParcel(Parcel dest, int flags) {
879 | dest.writeInt(position);
880 | dest.writeFloat(offset);
881 | dest.writeInt(isReverseLayout ? 1 : 0);
882 | }
883 |
884 | public static final Creatortrue
so that views will be available to other RecyclerViews
217 | * immediately.
218 | *