BaseGridView.java revision 5b1f117209e8a38d6d6b004c3c0d0285acc9b27f
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.View; 23import android.view.ViewGroup; 24import android.view.animation.Interpolator; 25 26/** 27 * Base class for vertically and horizontally scrolling lists. The items come 28 * from the {@link RecyclerView.Adapter} associated with this view. 29 * @hide 30 */ 31abstract class BaseGridView extends RecyclerView { 32 33 /** 34 * Always keep focused item at a aligned position. Developer can use 35 * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned. 36 * In this mode, the last focused position will be remembered and restored when focus 37 * is back to the view. 38 */ 39 public final static int FOCUS_SCROLL_ALIGNED = 0; 40 41 /** 42 * Scroll to make the focused item inside client area. 43 */ 44 public final static int FOCUS_SCROLL_ITEM = 1; 45 46 /** 47 * Scroll a page of items when focusing to item outside the client area. 48 * The page size matches the client area size of RecyclerView. 49 */ 50 public final static int FOCUS_SCROLL_PAGE = 2; 51 52 /** 53 * The first item is aligned with the low edge of the viewport. When 54 * navigating away from the first item, the focus maintains a middle 55 * location. 56 * <p> 57 * The middle location is calculated by "windowAlignOffset" and 58 * "windowAlignOffsetPercent"; if neither of these two is defined, the 59 * default value is 1/2 of the size. 60 */ 61 public final static int WINDOW_ALIGN_LOW_EDGE = 1; 62 63 /** 64 * The last item is aligned with the high edge of the viewport when 65 * navigating to the end of list. When navigating away from the end, the 66 * focus maintains a middle location. 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_HIGH_EDGE = 1 << 1; 73 74 /** 75 * The first item and last item are aligned with the two edges of the 76 * viewport. When navigating in the middle of list, the focus maintains a 77 * middle location. 78 * <p> 79 * The middle location is calculated by "windowAlignOffset" and 80 * "windowAlignOffsetPercent"; if neither of these two is defined, the 81 * default value is 1/2 of the size. 82 */ 83 public final static int WINDOW_ALIGN_BOTH_EDGE = 84 WINDOW_ALIGN_LOW_EDGE | WINDOW_ALIGN_HIGH_EDGE; 85 86 /** 87 * The focused item always stays in a 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_NO_EDGE = 0; 94 95 /** 96 * Value indicates that percent is not used. 97 */ 98 public final static float WINDOW_ALIGN_OFFSET_PERCENT_DISABLED = -1; 99 100 /** 101 * Value indicates that percent is not used. 102 */ 103 public final static float ITEM_ALIGN_OFFSET_PERCENT_DISABLED = -1; 104 105 protected final GridLayoutManager mLayoutManager; 106 107 private int[] mMeasuredSize = new int[2]; 108 109 public BaseGridView(Context context, AttributeSet attrs, int defStyle) { 110 super(context, attrs, defStyle); 111 mLayoutManager = new GridLayoutManager(this); 112 setLayoutManager(mLayoutManager); 113 setHasFixedSize(true); 114 setChildrenDrawingOrderEnabled(true); 115 } 116 117 protected void initBaseGridViewAttributes(Context context, AttributeSet attrs) { 118 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView); 119 boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false); 120 boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false); 121 mLayoutManager.setFocusOutAllowed(throughFront, throughEnd); 122 mLayoutManager.setVerticalMargin( 123 a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0)); 124 mLayoutManager.setHorizontalMargin( 125 a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0)); 126 a.recycle(); 127 } 128 129 /** 130 * Set the strategy used to scroll in response to item focus changing: 131 * <ul> 132 * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li> 133 * <li>{@link #FOCUS_SCROLL_ITEM}</li> 134 * <li>{@link #FOCUS_SCROLL_PAGE}</li> 135 * </ul> 136 */ 137 public void setFocusScrollStrategy(int scrollStrategy) { 138 if (scrollStrategy != FOCUS_SCROLL_ALIGNED && scrollStrategy != FOCUS_SCROLL_ITEM 139 && scrollStrategy != FOCUS_SCROLL_PAGE) { 140 throw new IllegalArgumentException("Invalid scrollStrategy"); 141 } 142 mLayoutManager.setFocusScrollStrategy(scrollStrategy); 143 requestLayout(); 144 } 145 146 /** 147 * Returns the strategy used to scroll in response to item focus changing. 148 * <ul> 149 * <li>{@link #FOCUS_SCROLL_ALIGNED} (default) </li> 150 * <li>{@link #FOCUS_SCROLL_ITEM}</li> 151 * <li>{@link #FOCUS_SCROLL_PAGE}</li> 152 * </ul> 153 */ 154 public int getFocusScrollStrategy() { 155 return mLayoutManager.getFocusScrollStrategy(); 156 } 157 158 /** 159 * Set how the focused item gets aligned in the view. 160 * 161 * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE}, 162 * {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or 163 * {@link #WINDOW_ALIGN_NO_EDGE}. 164 */ 165 public void setWindowAlignment(int windowAlignment) { 166 mLayoutManager.setWindowAlignment(windowAlignment); 167 requestLayout(); 168 } 169 170 /** 171 * Get how the focused item gets aligned in the view. 172 * 173 * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE}, 174 * {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}. 175 */ 176 public int getWindowAlignment() { 177 return mLayoutManager.getWindowAlignment(); 178 } 179 180 /** 181 * Set the absolute offset in pixels for window alignment. 182 * 183 * @param offset The number of pixels to offset. Can be negative for 184 * alignment from the high edge, or positive for alignment from the 185 * low edge. 186 */ 187 public void setWindowAlignmentOffset(int offset) { 188 mLayoutManager.setWindowAlignmentOffset(offset); 189 requestLayout(); 190 } 191 192 /** 193 * Get the absolute offset in pixels for window alignment. 194 * 195 * @return The number of pixels to offset. Will be negative for alignment 196 * from the high edge, or positive for alignment from the low edge. 197 * Default value is 0. 198 */ 199 public int getWindowAlignmentOffset() { 200 return mLayoutManager.getWindowAlignmentOffset(); 201 } 202 203 /** 204 * Set offset percent for window alignment in addition to {@link 205 * #getWindowAlignmentOffset()}. 206 * 207 * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the 208 * width from low edge. Use 209 * {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable. 210 */ 211 public void setWindowAlignmentOffsetPercent(float offsetPercent) { 212 mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent); 213 requestLayout(); 214 } 215 216 /** 217 * Get offset percent for window alignment in addition to 218 * {@link #getWindowAlignmentOffset()}. 219 * 220 * @return Percentage to offset. E.g., 40 means 40% of the width from the 221 * low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if 222 * disabled. Default value is 50. 223 */ 224 public float getWindowAlignmentOffsetPercent() { 225 return mLayoutManager.getWindowAlignmentOffsetPercent(); 226 } 227 228 /** 229 * Set the absolute offset in pixels for item alignment. 230 * 231 * @param offset The number of pixels to offset. Can be negative for 232 * alignment from the high edge, or positive for alignment from the 233 * low edge. 234 */ 235 public void setItemAlignmentOffset(int offset) { 236 mLayoutManager.setItemAlignmentOffset(offset); 237 requestLayout(); 238 } 239 240 /** 241 * Get the absolute offset in pixels for item alignment. 242 * 243 * @return The number of pixels to offset. Will be negative for alignment 244 * from the high edge, or positive for alignment from the low edge. 245 * Default value is 0. 246 */ 247 public int getItemAlignmentOffset() { 248 return mLayoutManager.getItemAlignmentOffset(); 249 } 250 251 /** 252 * Set offset percent for item alignment in addition to {@link 253 * #getItemAlignmentOffset()}. 254 * 255 * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the 256 * width from the low edge. Use 257 * {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable. 258 */ 259 public void setItemAlignmentOffsetPercent(float offsetPercent) { 260 mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent); 261 requestLayout(); 262 } 263 264 /** 265 * Get offset percent for item alignment in addition to {@link 266 * #getItemAlignmentOffset()}. 267 * 268 * @return Percentage to offset. E.g., 40 means 40% of the width from the 269 * low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if 270 * disabled. Default value is 50. 271 */ 272 public float getItemAlignmentOffsetPercent() { 273 return mLayoutManager.getItemAlignmentOffsetPercent(); 274 } 275 276 /** 277 * Set the id of the view to align with. Use zero (default) for the item 278 * view itself. 279 */ 280 public void setItemAlignmentViewId(int viewId) { 281 mLayoutManager.setItemAlignmentViewId(viewId); 282 } 283 284 /** 285 * Get the id of the view to align with, or zero for the item view itself. 286 */ 287 public int getItemAlignmentViewId() { 288 return mLayoutManager.getItemAlignmentViewId(); 289 } 290 291 /** 292 * Set the margin in pixels between two child items. 293 */ 294 public void setItemMargin(int margin) { 295 mLayoutManager.setItemMargin(margin); 296 requestLayout(); 297 } 298 299 /** 300 * Set the margin in pixels between two child items vertically. 301 */ 302 public void setVerticalMargin(int margin) { 303 mLayoutManager.setVerticalMargin(margin); 304 requestLayout(); 305 } 306 307 /** 308 * Get the margin in pixels between two child items vertically. 309 */ 310 public int getVerticalMargin() { 311 return mLayoutManager.getVerticalMargin(); 312 } 313 314 /** 315 * Set the margin in pixels between two child items horizontally. 316 */ 317 public void setHorizontalMargin(int margin) { 318 mLayoutManager.setHorizontalMargin(margin); 319 requestLayout(); 320 } 321 322 /** 323 * Get the margin in pixels between two child items horizontally. 324 */ 325 public int getHorizontalMargin() { 326 return mLayoutManager.getHorizontalMargin(); 327 } 328 329 /** 330 * Register a callback to be invoked when an item in BaseGridView has 331 * been selected. 332 */ 333 public void setOnChildSelectedListener(OnChildSelectedListener listener) { 334 mLayoutManager.setOnChildSelectedListener(listener); 335 } 336 337 /** 338 * Change the selected item immediately without animation. 339 */ 340 public void setSelectedPosition(int position) { 341 mLayoutManager.setSelection(this, position); 342 } 343 344 /** 345 * Change the selected item and run an animation to scroll to the target 346 * position. 347 */ 348 public void setSelectedPositionSmooth(int position) { 349 mLayoutManager.setSelectionSmooth(this, position); 350 } 351 352 /** 353 * Get the selected item position. 354 */ 355 public int getSelectedPosition() { 356 return mLayoutManager.getSelection(); 357 } 358 359 /** 360 * Set if an animation should run when a child changes size or when adding 361 * or removing a child. 362 * <p><i>Unstable API, might change later.</i> 363 */ 364 public void setAnimateChildLayout(boolean animateChildLayout) { 365 mLayoutManager.setAnimateChildLayout(animateChildLayout); 366 } 367 368 /** 369 * Return true if an animation will run when a child changes size or when 370 * adding or removing a child. 371 * <p><i>Unstable API, might change later.</i> 372 */ 373 public boolean isChildLayoutAnimated() { 374 return mLayoutManager.isChildLayoutAnimated(); 375 } 376 377 /** 378 * Set an interpolator for the animation when a child changes size or when 379 * adding or removing a child. 380 * <p><i>Unstable API, might change later.</i> 381 */ 382 public void setChildLayoutAnimationInterpolator(Interpolator interpolator) { 383 mLayoutManager.setChildLayoutAnimationInterpolator(interpolator); 384 } 385 386 /** 387 * Get the interpolator for the animation when a child changes size or when 388 * adding or removing a child. 389 * <p><i>Unstable API, might change later.</i> 390 */ 391 public Interpolator getChildLayoutAnimationInterpolator() { 392 return mLayoutManager.getChildLayoutAnimationInterpolator(); 393 } 394 395 /** 396 * Set the duration of the animation when a child changes size or when 397 * adding or removing a child. 398 * <p><i>Unstable API, might change later.</i> 399 */ 400 public void setChildLayoutAnimationDuration(long duration) { 401 mLayoutManager.setChildLayoutAnimationDuration(duration); 402 } 403 404 /** 405 * Get the duration of the animation when a child changes size or when 406 * adding or removing a child. 407 * <p><i>Unstable API, might change later.</i> 408 */ 409 public long getChildLayoutAnimationDuration() { 410 return mLayoutManager.getChildLayoutAnimationDuration(); 411 } 412 413 /** 414 * Describes how the child views are positioned. Defaults to 415 * GRAVITY_TOP|GRAVITY_LEFT. 416 * 417 * @param gravity See {@link android.view.Gravity} 418 */ 419 public void setGravity(int gravity) { 420 mLayoutManager.setGravity(gravity); 421 requestLayout(); 422 } 423 424 @Override 425 protected final void onMeasure(int widthSpec, int heightSpec) { 426 mLayoutManager.gridOnMeasure(widthSpec, heightSpec, mMeasuredSize); 427 setMeasuredDimension(mMeasuredSize[0], mMeasuredSize[1]); 428 } 429 430 @Override 431 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 432 return mLayoutManager.gridOnRequestFocus(this, direction, previouslyFocusedRect); 433 } 434 435 /** 436 * Get the x/y offsets to final position from current position if the view 437 * is selected. 438 * 439 * @param view The view to get offsets. 440 * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of 441 * Y. 442 */ 443 public void getViewSelectedOffsets(View view, int[] offsets) { 444 mLayoutManager.getViewSelectedOffsets(view, offsets); 445 } 446 447 @Override 448 public int getChildDrawingOrder(int childCount, int i) { 449 return mLayoutManager.getChildDrawingOrder(this, childCount, i); 450 } 451 452 final boolean isChildrenDrawingOrderEnabledInternal() { 453 return isChildrenDrawingOrderEnabled(); 454 } 455} 456