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