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
17package android.support.percent;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.support.annotation.NonNull;
22import android.support.v4.view.MarginLayoutParamsCompat;
23import android.support.v4.view.ViewCompat;
24import android.util.AttributeSet;
25import android.util.Log;
26import android.view.View;
27import 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
123public 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
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     */
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     */
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     */
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     */
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     */
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
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
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
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
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         */
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
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         */
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
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         */
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         */
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 {
618        PercentLayoutInfo getPercentLayoutInfo();
619    }
620}
621