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