1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.percentlayout.widget;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.util.AttributeSet;
22import android.view.ViewGroup;
23import android.widget.FrameLayout;
24
25import androidx.annotation.RequiresApi;
26
27/**
28 * Subclass of {@link android.widget.FrameLayout} that supports percentage based dimensions and
29 * margins.
30 *
31 * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
32 * this example:
33 *
34 * <pre class="prettyprint">
35 * &lt;androidx.percentlayout.widget.PercentFrameLayout
36 *         xmlns:android="http://schemas.android.com/apk/res/android"
37 *         xmlns:app="http://schemas.android.com/apk/res-auto"
38 *         android:layout_width="match_parent"
39 *         android:layout_height="match_parent"&gt
40 *     &lt;ImageView
41 *         app:layout_widthPercent="50%"
42 *         app:layout_heightPercent="50%"
43 *         app:layout_marginTopPercent="25%"
44 *         app:layout_marginLeftPercent="25%"/&gt
45 * &lt;/androidx.percentlayout.widget.PercentFrameLayout&gt
46 * </pre>
47 *
48 * The attributes that you can use are:
49 * <ul>
50 *     <li>{@code layout_widthPercent}
51 *     <li>{@code layout_heightPercent}
52 *     <li>{@code layout_marginPercent}
53 *     <li>{@code layout_marginLeftPercent}
54 *     <li>{@code layout_marginTopPercent}
55 *     <li>{@code layout_marginRightPercent}
56 *     <li>{@code layout_marginBottomPercent}
57 *     <li>{@code layout_marginStartPercent}
58 *     <li>{@code layout_marginEndPercent}
59 *     <li>{@code layout_aspectRatio}
60 * </ul>
61 *
62 * It is not necessary to specify {@code layout_width/height} if you specify {@code
63 * layout_widthPercent.} However, if you want the view to be able to take up more space than what
64 * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
65 * if the percentage size is too small for the View's content, it will be resized using
66 * {@code wrap_content} rule.
67 *
68 * <p>
69 * You can also make one dimension be a fraction of the other by setting only width or height and
70 * using {@code layout_aspectRatio} for the second one to be calculated automatically. For
71 * example, if you would like to achieve 16:9 aspect ratio, you can write:
72 * <pre class="prettyprint">
73 *     android:layout_width="300dp"
74 *     app:layout_aspectRatio="178%"
75 * </pre>
76 * This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted
77 * accordingly.
78 *
79 * @deprecated consider using ConstraintLayout and associated layouts instead. The following shows
80 * how to replicate the functionality of percentage layouts with a ConstraintLayout. The Guidelines
81 * are used to define each percentage break point, and then a Button view is stretched to fill
82 * the gap:
83 *
84 * <pre class="prettyprint">
85 * &lt;androidx.constraintlayout.widget.ConstraintLayout
86 *         xmlns:android="http://schemas.android.com/apk/res/android"
87 *         xmlns:app="http://schemas.android.com/apk/res-auto"
88 *         android:layout_width="match_parent"
89 *         android:layout_height="match_parent"&gt
90 *
91 *     &lt;androidx.constraintlayout.widget.Guideline
92 *         android:layout_width="wrap_content"
93 *         android:layout_height="wrap_content"
94 *         android:id="@+id/left_guideline"
95 *         app:layout_constraintGuide_percent=".15"
96 *         android:orientation="vertical"/&gt
97 *
98 *     &lt;androidx.constraintlayout.widget.Guideline
99 *         android:layout_width="wrap_content"
100 *         android:layout_height="wrap_content"
101 *         android:id="@+id/right_guideline"
102 *         app:layout_constraintGuide_percent=".85"
103 *         android:orientation="vertical"/&gt
104 *
105 *     &lt;androidx.constraintlayout.widget.Guideline
106 *         android:layout_width="wrap_content"
107 *         android:layout_height="wrap_content"
108 *         android:id="@+id/top_guideline"
109 *         app:layout_constraintGuide_percent=".15"
110 *         android:orientation="horizontal"/&gt
111 *
112 *     &lt;androidx.constraintlayout.widget.Guideline
113 *         android:layout_width="wrap_content"
114 *         android:layout_height="wrap_content"
115 *         android:id="@+id/bottom_guideline"
116 *         app:layout_constraintGuide_percent=".85"
117 *         android:orientation="horizontal"/&gt
118 *
119 *     &lt;Button
120 *         android:text="Button"
121 *         android:layout_width="0dp"
122 *         android:layout_height="0dp"
123 *         android:id="@+id/button"
124 *         app:layout_constraintLeft_toLeftOf="@+id/left_guideline"
125 *         app:layout_constraintRight_toRightOf="@+id/right_guideline"
126 *         app:layout_constraintTop_toTopOf="@+id/top_guideline"
127 *         app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /&gt
128 *
129 * &lt;/androidx.constraintlayout.widget.ConstraintLayout&gt
130 * </pre>
131 */
132@Deprecated
133public class PercentFrameLayout extends FrameLayout {
134    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
135
136    public PercentFrameLayout(Context context) {
137        super(context);
138    }
139
140    public PercentFrameLayout(Context context, AttributeSet attrs) {
141        super(context, attrs);
142    }
143
144    public PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
145        super(context, attrs, defStyleAttr);
146    }
147
148    @Override
149    protected LayoutParams generateDefaultLayoutParams() {
150        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
151    }
152
153    @Override
154    public LayoutParams generateLayoutParams(AttributeSet attrs) {
155        return new LayoutParams(getContext(), attrs);
156    }
157
158    @Override
159    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
160        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
161        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
162        if (mHelper.handleMeasuredStateTooSmall()) {
163            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
164        }
165    }
166
167    @Override
168    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
169        super.onLayout(changed, left, top, right, bottom);
170        mHelper.restoreOriginalParams();
171    }
172
173    /**
174     * @deprecated this class is deprecated along with its parent class.
175     */
176    @Deprecated
177    public static class LayoutParams extends FrameLayout.LayoutParams
178            implements PercentLayoutHelper.PercentLayoutParams {
179        private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
180
181        public LayoutParams(Context c, AttributeSet attrs) {
182            super(c, attrs);
183            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
184        }
185
186        public LayoutParams(int width, int height) {
187            super(width, height);
188        }
189
190        public LayoutParams(int width, int height, int gravity) {
191            super(width, height, gravity);
192        }
193
194        public LayoutParams(ViewGroup.LayoutParams source) {
195            super(source);
196        }
197
198        public LayoutParams(MarginLayoutParams source) {
199            super(source);
200        }
201
202        public LayoutParams(FrameLayout.LayoutParams source) {
203            super((MarginLayoutParams) source);
204            gravity = source.gravity;
205        }
206
207        @RequiresApi(19)
208        public LayoutParams(LayoutParams source) {
209            // The copy constructor used here is only supported on API 19+.
210            this((FrameLayout.LayoutParams) source);
211            mPercentLayoutInfo = source.mPercentLayoutInfo;
212        }
213
214        @Override
215        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
216            if (mPercentLayoutInfo == null) {
217                mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo();
218            }
219
220            return mPercentLayoutInfo;
221        }
222
223        @Override
224        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
225            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
226        }
227    }
228}
229