blob: 9ad4e1f98d2ba4a18ffd9def1a36457c5b71322d [file] [log] [blame]
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -07001/*
2 * Copyright (C) 2010 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 com.android.contacts.widget;
18
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070019import android.content.Context;
20import android.content.res.TypedArray;
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -080021import android.graphics.Rect;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070022import android.util.AttributeSet;
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -080023import android.view.Gravity;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070024import android.view.View;
25import android.view.ViewGroup;
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -080026import android.widget.LinearLayout;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070027
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070028import com.android.contacts.R;
29
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070030/**
31 * Layout similar to LinearLayout that allows a child to specify examples of
32 * desired size depending on the parent size. For example if the widget wants to
33 * be 100dip when parent is 200dip and 110dip when parent is 400dip, the layout
34 * will ensure these requirements and interpolate for other parent sizes.
35 * You can also specify minWidth for each child. You can have at most one
36 * child with layout_width="match_parent" - it will take the entire remaining
37 * space.
38 */
39public class InterpolatingLayout extends ViewGroup {
40
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -080041 private Rect mInRect = new Rect();
42 private Rect mOutRect = new Rect();
43
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070044 public InterpolatingLayout(Context context) {
45 super(context);
46 }
47
48 public InterpolatingLayout(Context context, AttributeSet attrs) {
49 super(context, attrs);
50 }
51
52 public InterpolatingLayout(Context context, AttributeSet attrs, int defStyle) {
53 super(context, attrs, defStyle);
54 }
55
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -080056 public final static class LayoutParams extends LinearLayout.LayoutParams {
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070057
58 public int narrowParentWidth;
59 public int narrowWidth;
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -080060 public int narrowMarginLeft;
61 public int narrowPaddingLeft;
62 public int narrowMarginRight;
63 public int narrowPaddingRight;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070064 public int wideParentWidth;
65 public int wideWidth;
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -080066 public int wideMarginLeft;
67 public int widePaddingLeft;
68 public int wideMarginRight;
69 public int widePaddingRight;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070070 private float widthMultiplier;
71 private int widthConstant;
72 private float leftMarginMultiplier;
73 private int leftMarginConstant;
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -080074 private float leftPaddingMultiplier;
75 private int leftPaddingConstant;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070076 private float rightMarginMultiplier;
77 private int rightMarginConstant;
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -080078 private float rightPaddingMultiplier;
79 private int rightPaddingConstant;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070080
81 public LayoutParams(Context c, AttributeSet attrs) {
82 super(c, attrs);
83 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.InterpolatingLayout_Layout);
84
85 narrowParentWidth = a.getDimensionPixelSize(
86 R.styleable.InterpolatingLayout_Layout_layout_narrowParentWidth, -1);
87 narrowWidth = a.getDimensionPixelSize(
88 R.styleable.InterpolatingLayout_Layout_layout_narrowWidth, -1);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -080089 narrowMarginLeft = a.getDimensionPixelSize(
90 R.styleable.InterpolatingLayout_Layout_layout_narrowMarginLeft, -1);
91 narrowPaddingLeft = a.getDimensionPixelSize(
92 R.styleable.InterpolatingLayout_Layout_layout_narrowPaddingLeft, -1);
93 narrowMarginRight = a.getDimensionPixelSize(
94 R.styleable.InterpolatingLayout_Layout_layout_narrowMarginRight, -1);
95 narrowPaddingRight = a.getDimensionPixelSize(
96 R.styleable.InterpolatingLayout_Layout_layout_narrowPaddingRight, -1);
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -070097 wideParentWidth = a.getDimensionPixelSize(
98 R.styleable.InterpolatingLayout_Layout_layout_wideParentWidth, -1);
99 wideWidth = a.getDimensionPixelSize(
100 R.styleable.InterpolatingLayout_Layout_layout_wideWidth, -1);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800101 wideMarginLeft = a.getDimensionPixelSize(
102 R.styleable.InterpolatingLayout_Layout_layout_wideMarginLeft, -1);
103 widePaddingLeft = a.getDimensionPixelSize(
104 R.styleable.InterpolatingLayout_Layout_layout_widePaddingLeft, -1);
105 wideMarginRight = a.getDimensionPixelSize(
106 R.styleable.InterpolatingLayout_Layout_layout_wideMarginRight, -1);
107 widePaddingRight = a.getDimensionPixelSize(
108 R.styleable.InterpolatingLayout_Layout_layout_widePaddingRight, -1);
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700109
110 a.recycle();
111
112 if (narrowWidth != -1) {
113 widthMultiplier = (float) (wideWidth - narrowWidth)
114 / (wideParentWidth - narrowParentWidth);
115 widthConstant = (int) (narrowWidth - narrowParentWidth * widthMultiplier);
116 }
117
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800118 if (narrowMarginLeft != -1) {
119 leftMarginMultiplier = (float) (wideMarginLeft - narrowMarginLeft)
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700120 / (wideParentWidth - narrowParentWidth);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800121 leftMarginConstant = (int) (narrowMarginLeft - narrowParentWidth
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700122 * leftMarginMultiplier);
123 }
124
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800125 if (narrowPaddingLeft != -1) {
126 leftPaddingMultiplier = (float) (widePaddingLeft - narrowPaddingLeft)
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800127 / (wideParentWidth - narrowParentWidth);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800128 leftPaddingConstant = (int) (narrowPaddingLeft - narrowParentWidth
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800129 * leftPaddingMultiplier);
130 }
131
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800132 if (narrowMarginRight != -1) {
133 rightMarginMultiplier = (float) (wideMarginRight - narrowMarginRight)
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700134 / (wideParentWidth - narrowParentWidth);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800135 rightMarginConstant = (int) (narrowMarginRight - narrowParentWidth
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700136 * rightMarginMultiplier);
137 }
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800138
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800139 if (narrowPaddingRight != -1) {
140 rightPaddingMultiplier = (float) (widePaddingRight - narrowPaddingRight)
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800141 / (wideParentWidth - narrowParentWidth);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800142 rightPaddingConstant = (int) (narrowPaddingRight - narrowParentWidth
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800143 * rightPaddingMultiplier);
144 }
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700145 }
146
147 public LayoutParams(int width, int height) {
148 super(width, height);
149 }
150
151 public LayoutParams(MarginLayoutParams source) {
152 super(source);
153 }
154
155 public int resolveWidth(int parentSize) {
156 if (narrowWidth == -1) {
157 return width;
158 } else {
159 int w = (int) (parentSize * widthMultiplier) + widthConstant;
160 return w <= 0 ? WRAP_CONTENT : w;
161 }
162 }
163
164 public int resolveLeftMargin(int parentSize) {
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800165 if (narrowMarginLeft == -1) {
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700166 return leftMargin;
167 } else {
168 int w = (int) (parentSize * leftMarginMultiplier) + leftMarginConstant;
169 return w < 0 ? 0 : w;
170 }
171 }
172
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800173 public int resolveLeftPadding(int parentSize) {
174 int w = (int) (parentSize * leftPaddingMultiplier) + leftPaddingConstant;
175 return w < 0 ? 0 : w;
176 }
177
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700178 public int resolveRightMargin(int parentSize) {
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800179 if (narrowMarginRight == -1) {
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700180 return rightMargin;
181 } else {
182 int w = (int) (parentSize * rightMarginMultiplier) + rightMarginConstant;
183 return w < 0 ? 0 : w;
184 }
185 }
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800186
187 public int resolveRightPadding(int parentSize) {
188 int w = (int) (parentSize * rightPaddingMultiplier) + rightPaddingConstant;
189 return w < 0 ? 0 : w;
190 }
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700191 }
192
193 @Override
194 public LayoutParams generateLayoutParams(AttributeSet attrs) {
195 return new LayoutParams(getContext(), attrs);
196 }
197
198 @Override
199 protected LayoutParams generateDefaultLayoutParams() {
200 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
201 }
202
203 @Override
204 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800205 int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
206 int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700207
208 int width = 0;
209 int height = 0;
210
211 View fillChild = null;
212 int count = getChildCount();
213 for (int i = 0; i < count; i++) {
214 View child = getChildAt(i);
215 if (child.getVisibility() == View.GONE) {
216 continue;
217 }
218
219 LayoutParams params = (LayoutParams) child.getLayoutParams();
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700220 if (params.width == LayoutParams.MATCH_PARENT) {
221 if (fillChild != null) {
222 throw new RuntimeException(
223 "Interpolating layout allows at most one child"
224 + " with layout_width='match_parent'");
225 }
226 fillChild = child;
227 } else {
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800228 int childWidth = params.resolveWidth(parentWidth);
229 int childWidthMeasureSpec;
230 switch (childWidth) {
231 case LayoutParams.WRAP_CONTENT:
232 childWidthMeasureSpec = MeasureSpec.UNSPECIFIED;
233 break;
234 default:
235 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
236 childWidth, MeasureSpec.EXACTLY);
237 break;
238 }
239
240 int childHeightMeasureSpec;
241 switch (params.height) {
242 case LayoutParams.WRAP_CONTENT:
243 childHeightMeasureSpec = MeasureSpec.UNSPECIFIED;
244 break;
245 case LayoutParams.MATCH_PARENT:
246 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
Dmitri Plotnikov65f8cae2010-12-10 16:38:22 -0800247 parentHeight - params.topMargin - params.bottomMargin,
248 MeasureSpec.EXACTLY);
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800249 break;
250 default:
251 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
252 params.height, MeasureSpec.EXACTLY);
253 break;
254 }
255
256 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700257 width += child.getMeasuredWidth();
258 height = Math.max(child.getMeasuredHeight(), height);
259 }
260
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800261 width += params.resolveLeftMargin(parentWidth) + params.resolveRightMargin(parentWidth);
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700262 }
263
264 if (fillChild != null) {
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800265 int remainder = parentWidth - width;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700266 int childMeasureSpec = remainder > 0
267 ? MeasureSpec.makeMeasureSpec(remainder, MeasureSpec.EXACTLY)
268 : MeasureSpec.UNSPECIFIED;
269 fillChild.measure(childMeasureSpec, heightMeasureSpec);
270 width += fillChild.getMeasuredWidth();
271 height = Math.max(fillChild.getMeasuredHeight(), height);
272 }
273
274 setMeasuredDimension(
275 resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
276 }
277
278 @Override
279 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
280 int offset = 0;
281 int width = right - left;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700282 int count = getChildCount();
283 for (int i = 0; i < count; i++) {
284 View child = getChildAt(i);
285
Frank Sposaro76dd1282011-06-24 11:09:04 -0700286 if (child.getVisibility() == View.GONE) {
287 continue;
288 }
289
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700290 LayoutParams params = (LayoutParams) child.getLayoutParams();
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800291 int gravity = params.gravity;
292 if (gravity == -1) {
Fabrice Di Meglio7d3b9152013-04-04 19:49:34 -0700293 gravity = Gravity.START | Gravity.TOP;
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800294 }
295
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800296 if (params.narrowPaddingLeft != -1 || params.narrowPaddingRight != -1) {
297 int leftPadding = params.narrowPaddingLeft == -1 ? child.getPaddingLeft()
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800298 : params.resolveLeftPadding(width);
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800299 int rightPadding = params.narrowPaddingRight == -1 ? child.getPaddingRight()
Dmitri Plotnikovbb0788a2010-11-29 18:50:14 -0800300 : params.resolveRightPadding(width);
301 child.setPadding(
302 leftPadding, child.getPaddingTop(), rightPadding, child.getPaddingBottom());
303 }
304
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800305 int leftMargin = params.resolveLeftMargin(width);
306 int rightMargin = params.resolveRightMargin(width);
307
308 mInRect.set(offset + leftMargin, params.topMargin,
Dmitri Plotnikovb18624f2011-01-21 19:10:43 -0800309 right - rightMargin, bottom - params.bottomMargin);
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800310
Dmitri Plotnikov75d6cae2011-01-11 11:00:17 -0800311 Gravity.apply(gravity, child.getMeasuredWidth(), child.getMeasuredHeight(),
Dmitri Plotnikov5c0935b2010-11-16 15:56:18 -0800312 mInRect, mOutRect);
313 child.layout(mOutRect.left, mOutRect.top, mOutRect.right, mOutRect.bottom);
314
315 offset = mOutRect.right + rightMargin;
Dmitri Plotnikov2ddba012010-09-16 18:27:35 -0700316 }
317 }
318}