public class

PercentLayoutHelper

extends java.lang.Object

 java.lang.Object

↳androidx.percentlayout.widget.PercentLayoutHelper

Gradle dependencies

compile group: 'androidx.percentlayout', name: 'percentlayout', version: '1.0.0'

  • groupId: androidx.percentlayout
  • artifactId: percentlayout
  • version: 1.0.0

Artifact androidx.percentlayout:percentlayout:1.0.0 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.percentlayout:percentlayout com.android.support:percent

Androidx class mapping:

androidx.percentlayout.widget.PercentLayoutHelper android.support.percent.PercentLayoutHelper

Overview

Helper for layouts that want to support percentage based dimensions.

This class collects utility methods that are involved in extracting percentage based dimension attributes and applying them to ViewGroup's children. If you would like to implement a layout that supports percentage based dimensions, you need to take several steps:

  1. You need a subclass in your ViewGroup that implements PercentLayoutHelper.PercentLayoutParams.
  2. In your LayoutParams(Context c, AttributeSet attrs) constructor create an instance of PercentLayoutHelper.PercentLayoutInfo by calling PercentLayoutHelper.getPercentLayoutInfo(Context, AttributeSet). Return this object from public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() method that you implemented for PercentLayoutHelper.PercentLayoutParams interface.
  3. Override with a single line implementation PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
  4. In your ViewGroup override ViewGroup to return your LayoutParams.
  5. In your ViewGroup override, you need to implement following pattern:
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (mHelper.handleMeasuredStateTooSmall()) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
     }
     
  6. In your ViewGroup override, you need to implement following pattern:
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
         mHelper.restoreOriginalParams();
     }
     

Summary

Constructors
publicPercentLayoutHelper(ViewGroup host)

Methods
public voidadjustChildren(int widthMeasureSpec, int heightMeasureSpec)

Iterates over children and changes their width and height to one calculated from percentage values.

public static voidfetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array, int widthAttr, int heightAttr)

Helper method to be called from override that reads layout_width and layout_height attribute values without throwing an exception if they aren't present.

public static PercentLayoutHelper.PercentLayoutInfogetPercentLayoutInfo(Context context, AttributeSet attrs)

Constructs a PercentLayoutInfo from attributes associated with a View.

public booleanhandleMeasuredStateTooSmall()

Iterates over children and checks if any of them would like to get more space than it received through the percentage dimension.

public voidrestoreOriginalParams()

Iterates over children and restores their original dimensions that were changed for percentage values.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public PercentLayoutHelper(ViewGroup host)

Methods

public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array, int widthAttr, int heightAttr)

Helper method to be called from override that reads layout_width and layout_height attribute values without throwing an exception if they aren't present.

public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec)

Iterates over children and changes their width and height to one calculated from percentage values.

Parameters:

widthMeasureSpec: Width MeasureSpec of the parent ViewGroup.
heightMeasureSpec: Height MeasureSpec of the parent ViewGroup.

public static PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(Context context, AttributeSet attrs)

Constructs a PercentLayoutInfo from attributes associated with a View. Call this method from LayoutParams(Context c, AttributeSet attrs) constructor.

public void restoreOriginalParams()

Iterates over children and restores their original dimensions that were changed for percentage values. Calling this method only makes sense if you previously called PercentLayoutHelper.adjustChildren(int, int).

public boolean handleMeasuredStateTooSmall()

Iterates over children and checks if any of them would like to get more space than it received through the percentage dimension. If you are building a layout that supports percentage dimensions you are encouraged to take advantage of this method. The developer should be able to specify that a child should be remeasured by adding normal dimension attribute with wrap_content value. For example he might specify child's attributes as app:layout_widthPercent="60%p" and android:layout_width="wrap_content". In this case if the child receives too little space, it will be remeasured with width set to WRAP_CONTENT.

Returns:

True if the measure phase needs to be rerun because one of the children would like to receive more space.

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.percentlayout.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.core.view.MarginLayoutParamsCompat;
import androidx.core.view.ViewCompat;
import androidx.percentlayout.R;

