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