BaseGridView.java revision a8a3b898da49324e83ea32c3f08776a481312166
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 } 97 98 protected void initBaseGridViewAttributes(Context context, AttributeSet attrs) { 99 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbBaseGridView); 100 boolean throughFront = a.getBoolean(R.styleable.lbBaseGridView_focusOutFront, false); 101 boolean throughEnd = a.getBoolean(R.styleable.lbBaseGridView_focusOutEnd, false); 102 mLayoutManager.setFocusOutAllowed(throughFront, throughEnd); 103 mLayoutManager.setVerticalMargin( 104 a.getDimensionPixelSize(R.styleable.lbBaseGridView_verticalMargin, 0)); 105 mLayoutManager.setHorizontalMargin( 106 a.getDimensionPixelSize(R.styleable.lbBaseGridView_horizontalMargin, 0)); 107 a.recycle(); 108 } 109 110 /** 111 * Set how the focused item gets aligned in the view. 112 * 113 * @param windowAlignment {@link #WINDOW_ALIGN_BOTH_EDGE}, 114 * {@link #WINDOW_ALIGN_LOW_EDGE}, {@link #WINDOW_ALIGN_HIGH_EDGE} or 115 * {@link #WINDOW_ALIGN_NO_EDGE}. 116 */ 117 public void setWindowAlignment(int windowAlignment) { 118 mLayoutManager.setWindowAlignment(windowAlignment); 119 requestLayout(); 120 } 121 122 /** 123 * Get how the focused item gets aligned in the view. 124 * 125 * @return {@link #WINDOW_ALIGN_BOTH_EDGE}, {@link #WINDOW_ALIGN_LOW_EDGE}, 126 * {@link #WINDOW_ALIGN_HIGH_EDGE} or {@link #WINDOW_ALIGN_NO_EDGE}. 127 */ 128 public int getWindowAlignment() { 129 return mLayoutManager.getWindowAlignment(); 130 } 131 132 /** 133 * Set the absolute offset in pixels for window alignment. 134 * 135 * @param offset The number of pixels to offset. Can be negative for 136 * alignment from the high edge, or positive for alignment from the 137 * low edge. 138 */ 139 public void setWindowAlignmentOffset(int offset) { 140 mLayoutManager.setWindowAlignmentOffset(offset); 141 requestLayout(); 142 } 143 144 /** 145 * Get the absolute offset in pixels for window alignment. 146 * 147 * @return The number of pixels to offset. Will be negative for alignment 148 * from the high edge, or positive for alignment from the low edge. 149 * Default value is 0. 150 */ 151 public int getWindowAlignmentOffset() { 152 return mLayoutManager.getWindowAlignmentOffset(); 153 } 154 155 /** 156 * Set offset percent for window alignment in addition to {@link 157 * #getWindowAlignmentOffset()}. 158 * 159 * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the 160 * width from low edge. Use 161 * {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} to disable. 162 */ 163 public void setWindowAlignmentOffsetPercent(float offsetPercent) { 164 mLayoutManager.setWindowAlignmentOffsetPercent(offsetPercent); 165 requestLayout(); 166 } 167 168 /** 169 * Get offset percent for window alignment in addition to 170 * {@link #getWindowAlignmentOffset()}. 171 * 172 * @return Percentage to offset. E.g., 40 means 40% of the width from the 173 * low edge, or {@link #WINDOW_ALIGN_OFFSET_PERCENT_DISABLED} if 174 * disabled. Default value is 50. 175 */ 176 public float getWindowAlignmentOffsetPercent() { 177 return mLayoutManager.getWindowAlignmentOffsetPercent(); 178 } 179 180 /** 181 * Set the absolute offset in pixels for item 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 setItemAlignmentOffset(int offset) { 188 mLayoutManager.setItemAlignmentOffset(offset); 189 requestLayout(); 190 } 191 192 /** 193 * Get the absolute offset in pixels for item 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 getItemAlignmentOffset() { 200 return mLayoutManager.getItemAlignmentOffset(); 201 } 202 203 /** 204 * Set offset percent for item alignment in addition to {@link 205 * #getItemAlignmentOffset()}. 206 * 207 * @param offsetPercent Percentage to offset. E.g., 40 means 40% of the 208 * width from the low edge. Use 209 * {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} to disable. 210 */ 211 public void setItemAlignmentOffsetPercent(float offsetPercent) { 212 mLayoutManager.setItemAlignmentOffsetPercent(offsetPercent); 213 requestLayout(); 214 } 215 216 /** 217 * Get offset percent for item alignment in addition to {@link 218 * #getItemAlignmentOffset()}. 219 * 220 * @return Percentage to offset. E.g., 40 means 40% of the width from the 221 * low edge, or {@link #ITEM_ALIGN_OFFSET_PERCENT_DISABLED} if 222 * disabled. Default value is 50. 223 */ 224 public float getItemAlignmentOffsetPercent() { 225 return mLayoutManager.getItemAlignmentOffsetPercent(); 226 } 227 228 /** 229 * Set the id of the view to align with. Use zero (default) for the item 230 * view itself. 231 */ 232 public void setItemAlignmentViewId(int viewId) { 233 mLayoutManager.setItemAlignmentViewId(viewId); 234 } 235 236 /** 237 * Get the id of the view to align with, or zero for the item view itself. 238 */ 239 public int getItemAlignmentViewId() { 240 return mLayoutManager.getItemAlignmentViewId(); 241 } 242 243 /** 244 * Set the margin in pixels between two child items. 245 */ 246 public void setItemMargin(int margin) { 247 mLayoutManager.setItemMargin(margin); 248 requestLayout(); 249 } 250 251 @Deprecated 252 public void setMargin(int margin) { 253 setItemMargin(margin); 254 } 255 256 /** 257 * Set the margin in pixels between two child items vertically. 258 */ 259 public void setVerticalMargin(int margin) { 260 mLayoutManager.setVerticalMargin(margin); 261 requestLayout(); 262 } 263 264 /** 265 * Get the margin in pixels between two child items vertically. 266 */ 267 public int getVerticalMargin() { 268 return mLayoutManager.getVerticalMargin(); 269 } 270 271 /** 272 * Register a callback to be invoked when an item in BaseGridView has 273 * been selected. 274 */ 275 public void setOnChildSelectedListener(OnChildSelectedListener listener) { 276 mLayoutManager.setOnChildSelectedListener(listener); 277 } 278 279 /** 280 * Change the selected item immediately without animation. 281 */ 282 public void setSelectedPosition(int position) { 283 mLayoutManager.setSelection(this, position); 284 } 285 286 /** 287 * Change the selected item and run an animation to scroll to the target 288 * position. 289 */ 290 public void setSelectedPositionSmooth(int position) { 291 mLayoutManager.setSelectionSmooth(this, position); 292 } 293 294 /** 295 * Get the selected item position. 296 */ 297 public int getSelectedPosition() { 298 return mLayoutManager.getSelection(); 299 } 300 301 /** 302 * Set if an animation should run when a child changes size or when adding 303 * or removing a child. 304 * <p><i>Unstable API, might change later.</i> 305 */ 306 public void setAnimateChildLayout(boolean animateChildLayout) { 307 mLayoutManager.setAnimateChildLayout(animateChildLayout); 308 } 309 310 /** 311 * Return true if an animation will run when a child changes size or when 312 * adding or removing a child. 313 * <p><i>Unstable API, might change later.</i> 314 */ 315 public boolean isChildLayoutAnimated() { 316 return mLayoutManager.isChildLayoutAnimated(); 317 } 318 319 /** 320 * Set an interpolator for the animation when a child changes size or when 321 * adding or removing a child. 322 * <p><i>Unstable API, might change later.</i> 323 */ 324 public void setChildLayoutAnimationInterpolator(Interpolator interpolator) { 325 mLayoutManager.setChildLayoutAnimationInterpolator(interpolator); 326 } 327 328 /** 329 * Get the interpolator for the animation when a child changes size or when 330 * adding or removing a child. 331 * <p><i>Unstable API, might change later.</i> 332 */ 333 public Interpolator getChildLayoutAnimationInterpolator() { 334 return mLayoutManager.getChildLayoutAnimationInterpolator(); 335 } 336 337 /** 338 * Set the duration of the animation when a child changes size or when 339 * adding or removing a child. 340 * <p><i>Unstable API, might change later.</i> 341 */ 342 public void setChildLayoutAnimationDuration(long duration) { 343 mLayoutManager.setChildLayoutAnimationDuration(duration); 344 } 345 346 /** 347 * Get the duration of the animation when a child changes size or when 348 * adding or removing a child. 349 * <p><i>Unstable API, might change later.</i> 350 */ 351 public long getChildLayoutAnimationDuration() { 352 return mLayoutManager.getChildLayoutAnimationDuration(); 353 } 354 355 @Override 356 protected final void onMeasure(int widthSpec, int heightSpec) { 357 mLayoutManager.onMeasure(widthSpec, heightSpec, mMeasuredSize); 358 setMeasuredDimension(mMeasuredSize[0], mMeasuredSize[1]); 359 } 360 361 @Override 362 public boolean requestFocus(int direction, Rect previouslyFocusedRect) { 363 if (mLayoutManager.focusSelectedChild(direction, previouslyFocusedRect)) { 364 return true; 365 } 366 return super.requestFocus(direction, previouslyFocusedRect); 367 } 368 369 /** 370 * Get the x/y offsets to final position from current position if the view 371 * is selected. 372 * 373 * @param view The view to get offsets. 374 * @param offsets offsets[0] holds offset of X, offsets[1] holds offset of 375 * Y. 376 */ 377 public void getViewSelectedOffsets(View view, int[] offsets) { 378 mLayoutManager.getViewSelectedOffsets(view, offsets); 379 } 380} 381