/**
 * Helper for layouts that want to support percentage based dimensions.
 *
 * <p>This class collects utility methods that are involved in extracting percentage based dimension
 * attributes and applying them to ViewGroup's children. If you would like to implement a layout
 * that supports percentage based dimensions, you need to take several steps:
 *
 * <ol>
 * <li> You need a {@link ViewGroup.LayoutParams} subclass in your ViewGroup that implements
 * {@link PercentLayoutHelper.PercentLayoutParams}.
 * <li> In your {@code LayoutParams(Context c, AttributeSet attrs)} constructor create an instance
 * of {@link PercentLayoutHelper.PercentLayoutInfo} by calling
 * {@link PercentLayoutHelper#getPercentLayoutInfo(Context, AttributeSet)}. Return this
 * object from {@code public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()}
 * method that you implemented for {@link PercentLayoutHelper.PercentLayoutParams} interface.
 * <li> Override
 * {@link ViewGroup.LayoutParams#setBaseAttributes(TypedArray, int, int)}
 * with a single line implementation {@code PercentLayoutHelper.fetchWidthAndHeight(this, a,
 * widthAttr, heightAttr);}
 * <li> In your ViewGroup override {@link ViewGroup#generateLayoutParams(AttributeSet)} to return
 * your LayoutParams.
 * <li> In your {@link ViewGroup#onMeasure(int, int)} override, you need to implement following
 * pattern:
 * <pre class="prettyprint">
 * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 *     mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
 *     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 *     if (mHelper.handleMeasuredStateTooSmall()) {
 *         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 *     }
 * }
 * </pre>
 * <li>In your {@link ViewGroup#onLayout(boolean, int, int, int, int)} override, you need to
 * implement following pattern:
 * <pre class="prettyprint">
 * protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
 *     super.onLayout(changed, left, top, right, bottom);
 *     mHelper.restoreOriginalParams();
 * }
 * </pre>
 * </ol>
 * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
 * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
 * are used to define each percentage break point, and then a Button view is stretched to fill
 * the gap:
 *
 * <pre class="prettyprint">
 * &lt;androidx.constraintlayout.widget.ConstraintLayout
 *         xmlns:android="http://schemas.android.com/apk/res/android"
 *         xmlns:app="http://schemas.android.com/apk/res-auto"
 *         android:layout_width="match_parent"
 *         android:layout_height="match_parent"&gt
 *
 *     &lt;androidx.constraintlayout.widget.Guideline
 *         android:layout_width="wrap_content"
 *         android:layout_height="wrap_content"
 *         android:id="@+id/left_guideline"
 *         app:layout_constraintGuide_percent=".15"
 *         android:orientation="vertical"/&gt
 *
 *     &lt;androidx.constraintlayout.widget.Guideline
 *         android:layout_width="wrap_content"
 *         android:layout_height="wrap_content"
 *         android:id="@+id/right_guideline"
 *         app:layout_constraintGuide_percent=".85"
 *         android:orientation="vertical"/&gt
 *
 *     &lt;androidx.constraintlayout.widget.Guideline
 *         android:layout_width="wrap_content"
 *         android:layout_height="wrap_content"
 *         android:id="@+id/top_guideline"
 *         app:layout_constraintGuide_percent=".15"
 *         android:orientation="horizontal"/&gt
 *
 *     &lt;androidx.constraintlayout.widget.Guideline
 *         android:layout_width="wrap_content"
 *         android:layout_height="wrap_content"
 *         android:id="@+id/bottom_guideline"
 *         app:layout_constraintGuide_percent=".85"
 *         android:orientation="horizontal"/&gt
 *
 *     &lt;Button
 *         android:text="Button"
 *         android:layout_width="0dp"
 *         android:layout_height="0dp"
 *         android:id="@+id/button"
 *         app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
 *         app:layout_constraintRight_toRightOf="@+id/right_guideline"
 *         app:layout_constraintTop_toTopOf="@+id/top_guideline"
 *         app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /&gt
 *
 * &lt;/androidx.constraintlayout.widget.ConstraintLayout&gt
 */
@Deprecated
public class PercentLayoutHelper {
    private static final String TAG = "PercentLayout";

    private static final boolean DEBUG = false;
    private static final boolean VERBOSE = false;

    private final ViewGroup mHost;

    public PercentLayoutHelper(@NonNull ViewGroup host) {
        if (host == null) {
            throw new IllegalArgumentException("host must be non-null");
        }
        mHost = host;
    }

