1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.support.v7.widget; 18 19 import android.view.View; 20 import android.widget.LinearLayout; 21 22 /** 23 * Helper class for LayoutManagers to abstract measurements depending on the View's orientation. 24 * <p> 25 * It is developed to easily support vertical and horizontal orientations in a LayoutManager but 26 * can also be used to abstract calls around view bounds and child measurements with margins and 27 * decorations. 28 * 29 * @see #createHorizontalHelper(RecyclerView.LayoutManager) 30 * @see #createVerticalHelper(RecyclerView.LayoutManager) 31 */ 32 public abstract class OrientationHelper { 33 34 private static final int INVALID_SIZE = Integer.MIN_VALUE; 35 36 protected final RecyclerView.LayoutManager mLayoutManager; 37 38 public static final int HORIZONTAL = LinearLayout.HORIZONTAL; 39 40 public static final int VERTICAL = LinearLayout.VERTICAL; 41 42 private int mLastTotalSpace = INVALID_SIZE; 43 OrientationHelper(RecyclerView.LayoutManager layoutManager)44 private OrientationHelper(RecyclerView.LayoutManager layoutManager) { 45 mLayoutManager = layoutManager; 46 } 47 48 /** 49 * Call this method after onLayout method is complete if state is NOT pre-layout. 50 * This method records information like layout bounds that might be useful in the next layout 51 * calculations. 52 */ onLayoutComplete()53 public void onLayoutComplete() { 54 mLastTotalSpace = getTotalSpace(); 55 } 56 57 /** 58 * Returns the layout space change between the previous layout pass and current layout pass. 59 * <p> 60 * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's 61 * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler, 62 * RecyclerView.State)} method. 63 * 64 * @return The difference between the current total space and previous layout's total space. 65 * @see #onLayoutComplete() 66 */ getTotalSpaceChange()67 public int getTotalSpaceChange() { 68 return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace; 69 } 70 71 /** 72 * Returns the start of the view including its decoration and margin. 73 * <p> 74 * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left 75 * decoration and 3px left margin, returned value will be 15px. 76 * 77 * @param view The view element to check 78 * @return The first pixel of the element 79 * @see #getDecoratedEnd(android.view.View) 80 */ getDecoratedStart(View view)81 public abstract int getDecoratedStart(View view); 82 83 /** 84 * Returns the end of the view including its decoration and margin. 85 * <p> 86 * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right 87 * decoration and 3px right margin, returned value will be 205. 88 * 89 * @param view The view element to check 90 * @return The last pixel of the element 91 * @see #getDecoratedStart(android.view.View) 92 */ getDecoratedEnd(View view)93 public abstract int getDecoratedEnd(View view); 94 95 /** 96 * Returns the space occupied by this View in the current orientation including decorations and 97 * margins. 98 * 99 * @param view The view element to check 100 * @return Total space occupied by this view 101 * @see #getDecoratedMeasurementInOther(View) 102 */ getDecoratedMeasurement(View view)103 public abstract int getDecoratedMeasurement(View view); 104 105 /** 106 * Returns the space occupied by this View in the perpendicular orientation including 107 * decorations and margins. 108 * 109 * @param view The view element to check 110 * @return Total space occupied by this view in the perpendicular orientation to current one 111 * @see #getDecoratedMeasurement(View) 112 */ getDecoratedMeasurementInOther(View view)113 public abstract int getDecoratedMeasurementInOther(View view); 114 115 /** 116 * Returns the start position of the layout after the start padding is added. 117 * 118 * @return The very first pixel we can draw. 119 */ getStartAfterPadding()120 public abstract int getStartAfterPadding(); 121 122 /** 123 * Returns the end position of the layout after the end padding is removed. 124 * 125 * @return The end boundary for this layout. 126 */ getEndAfterPadding()127 public abstract int getEndAfterPadding(); 128 129 /** 130 * Returns the end position of the layout without taking padding into account. 131 * 132 * @return The end boundary for this layout without considering padding. 133 */ getEnd()134 public abstract int getEnd(); 135 136 /** 137 * Offsets all children's positions by the given amount. 138 * 139 * @param amount Value to add to each child's layout parameters 140 */ offsetChildren(int amount)141 public abstract void offsetChildren(int amount); 142 143 /** 144 * Returns the total space to layout. This number is the difference between 145 * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}. 146 * 147 * @return Total space to layout children 148 */ getTotalSpace()149 public abstract int getTotalSpace(); 150 151 /** 152 * Offsets the child in this orientation. 153 * 154 * @param view View to offset 155 * @param offset offset amount 156 */ offsetChild(View view, int offset)157 public abstract void offsetChild(View view, int offset); 158 159 /** 160 * Returns the padding at the end of the layout. For horizontal helper, this is the right 161 * padding and for vertical helper, this is the bottom padding. This method does not check 162 * whether the layout is RTL or not. 163 * 164 * @return The padding at the end of the layout. 165 */ getEndPadding()166 public abstract int getEndPadding(); 167 168 /** 169 * Creates an OrientationHelper for the given LayoutManager and orientation. 170 * 171 * @param layoutManager LayoutManager to attach to 172 * @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL} 173 * @return A new OrientationHelper 174 */ createOrientationHelper( RecyclerView.LayoutManager layoutManager, int orientation)175 public static OrientationHelper createOrientationHelper( 176 RecyclerView.LayoutManager layoutManager, int orientation) { 177 switch (orientation) { 178 case HORIZONTAL: 179 return createHorizontalHelper(layoutManager); 180 case VERTICAL: 181 return createVerticalHelper(layoutManager); 182 } 183 throw new IllegalArgumentException("invalid orientation"); 184 } 185 186 /** 187 * Creates a horizontal OrientationHelper for the given LayoutManager. 188 * 189 * @param layoutManager The LayoutManager to attach to. 190 * @return A new OrientationHelper 191 */ createHorizontalHelper( RecyclerView.LayoutManager layoutManager)192 public static OrientationHelper createHorizontalHelper( 193 RecyclerView.LayoutManager layoutManager) { 194 return new OrientationHelper(layoutManager) { 195 @Override 196 public int getEndAfterPadding() { 197 return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight(); 198 } 199 200 @Override 201 public int getEnd() { 202 return mLayoutManager.getWidth(); 203 } 204 205 @Override 206 public void offsetChildren(int amount) { 207 mLayoutManager.offsetChildrenHorizontal(amount); 208 } 209 210 @Override 211 public int getStartAfterPadding() { 212 return mLayoutManager.getPaddingLeft(); 213 } 214 215 @Override 216 public int getDecoratedMeasurement(View view) { 217 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 218 view.getLayoutParams(); 219 return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin 220 + params.rightMargin; 221 } 222 223 @Override 224 public int getDecoratedMeasurementInOther(View view) { 225 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 226 view.getLayoutParams(); 227 return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin 228 + params.bottomMargin; 229 } 230 231 @Override 232 public int getDecoratedEnd(View view) { 233 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 234 view.getLayoutParams(); 235 return mLayoutManager.getDecoratedRight(view) + params.rightMargin; 236 } 237 238 @Override 239 public int getDecoratedStart(View view) { 240 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 241 view.getLayoutParams(); 242 return mLayoutManager.getDecoratedLeft(view) - params.leftMargin; 243 } 244 245 @Override 246 public int getTotalSpace() { 247 return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft() 248 - mLayoutManager.getPaddingRight(); 249 } 250 251 @Override 252 public void offsetChild(View view, int offset) { 253 view.offsetLeftAndRight(offset); 254 } 255 256 @Override 257 public int getEndPadding() { 258 return mLayoutManager.getPaddingRight(); 259 } 260 }; 261 } 262 263 /** 264 * Creates a vertical OrientationHelper for the given LayoutManager. 265 * 266 * @param layoutManager The LayoutManager to attach to. 267 * @return A new OrientationHelper 268 */ 269 public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) { 270 return new OrientationHelper(layoutManager) { 271 @Override 272 public int getEndAfterPadding() { 273 return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom(); 274 } 275 276 @Override 277 public int getEnd() { 278 return mLayoutManager.getHeight(); 279 } 280 281 @Override 282 public void offsetChildren(int amount) { 283 mLayoutManager.offsetChildrenVertical(amount); 284 } 285 286 @Override 287 public int getStartAfterPadding() { 288 return mLayoutManager.getPaddingTop(); 289 } 290 291 @Override 292 public int getDecoratedMeasurement(View view) { 293 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 294 view.getLayoutParams(); 295 return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin 296 + params.bottomMargin; 297 } 298 299 @Override 300 public int getDecoratedMeasurementInOther(View view) { 301 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 302 view.getLayoutParams(); 303 return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin 304 + params.rightMargin; 305 } 306 307 @Override 308 public int getDecoratedEnd(View view) { 309 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 310 view.getLayoutParams(); 311 return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin; 312 } 313 314 @Override 315 public int getDecoratedStart(View view) { 316 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) 317 view.getLayoutParams(); 318 return mLayoutManager.getDecoratedTop(view) - params.topMargin; 319 } 320 321 @Override 322 public int getTotalSpace() { 323 return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop() 324 - mLayoutManager.getPaddingBottom(); 325 } 326 327 @Override 328 public void offsetChild(View view, int offset) { 329 view.offsetTopAndBottom(offset); 330 } 331 332 @Override 333 public int getEndPadding() { 334 return mLayoutManager.getPaddingBottom(); 335 } 336 }; 337 } 338 }