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