BaseGridView.java revision 06388129e48b308b25bfd85e6f136db8528d4c62
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 * An abstract base class for vertically and horizontally scrolling lists. The items come
29 * from the {@link RecyclerView.Adapter} associated with this view.
30 * Do not directly use this class, use {@link VerticalGridView} and {@link HorizontalGridView}.
31 * @hide
32 */
33abstract class BaseGridView extends RecyclerView {
34
35    /**
36     * Always keep focused item at a aligned position.  Developer can use
37     * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
38     * In this mode, the last focused position will be remembered and restored when focus
39     * is back to the view.
40     */
41    public final static int FOCUS_SCROLL_ALIGNED = 0;
42
43    /**
44     * Scroll to make the focused item inside client area.
45     */
46    public final static int FOCUS_SCROLL_ITEM = 1;
47
48    /**
49     * Scroll a page of items when focusing to item outside the client area.
50     * The page size matches the client area size of RecyclerView.
51     */
52    public final static int FOCUS_SCROLL_PAGE = 2;
53
54    /**
55     * The first item is aligned with the low edge of the viewport. When
56     * navigating away from the first item, the focus maintains a middle
57     * location.
58     * <p>
59     * For HorizontalGridView, low edge refers to left edge when RTL is false or
60     * right edge when RTL is true.
61     * For VerticalGridView, low edge refers to top edge.
62     * <p>
63     * The middle location is calculated by "windowAlignOffset" and
64     * "windowAlignOffsetPercent"; if neither of these two is defined, the
65     * default value is 1/2 of the size.
66     */
67    public final static int WINDOW_ALIGN_LOW_EDGE = 1;
68
69    /**
70     * The last item is aligned with the high edge of the viewport when
71     * navigating to the end of list. When navigating away from the end, the
72     * focus maintains a middle location.
73     * <p>
74     * For HorizontalGridView, high edge refers to right edge when RTL is false or
75     * left edge when RTL is true.
76     * For VerticalGridView, high edge refers to bottom edge.
77     * <p>
78     * The middle location is calculated by "windowAlignOffset" and
79     * "windowAlignOffsetPercent"; if neither of these two is defined, the
80     * default value is 1/2 of the size.
81     */
82    public final static int WINDOW_ALIGN_HIGH_EDGE = 1 << 1;
83
84    /**
85     * The first item and last item are aligned with the two edges of the
86     * viewport. When navigating in the middle of list, the focus maintains a
87     * middle location.
88     * <p>
89     * The middle location is calculated by "windowAlignOffset" and
90     * "windowAlignOffsetPercent"; if neither of these two is defined, the
91     * default value is 1/2 of the size.
92     */
93    public final static int WINDOW_ALIGN_BOTH_EDGE =
94            WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE;
95
96    /**
97     * The focused item always stays in a middle location.
98     * <p>
99     * The middle location is calculated by "windowAlignOffset" and
100     * "windowAlignOffsetPercent"; if neither of these two is defined, the
101     * default value is 1/2 of the size.
102     */
103    public final static int WINDOW_ALIGN_NO_EDGE = 0;
104
105    /**
106     * Value indicates that percent is not used.
107     */
108    public final static float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1;
109
110    /**
111     * Value indicates that percent is not used.
112     */
113    public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED =
114            ItemAlignmentFacet.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
115
116    /**
117     * Dont save states of any child views.
118     */
119    public static final int SAVE_NO_CHILD = 0;
120
121    /**
122     * Only save on screen child views, the states are lost when they become off screen.
123     */
124    public static final int SAVE_ON_SCREEN_CHILD = 1;
125
126    /**
127     * Save on screen views plus save off screen child views states up to
128     * {@link #getSaveChildrenLimitNumber()}.
129     */
130    public static final int SAVE_LIMITED_CHILD = 2;
131
132    /**
133     * Save on screen views plus save off screen child views without any limitation.
134     * This might cause out of memory, only use it when you are dealing with limited data.
135     */
136    public static final int SAVE_ALL_CHILD = 3;
137
138    /**
139     * Listener for intercepting touch dispatch events.
140     */
141    public interface OnTouchInterceptListener {
142        /**
143         * Returns true if the touch dispatch event should be consumed.
144         */
145        public boolean onInterceptTouchEvent(MotionEvent event);
146    }
147
148    /**
149     * Listener for intercepting generic motion dispatch events.
150     */
151    public interface OnMotionInterceptListener {
152        /**
153         * Returns true if the touch dispatch event should be consumed.
154         */
155        public boolean onInterceptMotionEvent(MotionEvent event);
156    }
157
158    /**
159     * Listener for intercepting key dispatch events.
160     */
161    public interface OnKeyInterceptListener {
162        /**
163         * Returns true if the key dispatch event should be consumed.
164         */
165        public boolean onInterceptKeyEvent(KeyEvent event);
166    }
167
168    public interface OnUnhandledKeyListener {
169        /**
170         * Returns true if the key event should be consumed.
171         */
172        public boolean onUnhandledKey(KeyEvent event);
173    }
174
175    final GridLayoutManager mLayoutManager;
176
177    /**
178     * Animate layout changes from a child resizing or adding/removing a child.
179     */
180    private boolean mAnimateChildLayout = true;
181
182    private boolean mHasOverlappingRendering = true;
183
184    private RecyclerView.ItemAnimator mSavedItemAnimator;
185
186    private OnTouchInterceptListener mOnTouchInterceptListener;
187    private OnMotionInterceptListener mOnMotionInterceptListener;
188    private OnKeyInterceptListener mOnKeyInterceptListener;
189    private RecyclerView.RecyclerListener mChainedRecyclerListener;
190    private OnUnhandledKeyListener mOnUnhandledKeyListener;
191
192    public BaseGridView(Context context, AttributeSet attrs, int defStyle) {
193        super(context, attrs, defStyle);
194        mLayoutManager = new GridLayoutManager(this);
195        setLayoutManager(mLayoutManager);
196        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
197        setHasFixedSize(true);
198        setChildrenDrawingOrderEnabled(true);
199        setWillNotDraw(true);
200        setOverScrollMode(View.OVER_SCROLL_NEVER);
201        // Disable change animation by default on leanback.
202        // Change animation will create a new view and cause undesired
203        // focus animation between the old view and new view.
204        getItemAnimator().setSupportsChangeAnimations(false);
205        super.setRecyclerListener(new RecyclerView.RecyclerListener() {
206            @Override
207            public void onViewRecycled(RecyclerView.ViewHolder holder) {
208                mLayoutManager.onChildRecycled(holder);
209                if (mChainedRecyclerListener != null) {
210                    mChainedRecyclerListener.onViewRecycled(holder);
211                }
212            }
213        });
214    }
215
216    protected void initBaseGridViewAttributes(Context context, AttributeSet attrs) {
217        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView);
218        boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false);
219        boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false);
220        mLayoutManager.setFocusOutAllowed(throughFront, throughEnd);
221        mLayoutManager.setVerticalMargin(
222                a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0));
223        mLayoutManager.setHorizontalMargin(
224                a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0));
225        if (a.hasValue(R.styleable.lbBaseGridView_android_gravity)) {
226            setGravity(a.getInt(R.styleable.lbBaseGridView_android_gravity, Gravity.NO_GRAVITY));
227        }
228        a.recycle();
229    }
230
231    /**
232     * Sets the strategy used to scroll in response to item focus changing:
233     * <ul>
234     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
235     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
236     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
237     * </ul>
238     */
239    public void setFocusScrollStrategy(int scrollStrategy) {
240        if (scrollStrategy != FOCUS_SCROLL_ALIGNED && scrollStrategy != FOCUS_SCROLL_ITEM
241            && scrollStrategy != FOCUS_SCROLL_PAGE) {
242            throw new IllegalArgumentException("Invalid scrollStrategy");
243        }
244        mLayoutManager.setFocusScrollStrategy(scrollStrategy);
245        requestLayout();
246    }
247
248    /**
249     * Returns the strategy used to scroll in response to item focus changing.
250     * <ul>
251     * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li>
252     * <li>{@link #FOCUS_SCROLL_ITEM}</li>
253     * <li>{@link #FOCUS_SCROLL_PAGE}</li>
254     * </ul>
255     */
256    public int getFocusScrollStrategy() {
257        return mLayoutManager.getFocusScrollStrategy();
258    }
259
260    /**
261     * Sets the method for focused item alignment in the view.
262     *
263     * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE},
264     *        {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or
265     *        {@link #WINDOW_ALIGN_NO_EDGE}.
266     */
267    public void setWindowAlignment(int windowAlignment) {
268        mLayoutManager.setWindowAlignment(windowAlignment);
269        requestLayout();
270    }
271
272    /**
273     * Returns the method for focused item alignment in the view.
274     *
275     * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE},
276     *         {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}.
277     */
278    public int getWindowAlignment() {
279        return mLayoutManager.getWindowAlignment();
280    }
281
282    /**
283     * Sets the offset in pixels for window alignment.
284     *
285     * @param offset The number of pixels to offset.  If the offset is positive,
286     *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
287     *        if the offset is negative, the absolute value is distance from high
288     *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
289     *        Default value is 0.
290     */
291    public void setWindowAlignmentOffset(int offset) {
292        mLayoutManager.setWindowAlignmentOffset(offset);
293        requestLayout();
294    }
295
296    /**
297     * Returns the offset in pixels for window alignment.
298     *
299     * @return The number of pixels to offset.  If the offset is positive,
300     *        it is distance from low edge (see {@link #WINDOW_ALIGN_LOW_EDGE});
301     *        if the offset is negative, the absolute value is distance from high
302     *        edge (see {@link #WINDOW_ALIGN_HIGH_EDGE}).
303     *        Default value is 0.
304     */
305    public int getWindowAlignmentOffset() {
306        return mLayoutManager.getWindowAlignmentOffset();
307    }
308
309    /**
310     * Sets the offset percent for window alignment in addition to {@link
311     * #getWindowAlignmentOffset()}.
312     *
313     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
314     *        width from low edge. Use
315     *        {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
316     *         Default value is 50.
317     */
318    public void setWindowAlignmentOffsetPercent(float offsetPercent) {
319        mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent);
320        requestLayout();
321    }
322
323    /**
324     * Returns the offset percent for window alignment in addition to
325     * {@link #getWindowAlignmentOffset()}.
326     *
327     * @return Percentage to offset. E.g., 40 means 40% of the width from the
328     *         low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if
329     *         disabled. Default value is 50.
330     */
331    public float getWindowAlignmentOffsetPercent() {
332        return mLayoutManager.getWindowAlignmentOffsetPercent();
333    }
334
335    /**
336     * Sets the absolute offset in pixels for item alignment.
337     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
338     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
339     *
340     * @param offset The number of pixels to offset. Can be negative for
341     *        alignment from the high edge, or positive for alignment from the
342     *        low edge.
343     */
344    public void setItemAlignmentOffset(int offset) {
345        mLayoutManager.setItemAlignmentOffset(offset);
346        requestLayout();
347    }
348
349    /**
350     * Returns the absolute offset in pixels for item alignment.
351     *
352     * @return The number of pixels to offset. Will be negative for alignment
353     *         from the high edge, or positive for alignment from the low edge.
354     *         Default value is 0.
355     */
356    public int getItemAlignmentOffset() {
357        return mLayoutManager.getItemAlignmentOffset();
358    }
359
360    /**
361     * Set to true if include padding in calculating item align offset.
362     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
363     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
364     *
365     * @param withPadding When it is true: we include left/top padding for positive
366     *          item offset, include right/bottom padding for negative item offset.
367     */
368    public void setItemAlignmentOffsetWithPadding(boolean withPadding) {
369        mLayoutManager.setItemAlignmentOffsetWithPadding(withPadding);
370        requestLayout();
371    }
372
373    /**
374     * Returns true if include padding in calculating item align offset.
375     */
376    public boolean isItemAlignmentOffsetWithPadding() {
377        return mLayoutManager.isItemAlignmentOffsetWithPadding();
378    }
379
380    /**
381     * Sets the offset percent for item alignment in addition to {@link
382     * #getItemAlignmentOffset()}.
383     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
384     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
385     *
386     * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the
387     *        width from the low edge. Use
388     *        {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable.
389     */
390    public void setItemAlignmentOffsetPercent(float offsetPercent) {
391        mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent);
392        requestLayout();
393    }
394
395    /**
396     * Returns the offset percent for item alignment in addition to {@link
397     * #getItemAlignmentOffset()}.
398     *
399     * @return Percentage to offset. E.g., 40 means 40% of the width from the
400     *         low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if
401     *         disabled. Default value is 50.
402     */
403    public float getItemAlignmentOffsetPercent() {
404        return mLayoutManager.getItemAlignmentOffsetPercent();
405    }
406
407    /**
408     * Sets the id of the view to align with. Use {@link android.view.View#NO_ID} (default)
409     * for the item view itself.
410     * Item alignment settings are ignored for the child if {@link ItemAlignmentFacet}
411     * is provided by {@link RecyclerView.ViewHolder} or {@link FacetProviderAdapter}.
412     */
413    public void setItemAlignmentViewId(int viewId) {
414        mLayoutManager.setItemAlignmentViewId(viewId);
415    }
416
417    /**
418     * Returns the id of the view to align with, or zero for the item view itself.
419     */
420    public int getItemAlignmentViewId() {
421        return mLayoutManager.getItemAlignmentViewId();
422    }
423
424    /**
425     * Sets the margin in pixels between two child items.
426     */
427    public void setItemMargin(int margin) {
428        mLayoutManager.setItemMargin(margin);
429        requestLayout();
430    }
431
432    /**
433     * Sets the margin in pixels between two child items vertically.
434     */
435    public void setVerticalMargin(int margin) {
436        mLayoutManager.setVerticalMargin(margin);
437        requestLayout();
438    }
439
440    /**
441     * Returns the margin in pixels between two child items vertically.
442     */
443    public int getVerticalMargin() {
444        return mLayoutManager.getVerticalMargin();
445    }
446
447    /**
448     * Sets the margin in pixels between two child items horizontally.
449     */
450    public void setHorizontalMargin(int margin) {
451        mLayoutManager.setHorizontalMargin(margin);
452        requestLayout();
453    }
454
455    /**
456     * Returns the margin in pixels between two child items horizontally.
457     */
458    public int getHorizontalMargin() {
459        return mLayoutManager.getHorizontalMargin();
460    }
461
462    /**
463     * Registers a callback to be invoked when an item in BaseGridView has
464     * been laid out.
465     *
466     * @param listener The listener to be invoked.
467     */
468    public void setOnChildLaidOutListener(OnChildLaidOutListener listener) {
469        mLayoutManager.setOnChildLaidOutListener(listener);
470    }
471
472    /**
473     * Registers a callback to be invoked when an item in BaseGridView has
474     * been selected.  Note that the listener may be invoked when there is a
475     * layout pending on the view, affording the listener an opportunity to
476     * adjust the upcoming layout based on the selection state.
477     *
478     * @param listener The listener to be invoked.
479     */
480    public void setOnChildSelectedListener(OnChildSelectedListener listener) {
481        mLayoutManager.setOnChildSelectedListener(listener);
482    }
483
484    /**
485     * Registers a callback to be invoked when an item in BaseGridView has
486     * been selected.  Note that the listener may be invoked when there is a
487     * layout pending on the view, affording the listener an opportunity to
488     * adjust the upcoming layout based on the selection state.
489     *
490     * @param listener The listener to be invoked.
491     */
492    public void setOnChildViewHolderSelectedListener(OnChildViewHolderSelectedListener listener) {
493        mLayoutManager.setOnChildViewHolderSelectedListener(listener);
494    }
495
496    /**
497     * Changes the selected item immediately without animation.
498     */
499    public void setSelectedPosition(int position) {
500        mLayoutManager.setSelection(this, position, 0);
501    }
502
503    /**
504     * Changes the selected item and/or subposition immediately without animation.
505     */
506    public void setSelectedPositionWithSub(int position, int subposition) {
507        mLayoutManager.setSelectionWithSub(this, position, subposition, 0);
508    }
509
510    /**
511     * Changes the selected item immediately without animation, scrollExtra is
512     * applied in primary scroll direction.  The scrollExtra will be kept until
513     * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
514     */
515    public void setSelectedPosition(int position, int scrollExtra) {
516        mLayoutManager.setSelection(this, position, scrollExtra);
517    }
518
519    /**
520     * Changes the selected item and/or subposition immediately without animation, scrollExtra is
521     * applied in primary scroll direction.  The scrollExtra will be kept until
522     * another {@link #setSelectedPosition} or {@link #setSelectedPositionSmooth} call.
523     */
524    public void setSelectedPositionWithSub(int position, int subposition, int scrollExtra) {
525        mLayoutManager.setSelectionWithSub(this, position, subposition, scrollExtra);
526    }
527
528    /**
529     * Changes the selected item and run an animation to scroll to the target
530     * position.
531     */
532    public void setSelectedPositionSmooth(int position) {
533        mLayoutManager.setSelectionSmooth(this, position);
534    }
535
536    /**
537     * Changes the selected item and/or subposition, runs an animation to scroll to the target
538     * position.
539     */
540    public void setSelectedPositionSmoothWithSub(int position, int subposition) {
541        mLayoutManager.setSelectionSmoothWithSub(this, position, subposition);
542    }
543
544    /**
545     * Returns the selected item position.
546     */
547    public int getSelectedPosition() {
548        return mLayoutManager.getSelection();
549    }
550
551    /**
552     * Returns the sub selected item position started from zero.  An item can have
553     * multiple {@link ItemAlignmentFacet}s provided by {@link RecyclerView.ViewHolder}
554     * or {@link FacetProviderAdapter}.  Zero is returned when no {@link ItemAlignmentFacet}
555     * is defined.
556     */
557    public int getSelectedSubPosition() {
558        return mLayoutManager.getSubSelection();
559    }
560
561    /**
562     * Sets whether an animation should run when a child changes size or when adding
563     * or removing a child.
564     * <p><i>Unstable API, might change later.</i>
565     */
566    public void setAnimateChildLayout(boolean animateChildLayout) {
567        if (mAnimateChildLayout != animateChildLayout) {
568            mAnimateChildLayout = animateChildLayout;
569            if (!mAnimateChildLayout) {
570                mSavedItemAnimator = getItemAnimator();
571                super.setItemAnimator(null);
572            } else {
573                super.setItemAnimator(mSavedItemAnimator);
574            }
575        }
576    }
577
578    /**
579     * Returns true if an animation will run when a child changes size or when
580     * adding or removing a child.
581     * <p><i>Unstable API, might change later.</i>
582     */
583    public boolean isChildLayoutAnimated() {
584        return mAnimateChildLayout;
585    }
586
587    /**
588     * Sets the gravity used for child view positioning. Defaults to
589     * GRAVITY_TOP|GRAVITY_START.
590     *
591     * @param gravity See {@link android.view.Gravity}
592     */
593    public void setGravity(int gravity) {
594        mLayoutManager.setGravity(gravity);
595        requestLayout();
596    }
597
598    @Override
599    public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
600        return mLayoutManager.gridOnRequestFocusInDescendants(this, direction,
601                previouslyFocusedRect);
602    }
603
604    /**
605     * Returns the x/y offsets to final position from current position if the view
606     * is selected.
607     *
608     * @param view The view to get offsets.
609     * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of Y.
610     */
611    public void getViewSelectedOffsets(View view, int[] offsets) {
612        mLayoutManager.getViewSelectedOffsets(view, offsets);
613    }
614
615    @Override
616    public int getChildDrawingOrder(int childCount, int i) {
617        return mLayoutManager.getChildDrawingOrder(this, childCount, i);
618    }
619
620    final boolean isChildrenDrawingOrderEnabledInternal() {
621        return isChildrenDrawingOrderEnabled();
622    }
623
624    @Override
625    public View focusSearch(int direction) {
626        if (isFocused()) {
627            // focusSearch(int) is called when GridView itself is focused.
628            // Calling focusSearch(view, int) to get next sibling of current selected child.
629            View view = mLayoutManager.findViewByPosition(mLayoutManager.getSelection());
630            if (view != null) {
631                return focusSearch(view, direction);
632            }
633        }
634        // otherwise, go to mParent to perform focusSearch
635        return super.focusSearch(direction);
636    }
637
638    @Override
639    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
640        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
641        mLayoutManager.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
642    }
643
644    /**
645     * Disables or enables focus search.
646     */
647    public final void setFocusSearchDisabled(boolean disabled) {
648        // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
649        // re-gain focus after a BACK key pressed, so block children focus during transition.
650        setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS: FOCUS_AFTER_DESCENDANTS);
651        mLayoutManager.setFocusSearchDisabled(disabled);
652    }
653
654    /**
655     * Returns true if focus search is disabled.
656     */
657    public final boolean isFocusSearchDisabled() {
658        return mLayoutManager.isFocusSearchDisabled();
659    }
660
661    /**
662     * Enables or disables layout.  All children will be removed when layout is
663     * disabled.
664     */
665    public void setLayoutEnabled(boolean layoutEnabled) {
666        mLayoutManager.setLayoutEnabled(layoutEnabled);
667    }
668
669    /**
670     * Changes and overrides children's visibility.
671     */
672    public void setChildrenVisibility(int visibility) {
673        mLayoutManager.setChildrenVisibility(visibility);
674    }
675
676    /**
677     * Enables or disables pruning of children.  Disable is useful during transition.
678     */
679    public void setPruneChild(boolean pruneChild) {
680        mLayoutManager.setPruneChild(pruneChild);
681    }
682
683    /**
684     * Enables or disables scrolling.  Disable is useful during transition.
685     */
686    public void setScrollEnabled(boolean scrollEnabled) {
687        mLayoutManager.setScrollEnabled(scrollEnabled);
688    }
689
690    /**
691     * Returns true if scrolling is enabled.
692     */
693    public boolean isScrollEnabled() {
694        return mLayoutManager.isScrollEnabled();
695    }
696
697    /**
698     * Returns true if the view at the given position has a same row sibling
699     * in front of it.  This will return true if first item view is not created.
700     * So application should check in both {@link OnChildSelectedListener} and {@link
701     * OnChildLaidOutListener}.
702     *
703     * @param position Position in adapter.
704     */
705    public boolean hasPreviousViewInSameRow(int position) {
706        return mLayoutManager.hasPreviousViewInSameRow(position);
707    }
708
709    /**
710     * Enables or disables the default "focus draw at last" order rule.
711     */
712    public void setFocusDrawingOrderEnabled(boolean enabled) {
713        super.setChildrenDrawingOrderEnabled(enabled);
714    }
715
716    /**
717     * Returns true if default "focus draw at last" order rule is enabled.
718     */
719    public boolean isFocusDrawingOrderEnabled() {
720        return super.isChildrenDrawingOrderEnabled();
721    }
722
723    /**
724     * Sets the touch intercept listener.
725     */
726    public void setOnTouchInterceptListener(OnTouchInterceptListener listener) {
727        mOnTouchInterceptListener = listener;
728    }
729
730    /**
731     * Sets the generic motion intercept listener.
732     */
733    public void setOnMotionInterceptListener(OnMotionInterceptListener listener) {
734        mOnMotionInterceptListener = listener;
735    }
736
737    /**
738     * Sets the key intercept listener.
739     */
740    public void setOnKeyInterceptListener(OnKeyInterceptListener listener) {
741        mOnKeyInterceptListener = listener;
742    }
743
744    /**
745     * Sets the unhandled key listener.
746     */
747    public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
748        mOnUnhandledKeyListener = listener;
749    }
750
751    /**
752     * Returns the unhandled key listener.
753     */
754    public OnUnhandledKeyListener getOnUnhandledKeyListener() {
755        return mOnUnhandledKeyListener;
756    }
757
758    @Override
759    public boolean dispatchKeyEvent(KeyEvent event) {
760        if (mOnKeyInterceptListener != null && mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
761            return true;
762        }
763        if (super.dispatchKeyEvent(event)) {
764            return true;
765        }
766        if (mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event)) {
767            return true;
768        }
769        return false;
770    }
771
772    @Override
773    public boolean dispatchTouchEvent(MotionEvent event) {
774        if (mOnTouchInterceptListener != null) {
775            if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) {
776                return true;
777            }
778        }
779        return super.dispatchTouchEvent(event);
780    }
781
782    @Override
783    public boolean dispatchGenericFocusedEvent(MotionEvent event) {
784        if (mOnMotionInterceptListener != null) {
785            if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
786                return true;
787            }
788        }
789        return super.dispatchGenericFocusedEvent(event);
790    }
791
792    /**
793     * Returns the policy for saving children.
794     *
795     * @return policy, one of {@link #SAVE_NO_CHILD}
796     * {@link #SAVE_ON_SCREEN_CHILD} {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
797     */
798    public final int getSaveChildrenPolicy() {
799        return mLayoutManager.mChildrenStates.getSavePolicy();
800    }
801
802    /**
803     * Returns the limit used when when {@link #getSaveChildrenPolicy()} is
804     *         {@link #SAVE_LIMITED_CHILD}
805     */
806    public final int getSaveChildrenLimitNumber() {
807        return mLayoutManager.mChildrenStates.getLimitNumber();
808    }
809
810    /**
811     * Sets the policy for saving children.
812     * @param savePolicy One of {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
813     * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
814     */
815    public final void setSaveChildrenPolicy(int savePolicy) {
816        mLayoutManager.mChildrenStates.setSavePolicy(savePolicy);
817    }
818
819    /**
820     * Sets the limit number when {@link #getSaveChildrenPolicy()} is {@link #SAVE_LIMITED_CHILD}.
821     */
822    public final void setSaveChildrenLimitNumber(int limitNumber) {
823        mLayoutManager.mChildrenStates.setLimitNumber(limitNumber);
824    }
825
826    @Override
827    public boolean hasOverlappingRendering() {
828        return mHasOverlappingRendering;
829    }
830
831    public void setHasOverlappingRendering(boolean hasOverlapping) {
832        mHasOverlappingRendering = hasOverlapping;
833    }
834
835    /**
836     * Notify layout manager that layout directionality has been updated
837     */
838    @Override
839    public void onRtlPropertiesChanged(int layoutDirection) {
840        mLayoutManager.onRtlPropertiesChanged(layoutDirection);
841    }
842
843    @Override
844    public void setRecyclerListener(RecyclerView.RecyclerListener listener) {
845        mChainedRecyclerListener = listener;
846    }
847
848    /**
849     * Sets pixels of extra space for layout child in invisible area.
850     *
851     * @param extraLayoutSpace  Pixels of extra space for layout invisible child.
852     *                          Must be bigger or equals to 0.
853     * @hide
854     */
855    public void setExtraLayoutSpace(int extraLayoutSpace) {
856        mLayoutManager.setExtraLayoutSpace(extraLayoutSpace);
857    }
858
859    /**
860     * Returns pixels of extra space for layout child in invisible area.
861     *
862     * @hide
863     */
864    public int getExtraLayoutSpace() {
865        return mLayoutManager.getExtraLayoutSpace();
866    }
867}
868