BaseGridView.java revision d586ba8825b418d9589436725bfdead30f0dc075
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.content.Context;
17import android.content.res.TypedArray;
18import android.graphics.Rect;
19import android.support.v17.leanback.R;
20import android.support.v7.widget.RecyclerView;
21import android.util.AttributeSet;
22import android.view.Gravity;
23import android.view.KeyEvent;
24import android.view.MotionEvent;
25import android.view.View;
26
27/**
28 * Base class for vertically and horizontally scrolling lists. The items come
29 * from the {@link RecyclerView.Adapter} associated with this view.
30 * @hide
31 */
32abstract class BaseGridView extends RecyclerView {
33
34    /**
35     * Always keep focused item at a aligned position.  Developer can use
36     * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
37     * In this mode, the last focused position will be remembered and restored when focus
38     * is back to the view.
39     */
40    public final static int FOCUS_SCROLL_ALIGNED = 0;
41
42    /**
43     * Scroll to make the focused item inside client area.
44     */
45    public final static int FOCUS_SCROLL_ITEM = 1;
46
47    /**
48     * Scroll a page of items when focusing to item outside the client area.
49     * The page size matches the client area size of RecyclerView.
50     */
51    public final static int FOCUS_SCROLL_PAGE = 2;
52
53    /**
54     * The first item is aligned with the low edge of the viewport. When
55     * navigating away from the first item, the focus maintains a middle
56     * location.
57     * <p>
58     * For HorizontalGridView, low edge refers to left edge when RTL is false or
59     * right edge when RTL is true.
60     * For VerticalGridView, low edge refers to top edge.
61     * <p>
62     * The middle location is calculated by "windowAlignOffset" and
63     * "windowAlignOffsetPercent"; if neither of these two is defined, the
64     * default value is 1/2 of the size.
65     */
66    public final static int WINDOW_ALIGN_LOW_EDGE = 1;
67
68    /**
69     * The last item is aligned with the high edge of the viewport when
70     * navigating to the end of list. When navigating away from the end, the
71     * focus maintains a middle location.
72     * <p>
73     * For HorizontalGridView, high edge refers to right edge when RTL is false or
74     * left edge when RTL is true.
75     * For VerticalGridView, high edge refers to bottom edge.
76     * <p>
77     * The middle location is calculated by "windowAlignOffset" and
78     * "windowAlignOffsetPercent"; if neither of these two is defined, the
79     * default value is 1/2 of the size.
80     */
81    public final static int WINDOW_ALIGN_HIGH_EDGE = 1 << 1;
82
83    /**
84     * The first item and last item are aligned with the two edges of the
85     * viewport. When navigating in the middle of list, the focus maintains a
86     * middle location.
87     * <p>
88     * The middle location is calculated by "windowAlignOffset" and
89     * "windowAlignOffsetPercent"; if neither of these two is defined, the
90     * default value is 1/2 of the size.
91     */
92    public final static int WINDOW_ALIGN_BOTH_EDGE =
93            WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE;
94
95    /**
96     * The focused item always stays in a middle location.
97     * <p>
98     * The middle location is calculated by "windowAlignOffset" and
99     * "windowAlignOffsetPercent"; if neither of these two is defined, the
100     * default value is 1/2 of the size.
101     */
102    public final static int WINDOW_ALIGN_NO_EDGE = 0;
103
104    /**
105     * Value indicates that percent is not used.
106     */
107    public final static float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1;
108
109    /**
110     * Value indicates that percent is not used.
111     */
112    public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1;
113
114    /**
115     * Dont save states of any child views.
116     */
117    public static final int SAVE_NO_CHILD = 0;
118
119    /**
120     * Only save on screen child views, the states are lost when they become off screen.
121     */
122    public static final int SAVE_ON_SCREEN_CHILD = 1;
123
124    /**
125     * Save on screen views plus save off screen child views states up to
126     * {@link #getSaveChildrenLimitNumber()}.
127     */
128    public static final int SAVE_LIMITED_CHILD = 2;
129
130    /**
131     * Save on screen views plus save off screen child views without any limitation.
132     * This might cause out of memory, only use it when you are dealing with limited data.
133     */
134    public static final int SAVE_ALL_CHILD = 3;
135
136    /**
137     * Listener for intercepting touch dispatch events.
138     */
139    public interface OnTouchInterceptListener {
140        /**
141         * Returns true if the touch dispatch event should be consumed.
142         */
143        public boolean onInterceptTouchEvent(MotionEvent event);
144    }
145
146    /**
147     * Listener for intercepting generic motion dispatch events.
148     */
149    public interface OnMotionInterceptListener {
150        /**
151         * Returns true if the touch dispatch event should be consumed.
152         */
153        public boolean onInterceptMotionEvent(MotionEvent event);
154    }
155
156    /**
157     * Listener for intercepting key dispatch events.
158     */
159    public interface OnKeyInterceptListener {
160        /**
161         * Returns true if the key dispatch event should be consumed.
162         */
163        public boolean onInterceptKeyEvent(KeyEvent event);
164    }
165
166    protected final GridLayoutManager mLayoutManager;
167
168    /**
169     * Animate layout changes from a child resizing or adding/removing a child.
170     */
171    private boolean mAnimateChildLayout = true;
172
173    private boolean mHasOverlappingRendering = true;
174
175    private RecyclerView.ItemAnimator mSavedItemAnimator;
176
177    private OnTouchInterceptListener mOnTouchInterceptListener;
178    private OnMotionInterceptListener mOnMotionInterceptListener;
179    private OnKeyInterceptListener mOnKeyInterceptListener;
180
181    public BaseGridView(Context context, AttributeSet attrs, int defStyle) {
182        super(context, attrs, defStyle);
183        mLayoutManager = new GridLayoutManager(this);
184        setLayoutManager(mLayoutManager);
185        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
186        setHasFixedSize(true);
187        setChildrenDrawingOrderEnabled(true);
188        setWillNotDraw(true);
189        setOverScrollMode(View.OVER_SCROLL_NEVER);
190        // Disable change animation by default on leanback.
191        // Change animation will create a new view and cause undesired
192        // focus animation between the old view and new view.
193        getItemAnimator().setSupportsChangeAnimations(false);
194    }
195
196    protected void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
197        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);
198        boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false);
199        boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false);
200        mLayoutManager.setFocusOutAllowed(throughFront, throughEnd);
201        mLayoutManager.setVerticalMargin(
202                a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0));
203        mLayoutManager.setHorizontalMargin(
204                a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0));
205        if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
206            setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
207        }
208        a.recycle();
209    }
210
211    /**
212     * Set the strategy used to scroll in response to item focus changing:
213     * <ul>
214     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
215     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
216     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
217     * </ul>
218     */
219    public void setFocusScrollStrategy(int scrollStrategy) {
220        if (scrollStrategy != FOCUS_SCROLL_ALIGNED && scrollStrategy != FOCUS_SCROLL_ITEM
221            && scrollStrategy != FOCUS_SCROLL_PAGE) {
222            throw new IllegalArgumentException("Invalid scrollStrategy");
223        }
224        mLayoutManager.setFocusScrollStrategy(scrollStrategy);
225        requestLayout();
226    }
227
228    /**
229     * Returns the strategy used to scroll in response to item focus changing.
230     * <ul>
231     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
232     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
233     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
234     * </ul>
235     */
236    public int getFocusScrollStrategy() {
237        return mLayoutManager.getFocusScrollStrategy();
238    }
239
240    /**
241     * Set how the focused item gets aligned in the view.
242     *
243     * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE},
244     *        {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or
245     *        {@link #WINDOW_ALIGN_NO_EDGE}.
246     */
247    public void setWindowAlignment(int windowAlignment) {
248        mLayoutManager.setWindowAlignment(windowAlignment);
249        requestLayout();
250    }
251
252    /**
253     * Get how the focused item gets aligned in the view.
254     *
255     * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE},
256     *         {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}.
257     */
258    public int getWindowAlignment() {
259        return mLayoutManager.getWindowAlignment();
260    }
261
262    /**
263     * Set the offset in pixels for window alignment.
264     *
265     * @param offset The number of pixels to offset.  If the offset is positive,
266     *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
267     *        if the offset is negative, the absolute value is distance from high
268     *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
269     *        Default value is 0.
270     */
271    public void setWindowAlignmentOffset(int offset) {
272        mLayoutManager.setWindowAlignmentOffset(offset);
273        requestLayout();
274    }
275
276    /**
277     * Get the offset in pixels for window alignment.
278     *
279     * @return The number of pixels to offset.  If the offset is positive,
280     *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
281     *        if the offset is negative, the absolute value is distance from high
282     *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
283     *        Default value is 0.
284     */
285    public int getWindowAlignmentOffset() {
286        return mLayoutManager.getWindowAlignmentOffset();
287    }
288
289    /**
290     * Set offset percent for window alignment in addition to {@link
291     * #getWindowAlignmentOffset()}.
292     *
293     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
294     *        width from low edge. Use
295     *        {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
296     *         Default value is 50.
297     */
298    public void setWindowAlignmentOffsetPercent(float offsetPercent) {
299        mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent);
300        requestLayout();
301    }
302
303    /**
304     * Get offset percent for window alignment in addition to
305     * {@link #getWindowAlignmentOffset()}.
306     *
307     * @return Percentage to offset. E.g., 40 means 40% of the width from the
308     *         low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if
309     *         disabled. Default value is 50.
310     */
311    public float getWindowAlignmentOffsetPercent() {
312        return mLayoutManager.getWindowAlignmentOffsetPercent();
313    }
314
315    /**
316     * Set the absolute offset in pixels for item alignment.
317     *
318     * @param offset The number of pixels to offset. Can be negative for
319     *        alignment from the high edge, or positive for alignment from the
320     *        low edge.
321     */
322    public void setItemAlignmentOffset(int offset) {
323        mLayoutManager.setItemAlignmentOffset(offset);
324        requestLayout();
325    }
326
327    /**
328     * Get the absolute offset in pixels for item alignment.
329     *
330     * @return The number of pixels to offset. Will be negative for alignment
331     *         from the high edge, or positive for alignment from the low edge.
332     *         Default value is 0.
333     */
334    public int getItemAlignmentOffset() {
335        return mLayoutManager.getItemAlignmentOffset();
336    }
337
338    /**
339     * Set to true if include padding in calculating item align offset.
340     *
341     * @param withPadding When it is true: we include left/top padding for positive
342     *          item offset, include right/bottom padding for negative item offset.
343     */
344    public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
345        mLayoutManager.setItemAlignmentOffsetWithPadding(withPadding);
346        requestLayout();
347    }
348
349    /**
350     * Returns true if include padding in calculating item align offset.
351     */
352    public boolean isItemAlignmentOffsetWithPadding() {
353        return mLayoutManager.isItemAlignmentOffsetWithPadding();
354    }
355
356    /**
357     * Set offset percent for item alignment in addition to {@link
358     * #getItemAlignmentOffset()}.
359     *
360     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
361     *        width from the low edge. Use
362     *        {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
363     */
364    public void setItemAlignmentOffsetPercent(float offsetPercent) {
365        mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent);
366        requestLayout();
367    }
368
369    /**
370     * Get offset percent for item alignment in addition to {@link
371     * #getItemAlignmentOffset()}.
372     *
373     * @return Percentage to offset. E.g., 40 means 40% of the width from the
374     *         low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if
375     *         disabled. Default value is 50.
376     */
377    public float getItemAlignmentOffsetPercent() {
378        return mLayoutManager.getItemAlignmentOffsetPercent();
379    }
380
381    /**
382     * Set the id of the view to align with. Use zero (default) for the item
383     * view itself.
384     */
385    public void setItemAlignmentViewId(int viewId) {
386        mLayoutManager.setItemAlignmentViewId(viewId);
387    }
388
389    /**
390     * Get the id of the view to align with, or zero for the item view itself.
391     */
392    public int getItemAlignmentViewId() {
393        return mLayoutManager.getItemAlignmentViewId();
394    }
395
396    /**
397     * Set the margin in pixels between two child items.
398     */
399    public void setItemMargin(int margin) {
400        mLayoutManager.setItemMargin(margin);
401        requestLayout();
402    }
403
404    /**
405     * Set the margin in pixels between two child items vertically.
406     */
407    public void setVerticalMargin(int margin) {
408        mLayoutManager.setVerticalMargin(margin);
409        requestLayout();
410    }
411
412    /**
413     * Get the margin in pixels between two child items vertically.
414     */
415    public int getVerticalMargin() {
416        return mLayoutManager.getVerticalMargin();
417    }
418
419    /**
420     * Set the margin in pixels between two child items horizontally.
421     */
422    public void setHorizontalMargin(int margin) {
423        mLayoutManager.setHorizontalMargin(margin);
424        requestLayout();
425    }
426
427    /**
428     * Get the margin in pixels between two child items horizontally.
429     */
430    public int getHorizontalMargin() {
431        return mLayoutManager.getHorizontalMargin();
432    }
433
434    /**
435     * Register a callback to be invoked when an item in BaseGridView has
436     * been laid out.
437     *
438     * @param listener The listener to be invoked.
439     */
440    public void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
441        mLayoutManager.setOnChildLaidOutListener(listener);
442    }
443
444    /**
445     * Register a callback to be invoked when an item in BaseGridView has
446     * been selected.  Note that the listener may be invoked when there is a
447     * layout pending on the view, affording the listener an opportunity to
448     * adjust the upcoming layout based on the selection state.
449     *
450     * @param listener The listener to be invoked.
451     */
452    public void setOnChildSelectedListener(OnChildSelectedListener listener) {
453        mLayoutManager.setOnChildSelectedListener(listener);
454    }
455
456    /**
457     * Change the selected item immediately without animation.
458     */
459    public void setSelectedPosition(int position) {
460        mLayoutManager.setSelection(this, position);
461    }
462
463    /**
464     * Change the selected item and run an animation to scroll to the target
465     * position.
466     */
467    public void setSelectedPositionSmooth(int position) {
468        mLayoutManager.setSelectionSmooth(this, position);
469    }
470
471    /**
472     * Get the selected item position.
473     */
474    public int getSelectedPosition() {
475        return mLayoutManager.getSelection();
476    }
477
478    /**
479     * Set if an animation should run when a child changes size or when adding
480     * or removing a child.
481     * <p><i>Unstable API, might change later.</i>
482     */
483    public void setAnimateChildLayout(boolean animateChildLayout) {
484        if (mAnimateChildLayout != animateChildLayout) {
485            mAnimateChildLayout = animateChildLayout;
486            if (!mAnimateChildLayout) {
487                mSavedItemAnimator = getItemAnimator();
488                super.setItemAnimator(null);
489            } else {
490                super.setItemAnimator(mSavedItemAnimator);
491            }
492        }
493    }
494
495    /**
496     * Return true if an animation will run when a child changes size or when
497     * adding or removing a child.
498     * <p><i>Unstable API, might change later.</i>
499     */
500    public boolean isChildLayoutAnimated() {
501        return mAnimateChildLayout;
502    }
503
504    /**
505     * Describes how the child views are positioned. Defaults to
506     * GRAVITY_TOP|GRAVITY_START.
507     *
508     * @param gravity See {@link android.view.Gravity}
509     */
510    public void setGravity(int gravity) {
511        mLayoutManager.setGravity(gravity);
512        requestLayout();
513    }
514
515    @Override
516    public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
517        return mLayoutManager.gridOnRequestFocusInDescendants(this, direction,
518                previouslyFocusedRect);
519    }
520
521    /**
522     * Get the x/y offsets to final position from current position if the view
523     * is selected.
524     *
525     * @param view The view to get offsets.
526     * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of
527     *        Y.
528     */
529    public void getViewSelectedOffsets(View view, int[] offsets) {
530        mLayoutManager.getViewSelectedOffsets(view, offsets);
531    }
532
533    @Override
534    public int getChildDrawingOrder(int childCount, int i) {
535        return mLayoutManager.getChildDrawingOrder(this, childCount, i);
536    }
537
538    final boolean isChildrenDrawingOrderEnabledInternal() {
539        return isChildrenDrawingOrderEnabled();
540    }
541
542    /**
543     * Disable or enable focus search.
544     */
545    public final void setFocusSearchDisabled(boolean disabled) {
546        // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
547        // re-gain focus after a BACK key pressed, so block children focus during transition.
548        setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS: FOCUS_AFTER_DESCENDANTS);
549        mLayoutManager.setFocusSearchDisabled(disabled);
550    }
551
552    /**
553     * Return true if focus search is disabled.
554     */
555    public final boolean isFocusSearchDisabled() {
556        return mLayoutManager.isFocusSearchDisabled();
557    }
558
559    /**
560     * Enable or disable layout.  All children will be removed when layout is
561     * disabled.
562     */
563    public void setLayoutEnabled(boolean layoutEnabled) {
564        mLayoutManager.setLayoutEnabled(layoutEnabled);
565    }
566
567    /**
568     * Change and override children's visibility.
569     */
570    public void setChildrenVisibility(int visibility) {
571        mLayoutManager.setChildrenVisibility(visibility);
572    }
573
574    /**
575     * Enable or disable pruning child.  Disable is useful during transition.
576     */
577    public void setPruneChild(boolean pruneChild) {
578        mLayoutManager.setPruneChild(pruneChild);
579    }
580
581    /**
582     * Enable or disable scrolling.  Disable is useful during transition.
583     */
584    public void setScrollEnabled(boolean scrollEnabled) {
585        mLayoutManager.setScrollEnabled(scrollEnabled);
586    }
587
588    /**
589     * Returns true if scrolling is enabled.
590     */
591    public boolean isScrollEnabled() {
592        return mLayoutManager.isScrollEnabled();
593    }
594
595    /**
596     * Returns true if the view at the given position has a same row sibling
597     * in front of it.  This will return true if first item view is not created.
598     * So application should check in both {@link OnChildSelectedListener} and {@link
599     * OnChildLaidOutListener}.
600     *
601     * @param position Position in adapter.
602     */
603    public boolean hasPreviousViewInSameRow(int position) {
604        return mLayoutManager.hasPreviousViewInSameRow(position);
605    }
606
607    /**
608     * Enable or disable the default "focus draw at last" order rule.
609     */
610    public void setFocusDrawingOrderEnabled(boolean enabled) {
611        super.setChildrenDrawingOrderEnabled(enabled);
612    }
613
614    /**
615     * Returns true if default "focus draw at last" order rule is enabled.
616     */
617    public boolean isFocusDrawingOrderEnabled() {
618        return super.isChildrenDrawingOrderEnabled();
619    }
620
621    /**
622     * Sets the touch intercept listener.
623     */
624    public void setOnTouchInterceptListener(OnTouchInterceptListener listener) {
625        mOnTouchInterceptListener = listener;
626    }
627
628    /**
629     * Sets the generic motion intercept listener.
630     */
631    public void setOnMotionInterceptListener(OnMotionInterceptListener listener) {
632        mOnMotionInterceptListener = listener;
633    }
634
635    /**
636     * Sets the key intercept listener.
637     */
638    public void setOnKeyInterceptListener(OnKeyInterceptListener listener) {
639        mOnKeyInterceptListener = listener;
640    }
641
642    @Override
643    public boolean dispatchKeyEvent(KeyEvent event) {
644        if (mOnKeyInterceptListener != null) {
645            if (mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
646                return true;
647            }
648        }
649        return super.dispatchKeyEvent(event);
650    }
651
652    @Override
653    public boolean dispatchTouchEvent(MotionEvent event) {
654        if (mOnTouchInterceptListener != null) {
655            if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) {
656                return true;
657            }
658        }
659        return super.dispatchTouchEvent(event);
660    }
661
662    @Override
663    public boolean dispatchGenericFocusedEvent(MotionEvent event) {
664        if (mOnMotionInterceptListener != null) {
665            if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
666                return true;
667            }
668        }
669        return super.dispatchGenericFocusedEvent(event);
670    }
671
672    /**
673     * @return policy for saving children.  One of {@link #SAVE_NO_CHILD}
674     * {@link #SAVE_ON_SCREEN_CHILD} {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
675     */
676    public final int getSaveChildrenPolicy() {
677        return mLayoutManager.mChildrenStates.getSavePolicy();
678    }
679
680    /**
681     * @return The limit number when {@link #getSaveChildrenPolicy()} is
682     *         {@link #SAVE_LIMITED_CHILD}
683     */
684    public final int getSaveChildrenLimitNumber() {
685        return mLayoutManager.mChildrenStates.getLimitNumber();
686    }
687
688    /**
689     * Set policy for saving children.
690     * @param savePolicy One of {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
691     * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
692     */
693    public final void setSaveChildrenPolicy(int savePolicy) {
694        mLayoutManager.mChildrenStates.setSavePolicy(savePolicy);
695    }
696
697    /**
698     * Set limit number when {@link #getSaveChildrenPolicy()} is {@link #SAVE_LIMITED_CHILD}.
699     */
700    public final void setSaveChildrenLimitNumber(int limitNumber) {
701        mLayoutManager.mChildrenStates.setLimitNumber(limitNumber);
702    }
703
704    @Override
705    public boolean hasOverlappingRendering() {
706        return mHasOverlappingRendering;
707    }
708
709    public void setHasOverlappingRendering(boolean hasOverlapping) {
710        mHasOverlappingRendering = hasOverlapping;
711    }
712
713    /**
714     * Notify layout manager that layout directionality has been updated
715     */
716    @Override
717    public void onRtlPropertiesChanged(int layoutDirection) {
718        mLayoutManager.onRtlPropertiesChanged(layoutDirection);
719    }
720
721    @Override
722    public void onChildDetachedFromWindow(View child) {
723        super.onChildDetachedFromWindow(child);
724        mLayoutManager.onChildDetachedFromWindow(getChildViewHolder(child));
725    }
726
727}
728