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