    /**
     * Helper method to be called from {@link ViewGroup.LayoutParams#setBaseAttributes} override
     * that reads layout_width and layout_height attribute values without throwing an exception if
     * they aren't present.
     */
    public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array,
            int widthAttr, int heightAttr) {
        params.width = array.getLayoutDimension(widthAttr, 0);
        params.height = array.getLayoutDimension(heightAttr, 0);
    }

    /**
     * Iterates over children and changes their width and height to one calculated from percentage
     * values.
     * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
     * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
     */
    public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
        if (DEBUG) {
            Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
                    + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
                    + View.MeasureSpec.toString(heightMeasureSpec));
        }

        // Calculate available space, accounting for host's paddings
        int widthHint = View.MeasureSpec.getSize(widthMeasureSpec) - mHost.getPaddingLeft()
                - mHost.getPaddingRight();
        int heightHint = View.MeasureSpec.getSize(heightMeasureSpec) - mHost.getPaddingTop()
                - mHost.getPaddingBottom();
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (DEBUG) {
                Log.d(TAG, "should adjust " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (DEBUG) {
                    Log.d(TAG, "using " + info);
                }
                if (info != null) {
                    if (params instanceof ViewGroup.MarginLayoutParams) {
                        info.fillMarginLayoutParams(view, (ViewGroup.MarginLayoutParams) params,
                                widthHint, heightHint);
                    } else {
                        info.fillLayoutParams(params, widthHint, heightHint);
                    }
                }
            }
        }
    }

    /**
     * Constructs a PercentLayoutInfo from attributes associated with a View. Call this method from
     * {@code LayoutParams(Context c, AttributeSet attrs)} constructor.
     */
    public static PercentLayoutInfo getPercentLayoutInfo(Context context,
            AttributeSet attrs) {
        PercentLayoutInfo info = null;
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
        float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent width: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.widthPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent height: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.heightPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.leftMarginPercent = value;
            info.topMarginPercent = value;
            info.rightMarginPercent = value;
            info.bottomMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent left margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.leftMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent top margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.topMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent right margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.rightMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent bottom margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.bottomMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent start margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.startMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
                -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "percent end margin: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.endMarginPercent = value;
        }

        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_aspectRatio, 1, 1, -1f);
        if (value != -1f) {
            if (VERBOSE) {
                Log.v(TAG, "aspect ratio: " + value);
            }
            info = info != null ? info : new PercentLayoutInfo();
            info.aspectRatio = value;
        }

        array.recycle();
        if (DEBUG) {
            Log.d(TAG, "constructed: " + info);
        }
        return info;
    }

    /**
     * Iterates over children and restores their original dimensions that were changed for
     * percentage values. Calling this method only makes sense if you previously called
     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
     */
    public void restoreOriginalParams() {
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (DEBUG) {
                Log.d(TAG, "should restore " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (DEBUG) {
                    Log.d(TAG, "using " + info);
                }
                if (info != null) {
                    if (params instanceof ViewGroup.MarginLayoutParams) {
                        info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
                    } else {
                        info.restoreLayoutParams(params);
                    }
                }
            }
        }
    }

    /**
     * Iterates over children and checks if any of them would like to get more space than it
     * received through the percentage dimension.
     *
     * If you are building a layout that supports percentage dimensions you are encouraged to take
     * advantage of this method. The developer should be able to specify that a child should be
     * remeasured by adding normal dimension attribute with {@code wrap_content} value. For example
     * he might specify child's attributes as {@code app:layout_widthPercent="60%p"} and
     * {@code android:layout_width="wrap_content"}. In this case if the child receives too little
     * space, it will be remeasured with width set to {@code WRAP_CONTENT}.
     *
     * @return True if the measure phase needs to be rerun because one of the children would like
     * to receive more space.
     */
    public boolean handleMeasuredStateTooSmall() {
        boolean needsSecondMeasure = false;
        for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            if (DEBUG) {
                Log.d(TAG, "should handle measured state too small " + view + " " + params);
            }
            if (params instanceof PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                if (info != null) {
                    if (shouldHandleMeasuredWidthTooSmall(view, info)) {
                        needsSecondMeasure = true;
                        params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                    if (shouldHandleMeasuredHeightTooSmall(view, info)) {
                        needsSecondMeasure = true;
                        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                }
            }
        }
        if (DEBUG) {
            Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
        }
        return needsSecondMeasure;
    }

    private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
        int state = view.getMeasuredWidthAndState() & View.MEASURED_STATE_MASK;
        return state == View.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0
                && info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
    }

    private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) {
        int state = view.getMeasuredHeightAndState() & View.MEASURED_STATE_MASK;
        return state == View.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0
                && info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT;
    }

    /* package */ static class PercentMarginLayoutParams extends ViewGroup.MarginLayoutParams {
        // These two flags keep track of whether we're computing the LayoutParams width and height
        // in the fill pass based on the aspect ratio. This allows the fill pass to be re-entrant
        // as the framework code can call onMeasure() multiple times before the onLayout() is
        // called. Those multiple invocations of onMeasure() are not guaranteed to be called with
        // the same set of width / height.
        boolean mIsHeightComputedFromAspectRatio;
        boolean mIsWidthComputedFromAspectRatio;

        public PercentMarginLayoutParams(int width, int height) {
            super(width, height);
        }
    }

    /**
     * Container for information about percentage dimensions and margins. It acts as an extension
     * for {@code LayoutParams}.
     *
     * @deprecated use ConstraintLayout and Guidelines for layout support.
     */
    @Deprecated
    public static class PercentLayoutInfo {
        /** The decimal value of the percentage-based width. */
        public float widthPercent;

        /** The decimal value of the percentage-based height. */
        public float heightPercent;

        /** The decimal value of the percentage-based left margin. */
        public float leftMarginPercent;

        /** The decimal value of the percentage-based top margin. */
        public float topMarginPercent;

        /** The decimal value of the percentage-based right margin. */
        public float rightMarginPercent;

        /** The decimal value of the percentage-based bottom margin. */
        public float bottomMarginPercent;

        /** The decimal value of the percentage-based start margin. */
        public float startMarginPercent;

        /** The decimal value of the percentage-based end margin. */
        public float endMarginPercent;

        /** The decimal value of the percentage-based aspect ratio. */
        public float aspectRatio;

        /* package */ final PercentMarginLayoutParams mPreservedParams;

        public PercentLayoutInfo() {
            widthPercent = -1f;
            heightPercent = -1f;
            leftMarginPercent = -1f;
            topMarginPercent = -1f;
            rightMarginPercent = -1f;
            bottomMarginPercent = -1f;
            startMarginPercent = -1f;
            endMarginPercent = -1f;
            mPreservedParams = new PercentMarginLayoutParams(0, 0);
        }

        /**
         * Fills the {@link ViewGroup.LayoutParams#width} and {@link ViewGroup.LayoutParams#height}
         * fields of the passed {@link ViewGroup.LayoutParams} object based on currently set
         * percentage values.
         */
        public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
                int heightHint) {
            // Preserve the original layout params, so we can restore them after the measure step.
            mPreservedParams.width = params.width;
            mPreservedParams.height = params.height;

            // We assume that width/height set to 0 means that value was unset. This might not
            // necessarily be true, as the user might explicitly set it to 0. However, we use this
            // information only for the aspect ratio. If the user set the aspect ratio attribute,
            // it means they accept or soon discover that it will be disregarded.
            final boolean widthNotSet =
                    (mPreservedParams.mIsWidthComputedFromAspectRatio
                            || mPreservedParams.width == 0) && (widthPercent < 0);
            final boolean heightNotSet =
                    (mPreservedParams.mIsHeightComputedFromAspectRatio
                            || mPreservedParams.height == 0) && (heightPercent < 0);

            if (widthPercent >= 0) {
                params.width = Math.round(widthHint * widthPercent);
            }

            if (heightPercent >= 0) {
                params.height = Math.round(heightHint * heightPercent);
            }

            if (aspectRatio >= 0) {
                if (widthNotSet) {
                    params.width = Math.round(params.height * aspectRatio);
                    // Keep track that we've filled the width based on the height and aspect ratio.
                    mPreservedParams.mIsWidthComputedFromAspectRatio = true;
                }
                if (heightNotSet) {
                    params.height = Math.round(params.width / aspectRatio);
                    // Keep track that we've filled the height based on the width and aspect ratio.
                    mPreservedParams.mIsHeightComputedFromAspectRatio = true;
                }
            }

            if (DEBUG) {
                Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
            }
        }

        /**
         * @deprecated Use
         * {@link #fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)}
         * for proper RTL support.
         */
        @Deprecated
        public void fillMarginLayoutParams(ViewGroup.MarginLayoutParams params,
                int widthHint, int heightHint) {
            fillMarginLayoutParams(null, params, widthHint, heightHint);
        }

        /**
         * Fills the margin fields of the passed {@link ViewGroup.MarginLayoutParams} object based
         * on currently set percentage values and the current layout direction of the passed
         * {@link View}.
         */
        public void fillMarginLayoutParams(View view, ViewGroup.MarginLayoutParams params,
                int widthHint, int heightHint) {
            fillLayoutParams(params, widthHint, heightHint);

            // Preserve the original margins, so we can restore them after the measure step.
            mPreservedParams.leftMargin = params.leftMargin;
            mPreservedParams.topMargin = params.topMargin;
            mPreservedParams.rightMargin = params.rightMargin;
            mPreservedParams.bottomMargin = params.bottomMargin;
            MarginLayoutParamsCompat.setMarginStart(mPreservedParams,
                    MarginLayoutParamsCompat.getMarginStart(params));
            MarginLayoutParamsCompat.setMarginEnd(mPreservedParams,
                    MarginLayoutParamsCompat.getMarginEnd(params));

            if (leftMarginPercent >= 0) {
                params.leftMargin = Math.round(widthHint * leftMarginPercent);
            }
            if (topMarginPercent >= 0) {
                params.topMargin = Math.round(heightHint * topMarginPercent);
            }
            if (rightMarginPercent >= 0) {
                params.rightMargin = Math.round(widthHint * rightMarginPercent);
            }
            if (bottomMarginPercent >= 0) {
                params.bottomMargin = Math.round(heightHint * bottomMarginPercent);
            }
            boolean shouldResolveLayoutDirection = false;
            if (startMarginPercent >= 0) {
                MarginLayoutParamsCompat.setMarginStart(params,
                        Math.round(widthHint * startMarginPercent));
                shouldResolveLayoutDirection = true;
            }
            if (endMarginPercent >= 0) {
                MarginLayoutParamsCompat.setMarginEnd(params,
                        Math.round(widthHint * endMarginPercent));
                shouldResolveLayoutDirection = true;
            }
            if (shouldResolveLayoutDirection && (view != null)) {
                // Force the resolve pass so that start / end margins are propagated to the
                // matching left / right fields
                MarginLayoutParamsCompat.resolveLayoutDirection(params,
                        ViewCompat.getLayoutDirection(view));
            }
            if (DEBUG) {
                Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
                        + ")");
            }
        }

        @Override
        public String toString() {
            return String.format("PercentLayoutInformation width: %f height %f, margins (%f, %f, "
                            + " %f, %f, %f, %f)", widthPercent, heightPercent, leftMarginPercent,
                    topMarginPercent, rightMarginPercent, bottomMarginPercent, startMarginPercent,
                    endMarginPercent);

        }

        /**
         * Restores the original dimensions and margins after they were changed for percentage based
         * values. You should call this method only if you previously called
         * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)}.
         */
        public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) {
            restoreLayoutParams(params);
            params.leftMargin = mPreservedParams.leftMargin;
            params.topMargin = mPreservedParams.topMargin;
            params.rightMargin = mPreservedParams.rightMargin;
            params.bottomMargin = mPreservedParams.bottomMargin;
            MarginLayoutParamsCompat.setMarginStart(params,
                    MarginLayoutParamsCompat.getMarginStart(mPreservedParams));
            MarginLayoutParamsCompat.setMarginEnd(params,
                    MarginLayoutParamsCompat.getMarginEnd(mPreservedParams));
        }

        /**
         * Restores original dimensions after they were changed for percentage based values.
         * You should call this method only if you previously called
         * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams(ViewGroup.LayoutParams, int, int)}.
         */
        public void restoreLayoutParams(ViewGroup.LayoutParams params) {
            if (!mPreservedParams.mIsWidthComputedFromAspectRatio) {
                // Only restore the width if we didn't compute it based on the height and
                // aspect ratio in the fill pass.
                params.width = mPreservedParams.width;
            }
            if (!mPreservedParams.mIsHeightComputedFromAspectRatio) {
                // Only restore the height if we didn't compute it based on the width and
                // aspect ratio in the fill pass.
                params.height = mPreservedParams.height;
            }

            // Reset the tracking flags.
            mPreservedParams.mIsWidthComputedFromAspectRatio = false;
            mPreservedParams.mIsHeightComputedFromAspectRatio = false;
        }
    }

    /**
     * If a layout wants to support percentage based dimensions and use this helper class, its
     * {@code LayoutParams} subclass must implement this interface.
     *
     * Your {@code LayoutParams} subclass should contain an instance of {@code PercentLayoutInfo}
     * and the implementation of this interface should be a simple accessor.
     *
     * @deprecated this class is deprecated along with its parent class.
     */
    @Deprecated
    public interface PercentLayoutParams {
        PercentLayoutInfo getPercentLayoutInfo();
    }
}