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.util.AttributeSet;
22 import android.view.ViewGroup;
23 import android.widget.RelativeLayout;
24 
25 /**
26  * Subclass of {@link android.widget.RelativeLayout} that supports percentage based dimensions and
27  * margins.
28  *
29  * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
30  * this example:
31  *
32  * <pre class="prettyprint">
33  * &lt;android.support.percent.PercentRelativeLayout
34  *         xmlns:android="http://schemas.android.com/apk/res/android"
35  *         xmlns:app="http://schemas.android.com/apk/res-auto"
36  *         android:layout_width="match_parent"
37  *         android:layout_height="match_parent"&gt
38  *     &lt;ImageView
39  *         app:layout_widthPercent="50%"
40  *         app:layout_heightPercent="50%"
41  *         app:layout_marginTopPercent="25%"
42  *         app:layout_marginLeftPercent="25%"/&gt
43  * &lt;/android.support.percent.PercentFrameLayout&gt
44  * </pre>
45  *
46  * The attributes that you can use are:
47  * <ul>
48  *     <li>{@code layout_widthPercent}
49  *     <li>{@code layout_heightPercent}
50  *     <li>{@code layout_marginPercent}
51  *     <li>{@code layout_marginLeftPercent}
52  *     <li>{@code layout_marginTopPercent}
53  *     <li>{@code layout_marginRightPercent}
54  *     <li>{@code layout_marginBottomPercent}
55  *     <li>{@code layout_marginStartPercent}
56  *     <li>{@code layout_marginEndPercent}
57  *     <li>{@code layout_aspectRatio}
58  * </ul>
59  *
60  * It is not necessary to specify {@code layout_width/height} if you specify {@code
61  * layout_widthPercent.} However, if you want the view to be able to take up more space than what
62  * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
63  * if the percentage size is too small for the View's content, it will be resized using
64  * {@code wrap_content} rule.
65  *
66  * <p>
67  * You can also make one dimension be a fraction of the other by setting only width or height and
68  * using {@code layout_aspectRatio} for the second one to be calculated automatically. For
69  * example, if you would like to achieve 16:9 aspect ratio, you can write:
70  * <pre class="prettyprint">
71  *     android:layout_width="300dp"
72  *     app:layout_aspectRatio="178%"
73  * </pre>
74  * This will make the aspect ratio 16:9 (1.78:1) with the width fixed at 300dp and height adjusted
75  * accordingly.
76  */
77 public class PercentRelativeLayout extends RelativeLayout {
78     private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
79 
PercentRelativeLayout(Context context)80     public PercentRelativeLayout(Context context) {
81         super(context);
82     }
83 
PercentRelativeLayout(Context context, AttributeSet attrs)84     public PercentRelativeLayout(Context context, AttributeSet attrs) {
85         super(context, attrs);
86     }
87 
PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle)88     public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
89         super(context, attrs, defStyle);
90     }
91 
92     @Override
generateDefaultLayoutParams()93     protected LayoutParams generateDefaultLayoutParams() {
94         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
95     }
96 
97     @Override
generateLayoutParams(AttributeSet attrs)98     public LayoutParams generateLayoutParams(AttributeSet attrs) {
99         return new LayoutParams(getContext(), attrs);
100     }
101 
102     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)103     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
104         mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
105         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
106         if (mHelper.handleMeasuredStateTooSmall()) {
107             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
108         }
109     }
110 
111     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)112     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
113         super.onLayout(changed, left, top, right, bottom);
114         mHelper.restoreOriginalParams();
115     }
116 
117     public static class LayoutParams extends RelativeLayout.LayoutParams
118             implements PercentLayoutHelper.PercentLayoutParams {
119         private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
120 
LayoutParams(Context c, AttributeSet attrs)121         public LayoutParams(Context c, AttributeSet attrs) {
122             super(c, attrs);
123             mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
124         }
125 
LayoutParams(int width, int height)126         public LayoutParams(int width, int height) {
127             super(width, height);
128         }
129 
LayoutParams(ViewGroup.LayoutParams source)130         public LayoutParams(ViewGroup.LayoutParams source) {
131             super(source);
132         }
133 
LayoutParams(MarginLayoutParams source)134         public LayoutParams(MarginLayoutParams source) {
135             super(source);
136         }
137 
138         @Override
getPercentLayoutInfo()139         public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
140             if (mPercentLayoutInfo == null) {
141                 mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo();
142             }
143 
144             return mPercentLayoutInfo;
145         }
146 
147         @Override
setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)148         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
149             PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
150         }
151     }
152 }
153