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