1 /* 2 * Copyright 2018 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 androidx.percentlayout.widget; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.util.AttributeSet; 22 import android.util.Log; 23 import android.view.View; 24 import android.view.ViewGroup; 25 26 import androidx.annotation.NonNull; 27 import androidx.core.view.MarginLayoutParamsCompat; 28 import androidx.core.view.ViewCompat; 29 import androidx.percentlayout.R; 30 31 /** 32 * Helper for layouts that want to support percentage based dimensions. 33 * 34 * <p>This class collects utility methods that are involved in extracting percentage based dimension 35 * attributes and applying them to ViewGroup's children. If you would like to implement a layout 36 * that supports percentage based dimensions, you need to take several steps: 37 * 38 * <ol> 39 * <li> You need a {@link ViewGroup.LayoutParams} subclass in your ViewGroup that implements 40 * {@link PercentLayoutHelper.PercentLayoutParams}. 41 * <li> In your {@code LayoutParams(Context c, AttributeSet attrs)} constructor create an instance 42 * of {@link PercentLayoutHelper.PercentLayoutInfo} by calling 43 * {@link PercentLayoutHelper#getPercentLayoutInfo(Context, AttributeSet)}. Return this 44 * object from {@code public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()} 45 * method that you implemented for {@link PercentLayoutHelper.PercentLayoutParams} interface. 46 * <li> Override 47 * {@link ViewGroup.LayoutParams#setBaseAttributes(TypedArray, int, int)} 48 * with a single line implementation {@code PercentLayoutHelper.fetchWidthAndHeight(this, a, 49 * widthAttr, heightAttr);} 50 * <li> In your ViewGroup override {@link ViewGroup#generateLayoutParams(AttributeSet)} to return 51 * your LayoutParams. 52 * <li> In your {@link ViewGroup#onMeasure(int, int)} override, you need to implement following 53 * pattern: 54 * <pre class="prettyprint"> 55 * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 56 * mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec); 57 * super.onMeasure(widthMeasureSpec, heightMeasureSpec); 58 * if (mHelper.handleMeasuredStateTooSmall()) { 59 * super.onMeasure(widthMeasureSpec, heightMeasureSpec); 60 * } 61 * } 62 * </pre> 63 * <li>In your {@link ViewGroup#onLayout(boolean, int, int, int, int)} override, you need to 64 * implement following pattern: 65 * <pre class="prettyprint"> 66 * protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 67 * super.onLayout(changed, left, top, right, bottom); 68 * mHelper.restoreOriginalParams(); 69 * } 70 * </pre> 71 * </ol> 72 * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows 73 * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines 74 * are used to define each percentage break point, and then a Button view is stretched to fill 75 * the gap: 76 * 77 * <pre class="prettyprint"> 78 * <androidx.constraintlayout.widget.ConstraintLayout 79 * xmlns:android="http://schemas.android.com/apk/res/android" 80 * xmlns:app="http://schemas.android.com/apk/res-auto" 81 * android:layout_width="match_parent" 82 * android:layout_height="match_parent"> 83 * 84 * <androidx.constraintlayout.widget.Guideline 85 * android:layout_width="wrap_content" 86 * android:layout_height="wrap_content" 87 * android:id="@+id/left_guideline" 88 * app:layout_constraintGuide_percent=".15" 89 * android:orientation="vertical"/> 90 * 91 * <androidx.constraintlayout.widget.Guideline 92 * android:layout_width="wrap_content" 93 * android:layout_height="wrap_content" 94 * android:id="@+id/right_guideline" 95 * app:layout_constraintGuide_percent=".85" 96 * android:orientation="vertical"/> 97 * 98 * <androidx.constraintlayout.widget.Guideline 99 * android:layout_width="wrap_content" 100 * android:layout_height="wrap_content" 101 * android:id="@+id/top_guideline" 102 * app:layout_constraintGuide_percent=".15" 103 * android:orientation="horizontal"/> 104 * 105 * <androidx.constraintlayout.widget.Guideline 106 * android:layout_width="wrap_content" 107 * android:layout_height="wrap_content" 108 * android:id="@+id/bottom_guideline" 109 * app:layout_constraintGuide_percent=".85" 110 * android:orientation="horizontal"/> 111 * 112 * <Button 113 * android:text="Button" 114 * android:layout_width="0dp" 115 * android:layout_height="0dp" 116 * android:id="@+id/button" 117 * app:layout_constraintLeft_toLeftOf="@+id/left_guideline" 118 * app:layout_constraintRight_toRightOf="@+id/right_guideline" 119 * app:layout_constraintTop_toTopOf="@+id/top_guideline" 120 * app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /> 121 * 122 * </androidx.constraintlayout.widget.ConstraintLayout> 123 */ 124 @Deprecated 125 public class PercentLayoutHelper { 126 private static final String TAG = "PercentLayout"; 127 128 private static final boolean DEBUG = false; 129 private static final boolean VERBOSE = false; 130 131 private final ViewGroup mHost; 132 PercentLayoutHelper(@onNull ViewGroup host)133 public PercentLayoutHelper(@NonNull ViewGroup host) { 134 if (host == null) { 135 throw new IllegalArgumentException("host must be non-null"); 136 } 137 mHost = host; 138 } 139 140 /** 141 * Helper method to be called from {@link ViewGroup.LayoutParams#setBaseAttributes} override 142 * that reads layout_width and layout_height attribute values without throwing an exception if 143 * they aren't present. 144 */ fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array, int widthAttr, int heightAttr)145 public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array, 146 int widthAttr, int heightAttr) { 147 params.width = array.getLayoutDimension(widthAttr, 0); 148 params.height = array.getLayoutDimension(heightAttr, 0); 149 } 150 151 /** 152 * Iterates over children and changes their width and height to one calculated from percentage 153 * values. 154 * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup. 155 * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup. 156 */ adjustChildren(int widthMeasureSpec, int heightMeasureSpec)157 public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) { 158 if (DEBUG) { 159 Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: " 160 + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: " 161 + View.MeasureSpec.toString(heightMeasureSpec)); 162 } 163 164 // Calculate available space, accounting for host's paddings 165 int widthHint = View.MeasureSpec.getSize(widthMeasureSpec) - mHost.getPaddingLeft() 166 - mHost.getPaddingRight(); 167 int heightHint = View.MeasureSpec.getSize(heightMeasureSpec) - mHost.getPaddingTop() 168 - mHost.getPaddingBottom(); 169 for (int i = 0, N = mHost.getChildCount(); i < N; i++) { 170 View view = mHost.getChildAt(i); 171 ViewGroup.LayoutParams params = view.getLayoutParams(); 172 if (DEBUG) { 173 Log.d(TAG, "should adjust " + view + " " + params); 174 } 175 if (params instanceof PercentLayoutParams) { 176 PercentLayoutInfo info = 177 ((PercentLayoutParams) params).getPercentLayoutInfo(); 178 if (DEBUG) { 179 Log.d(TAG, "using " + info); 180 } 181 if (info != null) { 182 if (params instanceof ViewGroup.MarginLayoutParams) { 183 info.fillMarginLayoutParams(view, (ViewGroup.MarginLayoutParams) params, 184 widthHint, heightHint); 185 } else { 186 info.fillLayoutParams(params, widthHint, heightHint); 187 } 188 } 189 } 190 } 191 } 192 193 /** 194 * Constructs a PercentLayoutInfo from attributes associated with a View. Call this method from 195 * {@code LayoutParams(Context c, AttributeSet attrs)} constructor. 196 */ getPercentLayoutInfo(Context context, AttributeSet attrs)197 public static PercentLayoutInfo getPercentLayoutInfo(Context context, 198 AttributeSet attrs) { 199 PercentLayoutInfo info = null; 200 TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout); 201 float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1, 202 -1f); 203 if (value != -1f) { 204 if (VERBOSE) { 205 Log.v(TAG, "percent width: " + value); 206 } 207 info = info != null ? info : new PercentLayoutInfo(); 208 info.widthPercent = value; 209 } 210 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f); 211 if (value != -1f) { 212 if (VERBOSE) { 213 Log.v(TAG, "percent height: " + value); 214 } 215 info = info != null ? info : new PercentLayoutInfo(); 216 info.heightPercent = value; 217 } 218 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f); 219 if (value != -1f) { 220 if (VERBOSE) { 221 Log.v(TAG, "percent margin: " + value); 222 } 223 info = info != null ? info : new PercentLayoutInfo(); 224 info.leftMarginPercent = value; 225 info.topMarginPercent = value; 226 info.rightMarginPercent = value; 227 info.bottomMarginPercent = value; 228 } 229 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1, 230 -1f); 231 if (value != -1f) { 232 if (VERBOSE) { 233 Log.v(TAG, "percent left margin: " + value); 234 } 235 info = info != null ? info : new PercentLayoutInfo(); 236 info.leftMarginPercent = value; 237 } 238 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1, 239 -1f); 240 if (value != -1f) { 241 if (VERBOSE) { 242 Log.v(TAG, "percent top margin: " + value); 243 } 244 info = info != null ? info : new PercentLayoutInfo(); 245 info.topMarginPercent = value; 246 } 247 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1, 248 -1f); 249 if (value != -1f) { 250 if (VERBOSE) { 251 Log.v(TAG, "percent right margin: " + value); 252 } 253 info = info != null ? info : new PercentLayoutInfo(); 254 info.rightMarginPercent = value; 255 } 256 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1, 257 -1f); 258 if (value != -1f) { 259 if (VERBOSE) { 260 Log.v(TAG, "percent bottom margin: " + value); 261 } 262 info = info != null ? info : new PercentLayoutInfo(); 263 info.bottomMarginPercent = value; 264 } 265 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1, 266 -1f); 267 if (value != -1f) { 268 if (VERBOSE) { 269 Log.v(TAG, "percent start margin: " + value); 270 } 271 info = info != null ? info : new PercentLayoutInfo(); 272 info.startMarginPercent = value; 273 } 274 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1, 275 -1f); 276 if (value != -1f) { 277 if (VERBOSE) { 278 Log.v(TAG, "percent end margin: " + value); 279 } 280 info = info != null ? info : new PercentLayoutInfo(); 281 info.endMarginPercent = value; 282 } 283 284 value = array.getFraction(R.styleable.PercentLayout_Layout_layout_aspectRatio, 1, 1, -1f); 285 if (value != -1f) { 286 if (VERBOSE) { 287 Log.v(TAG, "aspect ratio: " + value); 288 } 289 info = info != null ? info : new PercentLayoutInfo(); 290 info.aspectRatio = value; 291 } 292 293 array.recycle(); 294 if (DEBUG) { 295 Log.d(TAG, "constructed: " + info); 296 } 297 return info; 298 } 299 300 /** 301 * Iterates over children and restores their original dimensions that were changed for 302 * percentage values. Calling this method only makes sense if you previously called 303 * {@link PercentLayoutHelper#adjustChildren(int, int)}. 304 */ restoreOriginalParams()305 public void restoreOriginalParams() { 306 for (int i = 0, N = mHost.getChildCount(); i < N; i++) { 307 View view = mHost.getChildAt(i); 308 ViewGroup.LayoutParams params = view.getLayoutParams(); 309 if (DEBUG) { 310 Log.d(TAG, "should restore " + view + " " + params); 311 } 312 if (params instanceof PercentLayoutParams) { 313 PercentLayoutInfo info = 314 ((PercentLayoutParams) params).getPercentLayoutInfo(); 315 if (DEBUG) { 316 Log.d(TAG, "using " + info); 317 } 318 if (info != null) { 319 if (params instanceof ViewGroup.MarginLayoutParams) { 320 info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params); 321 } else { 322 info.restoreLayoutParams(params); 323 } 324 } 325 } 326 } 327 } 328 329 /** 330 * Iterates over children and checks if any of them would like to get more space than it 331 * received through the percentage dimension. 332 * 333 * If you are building a layout that supports percentage dimensions you are encouraged to take 334 * advantage of this method. The developer should be able to specify that a child should be 335 * remeasured by adding normal dimension attribute with {@code wrap_content} value. For example 336 * he might specify child's attributes as {@code app:layout_widthPercent="60%p"} and 337 * {@code android:layout_width="wrap_content"}. In this case if the child receives too little 338 * space, it will be remeasured with width set to {@code WRAP_CONTENT}. 339 * 340 * @return True if the measure phase needs to be rerun because one of the children would like 341 * to receive more space. 342 */ handleMeasuredStateTooSmall()343 public boolean handleMeasuredStateTooSmall() { 344 boolean needsSecondMeasure = false; 345 for (int i = 0, N = mHost.getChildCount(); i < N; i++) { 346 View view = mHost.getChildAt(i); 347 ViewGroup.LayoutParams params = view.getLayoutParams(); 348 if (DEBUG) { 349 Log.d(TAG, "should handle measured state too small " + view + " " + params); 350 } 351 if (params instanceof PercentLayoutParams) { 352 PercentLayoutInfo info = 353 ((PercentLayoutParams) params).getPercentLayoutInfo(); 354 if (info != null) { 355 if (shouldHandleMeasuredWidthTooSmall(view, info)) { 356 needsSecondMeasure = true; 357 params.width = ViewGroup.LayoutParams.WRAP_CONTENT; 358 } 359 if (shouldHandleMeasuredHeightTooSmall(view, info)) { 360 needsSecondMeasure = true; 361 params.height = ViewGroup.LayoutParams.WRAP_CONTENT; 362 } 363 } 364 } 365 } 366 if (DEBUG) { 367 Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure); 368 } 369 return needsSecondMeasure; 370 } 371 shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info)372 private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) { 373 int state = view.getMeasuredWidthAndState() & View.MEASURED_STATE_MASK; 374 return state == View.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 375 && info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT; 376 } 377 shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info)378 private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) { 379 int state = view.getMeasuredHeightAndState() & View.MEASURED_STATE_MASK; 380 return state == View.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0 381 && info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT; 382 } 383 384 /* package */ static class PercentMarginLayoutParams extends ViewGroup.MarginLayoutParams { 385 // These two flags keep track of whether we're computing the LayoutParams width and height 386 // in the fill pass based on the aspect ratio. This allows the fill pass to be re-entrant 387 // as the framework code can call onMeasure() multiple times before the onLayout() is 388 // called. Those multiple invocations of onMeasure() are not guaranteed to be called with 389 // the same set of width / height. 390 private boolean mIsHeightComputedFromAspectRatio; 391 private boolean mIsWidthComputedFromAspectRatio; 392 PercentMarginLayoutParams(int width, int height)393 public PercentMarginLayoutParams(int width, int height) { 394 super(width, height); 395 } 396 } 397 398 /** 399 * Container for information about percentage dimensions and margins. It acts as an extension 400 * for {@code LayoutParams}. 401 * 402 * @deprecated use ConstraintLayout and Guidelines for layout support. 403 */ 404 @Deprecated 405 public static class PercentLayoutInfo { 406 /** The decimal value of the percentage-based width. */ 407 public float widthPercent; 408 409 /** The decimal value of the percentage-based height. */ 410 public float heightPercent; 411 412 /** The decimal value of the percentage-based left margin. */ 413 public float leftMarginPercent; 414 415 /** The decimal value of the percentage-based top margin. */ 416 public float topMarginPercent; 417 418 /** The decimal value of the percentage-based right margin. */ 419 public float rightMarginPercent; 420 421 /** The decimal value of the percentage-based bottom margin. */ 422 public float bottomMarginPercent; 423 424 /** The decimal value of the percentage-based start margin. */ 425 public float startMarginPercent; 426 427 /** The decimal value of the percentage-based end margin. */ 428 public float endMarginPercent; 429 430 /** The decimal value of the percentage-based aspect ratio. */ 431 public float aspectRatio; 432 433 /* package */ final PercentMarginLayoutParams mPreservedParams; 434 PercentLayoutInfo()435 public PercentLayoutInfo() { 436 widthPercent = -1f; 437 heightPercent = -1f; 438 leftMarginPercent = -1f; 439 topMarginPercent = -1f; 440 rightMarginPercent = -1f; 441 bottomMarginPercent = -1f; 442 startMarginPercent = -1f; 443 endMarginPercent = -1f; 444 mPreservedParams = new PercentMarginLayoutParams(0, 0); 445 } 446 447 /** 448 * Fills the {@link ViewGroup.LayoutParams#width} and {@link ViewGroup.LayoutParams#height} 449 * fields of the passed {@link ViewGroup.LayoutParams} object based on currently set 450 * percentage values. 451 */ fillLayoutParams(ViewGroup.LayoutParams params, int widthHint, int heightHint)452 public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint, 453 int heightHint) { 454 // Preserve the original layout params, so we can restore them after the measure step. 455 mPreservedParams.width = params.width; 456 mPreservedParams.height = params.height; 457 458 // We assume that width/height set to 0 means that value was unset. This might not 459 // necessarily be true, as the user might explicitly set it to 0. However, we use this 460 // information only for the aspect ratio. If the user set the aspect ratio attribute, 461 // it means they accept or soon discover that it will be disregarded. 462 final boolean widthNotSet = 463 (mPreservedParams.mIsWidthComputedFromAspectRatio 464 || mPreservedParams.width == 0) && (widthPercent < 0); 465 final boolean heightNotSet = 466 (mPreservedParams.mIsHeightComputedFromAspectRatio 467 || mPreservedParams.height == 0) && (heightPercent < 0); 468 469 if (widthPercent >= 0) { 470 params.width = Math.round(widthHint * widthPercent); 471 } 472 473 if (heightPercent >= 0) { 474 params.height = Math.round(heightHint * heightPercent); 475 } 476 477 if (aspectRatio >= 0) { 478 if (widthNotSet) { 479 params.width = Math.round(params.height * aspectRatio); 480 // Keep track that we've filled the width based on the height and aspect ratio. 481 mPreservedParams.mIsWidthComputedFromAspectRatio = true; 482 } 483 if (heightNotSet) { 484 params.height = Math.round(params.width / aspectRatio); 485 // Keep track that we've filled the height based on the width and aspect ratio. 486 mPreservedParams.mIsHeightComputedFromAspectRatio = true; 487 } 488 } 489 490 if (DEBUG) { 491 Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")"); 492 } 493 } 494 495 /** 496 * @deprecated Use 497 * {@link #fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)} 498 * for proper RTL support. 499 */ 500 @Deprecated fillMarginLayoutParams(ViewGroup.MarginLayoutParams params, int widthHint, int heightHint)501 public void fillMarginLayoutParams(ViewGroup.MarginLayoutParams params, 502 int widthHint, int heightHint) { 503 fillMarginLayoutParams(null, params, widthHint, heightHint); 504 } 505 506 /** 507 * Fills the margin fields of the passed {@link ViewGroup.MarginLayoutParams} object based 508 * on currently set percentage values and the current layout direction of the passed 509 * {@link View}. 510 */ fillMarginLayoutParams(View view, ViewGroup.MarginLayoutParams params, int widthHint, int heightHint)511 public void fillMarginLayoutParams(View view, ViewGroup.MarginLayoutParams params, 512 int widthHint, int heightHint) { 513 fillLayoutParams(params, widthHint, heightHint); 514 515 // Preserve the original margins, so we can restore them after the measure step. 516 mPreservedParams.leftMargin = params.leftMargin; 517 mPreservedParams.topMargin = params.topMargin; 518 mPreservedParams.rightMargin = params.rightMargin; 519 mPreservedParams.bottomMargin = params.bottomMargin; 520 MarginLayoutParamsCompat.setMarginStart(mPreservedParams, 521 MarginLayoutParamsCompat.getMarginStart(params)); 522 MarginLayoutParamsCompat.setMarginEnd(mPreservedParams, 523 MarginLayoutParamsCompat.getMarginEnd(params)); 524 525 if (leftMarginPercent >= 0) { 526 params.leftMargin = Math.round(widthHint * leftMarginPercent); 527 } 528 if (topMarginPercent >= 0) { 529 params.topMargin = Math.round(heightHint * topMarginPercent); 530 } 531 if (rightMarginPercent >= 0) { 532 params.rightMargin = Math.round(widthHint * rightMarginPercent); 533 } 534 if (bottomMarginPercent >= 0) { 535 params.bottomMargin = Math.round(heightHint * bottomMarginPercent); 536 } 537 boolean shouldResolveLayoutDirection = false; 538 if (startMarginPercent >= 0) { 539 MarginLayoutParamsCompat.setMarginStart(params, 540 Math.round(widthHint * startMarginPercent)); 541 shouldResolveLayoutDirection = true; 542 } 543 if (endMarginPercent >= 0) { 544 MarginLayoutParamsCompat.setMarginEnd(params, 545 Math.round(widthHint * endMarginPercent)); 546 shouldResolveLayoutDirection = true; 547 } 548 if (shouldResolveLayoutDirection && (view != null)) { 549 // Force the resolve pass so that start / end margins are propagated to the 550 // matching left / right fields 551 MarginLayoutParamsCompat.resolveLayoutDirection(params, 552 ViewCompat.getLayoutDirection(view)); 553 } 554 if (DEBUG) { 555 Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height 556 + ")"); 557 } 558 } 559 560 @Override toString()561 public String toString() { 562 return String.format("PercentLayoutInformation width: %f height %f, margins (%f, %f, " 563 + " %f, %f, %f, %f)", widthPercent, heightPercent, leftMarginPercent, 564 topMarginPercent, rightMarginPercent, bottomMarginPercent, startMarginPercent, 565 endMarginPercent); 566 567 } 568 569 /** 570 * Restores the original dimensions and margins after they were changed for percentage based 571 * values. You should call this method only if you previously called 572 * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)}. 573 */ restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params)574 public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) { 575 restoreLayoutParams(params); 576 params.leftMargin = mPreservedParams.leftMargin; 577 params.topMargin = mPreservedParams.topMargin; 578 params.rightMargin = mPreservedParams.rightMargin; 579 params.bottomMargin = mPreservedParams.bottomMargin; 580 MarginLayoutParamsCompat.setMarginStart(params, 581 MarginLayoutParamsCompat.getMarginStart(mPreservedParams)); 582 MarginLayoutParamsCompat.setMarginEnd(params, 583 MarginLayoutParamsCompat.getMarginEnd(mPreservedParams)); 584 } 585 586 /** 587 * Restores original dimensions after they were changed for percentage based values. 588 * You should call this method only if you previously called 589 * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams(ViewGroup.LayoutParams, int, int)}. 590 */ restoreLayoutParams(ViewGroup.LayoutParams params)591 public void restoreLayoutParams(ViewGroup.LayoutParams params) { 592 if (!mPreservedParams.mIsWidthComputedFromAspectRatio) { 593 // Only restore the width if we didn't compute it based on the height and 594 // aspect ratio in the fill pass. 595 params.width = mPreservedParams.width; 596 } 597 if (!mPreservedParams.mIsHeightComputedFromAspectRatio) { 598 // Only restore the height if we didn't compute it based on the width and 599 // aspect ratio in the fill pass. 600 params.height = mPreservedParams.height; 601 } 602 603 // Reset the tracking flags. 604 mPreservedParams.mIsWidthComputedFromAspectRatio = false; 605 mPreservedParams.mIsHeightComputedFromAspectRatio = false; 606 } 607 } 608 609 /** 610 * If a layout wants to support percentage based dimensions and use this helper class, its 611 * {@code LayoutParams} subclass must implement this interface. 612 * 613 * Your {@code LayoutParams} subclass should contain an instance of {@code PercentLayoutInfo} 614 * and the implementation of this interface should be a simple accessor. 615 * 616 * @deprecated this class is deprecated along with its parent class. 617 */ 618 @Deprecated 619 public interface PercentLayoutParams { getPercentLayoutInfo()620 PercentLayoutInfo getPercentLayoutInfo(); 621 } 622 } 623