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.util.AttributeSet;
22import android.view.ViewGroup;
23import 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 */
77public class PercentRelativeLayout extends RelativeLayout {
78    private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
79
80    public PercentRelativeLayout(Context context) {
81        super(context);
82    }
83
84    public PercentRelativeLayout(Context context, AttributeSet attrs) {
85        super(context, attrs);
86    }
87
88    public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
89        super(context, attrs, defStyle);
90    }
91
92    @Override
93    protected LayoutParams generateDefaultLayoutParams() {
94        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
95    }
96
97    @Override
98    public LayoutParams generateLayoutParams(AttributeSet attrs) {
99        return new LayoutParams(getContext(), attrs);
100    }
101
102    @Override
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
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
121        public LayoutParams(Context c, AttributeSet attrs) {
122            super(c, attrs);
123            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
124        }
125
126        public LayoutParams(int width, int height) {
127            super(width, height);
128        }
129
130        public LayoutParams(ViewGroup.LayoutParams source) {
131            super(source);
132        }
133
134        public LayoutParams(MarginLayoutParams source) {
135            super(source);
136        }
137
138        @Override
139        public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
140            if (mPercentLayoutInfo == null) {
141                mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo();
142            }
143
144            return mPercentLayoutInfo;
145        }
146
147        @Override
148        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
149            PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
150        }
151    }
152}
153