BaseGridView.java revision 89b4aed6fcd80f940531e265936ab908a8037f87
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        // focusSearch(int) is called when GridView itself is focused.
627        // Calling focusSearch(view, int) to get next sibling of current selected child.
628        View view = mLayoutManager.findViewByPosition(mLayoutManager.getSelection());
629        return focusSearch(view, direction);
630    }
631
632    @Override
633    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
634        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
635        mLayoutManager.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
636    }
637
638    /**
639     * Disables or enables focus search.
640     */
641    public final void setFocusSearchDisabled(boolean disabled) {
642        // LayoutManager may detachView and attachView in fastRelayout, it causes RowsFragment
643        // re-gain focus after a BACK key pressed, so block children focus during transition.
644        setDescendantFocusability(disabled ? FOCUS_BLOCK_DESCENDANTS: FOCUS_AFTER_DESCENDANTS);
645        mLayoutManager.setFocusSearchDisabled(disabled);
646    }
647
648    /**
649     * Returns true if focus search is disabled.
650     */
651    public final boolean isFocusSearchDisabled() {
652        return mLayoutManager.isFocusSearchDisabled();
653    }
654
655    /**
656     * Enables or disables layout.  All children will be removed when layout is
657     * disabled.
658     */
659    public void setLayoutEnabled(boolean layoutEnabled) {
660        mLayoutManager.setLayoutEnabled(layoutEnabled);
661    }
662
663    /**
664     * Changes and overrides children's visibility.
665     */
666    public void setChildrenVisibility(int visibility) {
667        mLayoutManager.setChildrenVisibility(visibility);
668    }
669
670    /**
671     * Enables or disables pruning of children.  Disable is useful during transition.
672     */
673    public void setPruneChild(boolean pruneChild) {
674        mLayoutManager.setPruneChild(pruneChild);
675    }
676
677    /**
678     * Enables or disables scrolling.  Disable is useful during transition.
679     */
680    public void setScrollEnabled(boolean scrollEnabled) {
681        mLayoutManager.setScrollEnabled(scrollEnabled);
682    }
683
684    /**
685     * Returns true if scrolling is enabled.
686     */
687    public boolean isScrollEnabled() {
688        return mLayoutManager.isScrollEnabled();
689    }
690
691    /**
692     * Returns true if the view at the given position has a same row sibling
693     * in front of it.  This will return true if first item view is not created.
694     * So application should check in both {@link OnChildSelectedListener} and {@link
695     * OnChildLaidOutListener}.
696     *
697     * @param position Position in adapter.
698     */
699    public boolean hasPreviousViewInSameRow(int position) {
700        return mLayoutManager.hasPreviousViewInSameRow(position);
701    }
702
703    /**
704     * Enables or disables the default "focus draw at last" order rule.
705     */
706    public void setFocusDrawingOrderEnabled(boolean enabled) {
707        super.setChildrenDrawingOrderEnabled(enabled);
708    }
709
710    /**
711     * Returns true if default "focus draw at last" order rule is enabled.
712     */
713    public boolean isFocusDrawingOrderEnabled() {
714        return super.isChildrenDrawingOrderEnabled();
715    }
716
717    /**
718     * Sets the touch intercept listener.
719     */
720    public void setOnTouchInterceptListener(OnTouchInterceptListener listener) {
721        mOnTouchInterceptListener = listener;
722    }
723
724    /**
725     * Sets the generic motion intercept listener.
726     */
727    public void setOnMotionInterceptListener(OnMotionInterceptListener listener) {
728        mOnMotionInterceptListener = listener;
729    }
730
731    /**
732     * Sets the key intercept listener.
733     */
734    public void setOnKeyInterceptListener(OnKeyInterceptListener listener) {
735        mOnKeyInterceptListener = listener;
736    }
737
738    /**
739     * Sets the unhandled key listener.
740     */
741    public void setOnUnhandledKeyListener(OnUnhandledKeyListener listener) {
742        mOnUnhandledKeyListener = listener;
743    }
744
745    /**
746     * Returns the unhandled key listener.
747     */
748    public OnUnhandledKeyListener getOnUnhandledKeyListener() {
749        return mOnUnhandledKeyListener;
750    }
751
752    @Override
753    public boolean dispatchKeyEvent(KeyEvent event) {
754        if (mOnKeyInterceptListener != null && mOnKeyInterceptListener.onInterceptKeyEvent(event)) {
755            return true;
756        }
757        if (super.dispatchKeyEvent(event)) {
758            return true;
759        }
760        if (mOnUnhandledKeyListener != null && mOnUnhandledKeyListener.onUnhandledKey(event)) {
761            return true;
762        }
763        return false;
764    }
765
766    @Override
767    public boolean dispatchTouchEvent(MotionEvent event) {
768        if (mOnTouchInterceptListener != null) {
769            if (mOnTouchInterceptListener.onInterceptTouchEvent(event)) {
770                return true;
771            }
772        }
773        return super.dispatchTouchEvent(event);
774    }
775
776    @Override
777    public boolean dispatchGenericFocusedEvent(MotionEvent event) {
778        if (mOnMotionInterceptListener != null) {
779            if (mOnMotionInterceptListener.onInterceptMotionEvent(event)) {
780                return true;
781            }
782        }
783        return super.dispatchGenericFocusedEvent(event);
784    }
785
786    /**
787     * Returns the policy for saving children.
788     *
789     * @return policy, one of {@link #SAVE_NO_CHILD}
790     * {@link #SAVE_ON_SCREEN_CHILD} {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
791     */
792    public final int getSaveChildrenPolicy() {
793        return mLayoutManager.mChildrenStates.getSavePolicy();
794    }
795
796    /**
797     * Returns the limit used when when {@link #getSaveChildrenPolicy()} is
798     *         {@link #SAVE_LIMITED_CHILD}
799     */
800    public final int getSaveChildrenLimitNumber() {
801        return mLayoutManager.mChildrenStates.getLimitNumber();
802    }
803
804    /**
805     * Sets the policy for saving children.
806     * @param savePolicy One of {@link #SAVE_NO_CHILD} {@link #SAVE_ON_SCREEN_CHILD}
807     * {@link #SAVE_LIMITED_CHILD} {@link #SAVE_ALL_CHILD}.
808     */
809    public final void setSaveChildrenPolicy(int savePolicy) {
810        mLayoutManager.mChildrenStates.setSavePolicy(savePolicy);
811    }
812
813    /**
814     * Sets the limit number when {@link #getSaveChildrenPolicy()} is {@link #SAVE_LIMITED_CHILD}.
815     */
816    public final void setSaveChildrenLimitNumber(int limitNumber) {
817        mLayoutManager.mChildrenStates.setLimitNumber(limitNumber);
818    }
819
820    @Override
821    public boolean hasOverlappingRendering() {
822        return mHasOverlappingRendering;
823    }
824
825    public void setHasOverlappingRendering(boolean hasOverlapping) {
826        mHasOverlappingRendering = hasOverlapping;
827    }
828
829    /**
830     * Notify layout manager that layout directionality has been updated
831     */
832    @Override
833    public void onRtlPropertiesChanged(int layoutDirection) {
834        mLayoutManager.onRtlPropertiesChanged(layoutDirection);
835    }
836
837    @Override
838    public void setRecyclerListener(RecyclerView.RecyclerListener listener) {
839        mChainedRecyclerListener = listener;
840    }
841
842    /**
843     * Sets pixels of extra space for layout child in invisible area.
844     *
845     * @param extraLayoutSpace  Pixels of extra space for layout invisible child.
846     *                          Must be bigger or equals to 0.
847     * @hide
848     */
849    public void setExtraLayoutSpace(int extraLayoutSpace) {
850        mLayoutManager.setExtraLayoutSpace(extraLayoutSpace);
851    }
852
853    /**
854     * Returns pixels of extra space for layout child in invisible area.
855     *
856     * @hide
857     */
858    public int getExtraLayoutSpace() {
859        return mLayoutManager.getExtraLayoutSpace();
860    }
861}
862