blob: 24139966a21238ebbdd74b5eea06c892e159a56b [file] [log] [blame]
Winson Chung321e9ee2010-08-09 13:37:56 -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.launcher2;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.Canvas;
22import android.graphics.Color;
23import android.graphics.Paint;
24import android.graphics.PorterDuff;
25import android.graphics.Rect;
26import android.util.AttributeSet;
27import android.util.Log;
28import android.view.MotionEvent;
29import android.view.View;
30import android.view.ViewDebug;
31import android.view.ViewGroup;
32
33/**
34 * An abstraction of the original CellLayout which supports laying out items
35 * which span multiple cells into a grid-like layout. Also supports dimming
36 * to give a preview of its contents.
37 */
38public class PagedViewCellLayout extends ViewGroup {
Winson Chung321e9ee2010-08-09 13:37:56 -070039 static final String TAG = "PagedViewCellLayout";
40
Winson Chungb3347bb2010-08-19 14:51:28 -070041 private float mHolographicAlpha;
Winson Chung321e9ee2010-08-09 13:37:56 -070042
Winson Chung80baf5a2010-08-09 16:03:15 -070043 private boolean mCenterContent;
44
Winson Chung321e9ee2010-08-09 13:37:56 -070045 private int mCellCountX;
46 private int mCellCountY;
47 private int mCellWidth;
48 private int mCellHeight;
49 private static int sDefaultCellDimensions = 96;
50
Winson Chung321e9ee2010-08-09 13:37:56 -070051 public PagedViewCellLayout(Context context) {
52 this(context, null);
53 }
54
55 public PagedViewCellLayout(Context context, AttributeSet attrs) {
56 this(context, attrs, 0);
57 }
58
59 public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) {
60 super(context, attrs, defStyle);
61
Winson Chung321e9ee2010-08-09 13:37:56 -070062 setAlwaysDrawnWithCacheEnabled(false);
63
64 // setup default cell parameters
65 mCellWidth = mCellHeight = sDefaultCellDimensions;
66 mCellCountX = LauncherModel.getCellCountX();
67 mCellCountY = LauncherModel.getCellCountY();
Winson Chungb3347bb2010-08-19 14:51:28 -070068 mHolographicAlpha = 0.0f;
Winson Chung321e9ee2010-08-09 13:37:56 -070069 }
70
71 @Override
Winson Chungb3347bb2010-08-19 14:51:28 -070072 protected boolean onSetAlpha(int alpha) {
73 return true;
74 }
Winson Chung321e9ee2010-08-09 13:37:56 -070075
Winson Chungb3347bb2010-08-19 14:51:28 -070076 @Override
77 public void setAlpha(float alpha) {
78 super.setAlpha(alpha);
79 setChildrenAlpha(alpha);
80 mHolographicAlpha = 1.0f - alpha;
Winson Chung321e9ee2010-08-09 13:37:56 -070081 }
82
83 @Override
84 public void cancelLongPress() {
85 super.cancelLongPress();
86
87 // Cancel long press for all children
88 final int count = getChildCount();
89 for (int i = 0; i < count; i++) {
90 final View child = getChildAt(i);
91 child.cancelLongPress();
92 }
93 }
94
95 public boolean addViewToCellLayout(View child, int index, int childId,
96 PagedViewCellLayout.LayoutParams params) {
97 final PagedViewCellLayout.LayoutParams lp = params;
98
99 // Generate an id for each view, this assumes we have at most 256x256 cells
100 // per workspace screen
101 if (lp.cellX >= 0 && lp.cellX <= (mCellCountX - 1) &&
102 lp.cellY >= 0 && (lp.cellY <= mCellCountY - 1)) {
103 // If the horizontal or vertical span is set to -1, it is taken to
104 // mean that it spans the extent of the CellLayout
105 if (lp.cellHSpan < 0) lp.cellHSpan = mCellCountX;
106 if (lp.cellVSpan < 0) lp.cellVSpan = mCellCountY;
107
108 child.setId(childId);
109
110 // We might be in the middle or end of shrinking/fading to a dimmed view
111 // Make sure this view's alpha is set the same as all the rest of the views
Winson Chungb3347bb2010-08-19 14:51:28 -0700112 child.setAlpha(1.0f - mHolographicAlpha);
Winson Chung321e9ee2010-08-09 13:37:56 -0700113
114 addView(child, index, lp);
Winson Chung321e9ee2010-08-09 13:37:56 -0700115 return true;
116 }
117 return false;
118 }
119
120 @Override
Winson Chung321e9ee2010-08-09 13:37:56 -0700121 public void requestChildFocus(View child, View focused) {
122 super.requestChildFocus(child, focused);
123 if (child != null) {
124 Rect r = new Rect();
125 child.getDrawingRect(r);
126 requestRectangleOnScreen(r);
127 }
128 }
129
130 @Override
131 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
132 // TODO: currently ignoring padding
133
134 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
135 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
136
137 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
138 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
139
140 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
141 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
142 }
143
144 final int cellWidth = mCellWidth;
145 final int cellHeight = mCellHeight;
146
147 int numWidthGaps = mCellCountX - 1;
148 int numHeightGaps = mCellCountY - 1;
149
150 int vSpaceLeft = heightSpecSize - mPaddingTop
151 - mPaddingBottom - (cellHeight * mCellCountY);
152 int heightGap = vSpaceLeft / numHeightGaps;
153
154 int hSpaceLeft = widthSpecSize - mPaddingLeft
155 - mPaddingRight - (cellWidth * mCellCountX);
156 int widthGap = hSpaceLeft / numWidthGaps;
157
158 // center it around the min gaps
159 int minGap = Math.min(widthGap, heightGap);
160 int paddingLeft = mPaddingLeft;
161 int paddingTop = mPaddingTop;
162 /*
163 if (minGap < heightGap) {
164 // vertical space has shrunken, so change padding accordingly
165 paddingTop += ((heightGap - minGap) * (mCellCountY - 1)) / 2;
166 } else if (minGap < widthGap) {
167 // horizontal space has shrunken, so change padding accordingly
168 paddingLeft += ((widthGap - minGap) * (mCellCountX - 1)) / 2;
169 }
170 */
171 widthGap = heightGap = minGap;
172
173 int newWidth = mPaddingLeft + mPaddingRight + (mCellCountX * cellWidth) +
174 ((mCellCountX - 1) * minGap);
175 int newHeight = mPaddingTop + mPaddingBottom + (mCellCountY * cellHeight) +
176 ((mCellCountY - 1) * minGap);
177
178 final int count = getChildCount();
179 for (int i = 0; i < count; i++) {
180 View child = getChildAt(i);
181 PagedViewCellLayout.LayoutParams lp =
182 (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
183 lp.setup(cellWidth, cellHeight, widthGap, heightGap,
184 paddingLeft, paddingTop);
185
186 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
187 MeasureSpec.EXACTLY);
188 int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
189 MeasureSpec.EXACTLY);
190
191 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
192 }
193
194 setMeasuredDimension(newWidth, newHeight);
195 }
196
197 @Override
198 protected void onLayout(boolean changed, int l, int t, int r, int b) {
199 int count = getChildCount();
200
Winson Chung80baf5a2010-08-09 16:03:15 -0700201 int offsetX = 0;
202 if (mCenterContent) {
203 // determine the max width of all the rows and center accordingly
204 int maxRowWidth = 0;
205 for (int i = 0; i < count; i++) {
206 View child = getChildAt(i);
207 if (child.getVisibility() != GONE) {
208 PagedViewCellLayout.LayoutParams lp =
209 (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
210 maxRowWidth = Math.max(maxRowWidth, lp.x + lp.width);
211 }
212 }
213 offsetX = (getMeasuredWidth() / 2) - (maxRowWidth / 2);
214 }
215
Winson Chung321e9ee2010-08-09 13:37:56 -0700216 for (int i = 0; i < count; i++) {
217 View child = getChildAt(i);
218 if (child.getVisibility() != GONE) {
219 PagedViewCellLayout.LayoutParams lp =
220 (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
221
Winson Chung80baf5a2010-08-09 16:03:15 -0700222 int childLeft = offsetX + lp.x;
Winson Chung321e9ee2010-08-09 13:37:56 -0700223 int childTop = lp.y;
224 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
225 }
226 }
227 }
228
229 @Override
Winson Chung321e9ee2010-08-09 13:37:56 -0700230 public boolean onTouchEvent(MotionEvent event) {
231 return super.onTouchEvent(event) || true;
232 }
233
Winson Chung80baf5a2010-08-09 16:03:15 -0700234 public void enableCenteredContent(boolean enabled) {
235 mCenterContent = enabled;
236 }
237
Winson Chung321e9ee2010-08-09 13:37:56 -0700238 @Override
239 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
240 final int count = getChildCount();
241 for (int i = 0; i < count; i++) {
242 final View view = getChildAt(i);
243 view.setDrawingCacheEnabled(enabled);
244 // Update the drawing caches
245 view.buildDrawingCache(true);
246 }
247 }
248
249 public void setCellCount(int xCount, int yCount) {
250 mCellCountX = xCount;
251 mCellCountY = yCount;
252 requestLayout();
253 }
254
Winson Chung321e9ee2010-08-09 13:37:56 -0700255 private void setChildrenAlpha(float alpha) {
Winson Chungb3347bb2010-08-19 14:51:28 -0700256 final int childCount = getChildCount();
257 for (int i = 0; i < childCount; i++) {
Winson Chung321e9ee2010-08-09 13:37:56 -0700258 getChildAt(i).setAlpha(alpha);
259 }
260 }
261
Winson Chung80baf5a2010-08-09 16:03:15 -0700262 public int[] getCellCountForDimensions(int width, int height) {
263 // Always assume we're working with the smallest span to make sure we
264 // reserve enough space in both orientations
265 int smallerSize = Math.min(mCellWidth, mCellHeight);
266
267 // Always round up to next largest cell
268 int spanX = (width + smallerSize) / smallerSize;
269 int spanY = (height + smallerSize) / smallerSize;
270
271 return new int[] { spanX, spanY };
272 }
273
Winson Chung321e9ee2010-08-09 13:37:56 -0700274 /**
275 * Start dragging the specified child
276 *
277 * @param child The child that is being dragged
278 */
279 void onDragChild(View child) {
280 PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
281 lp.isDragging = true;
282 }
283
Winson Chung80baf5a2010-08-09 16:03:15 -0700284 public int estimateCellHSpan(int width) {
285 return (width + mCellWidth) / mCellWidth;
286 }
287 public int estimateCellVSpan(int height) {
288 return (height + mCellHeight) / mCellHeight;
289 }
290 public int[] estimateCellDimensions(int approxWidth, int approxHeight,
291 int cellHSpan, int cellVSpan) {
292 // NOTE: we are disabling this until we find a good way to approximate this without fully
293 // measuring
294 /*
295 // we may want to use this before any measuring/layout happens, so we pass in an approximate
296 // size for the layout
297 int numWidthGaps = mCellCountX - 1;
298 int numHeightGaps = mCellCountY - 1;
299 int hSpaceLeft = approxWidth - mPaddingLeft
300 - mPaddingRight - (mCellWidth * mCellCountX);
301 int vSpaceLeft = approxHeight - mPaddingTop
302 - mPaddingBottom - (mCellHeight * mCellCountY);
303 int widthGap = hSpaceLeft / numWidthGaps;
304 int heightGap = vSpaceLeft / numHeightGaps;
305 int minGap = Math.min(widthGap, heightGap);
306 return new int[] {
307 (cellHSpan * mCellWidth) + ((cellHSpan - 1) * minGap),
308 (cellVSpan * mCellHeight) + ((cellVSpan - 1) * minGap)
309 };
310 */
311 return new int[] {
312 (cellHSpan * mCellWidth),
313 (cellVSpan * mCellHeight)
314 };
315 }
316
Winson Chung321e9ee2010-08-09 13:37:56 -0700317 @Override
318 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
319 return new PagedViewCellLayout.LayoutParams(getContext(), attrs);
320 }
321
322 @Override
323 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
324 return p instanceof PagedViewCellLayout.LayoutParams;
325 }
326
327 @Override
328 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
329 return new PagedViewCellLayout.LayoutParams(p);
330 }
331
332 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
333 /**
334 * Horizontal location of the item in the grid.
335 */
336 @ViewDebug.ExportedProperty
337 public int cellX;
338
339 /**
340 * Vertical location of the item in the grid.
341 */
342 @ViewDebug.ExportedProperty
343 public int cellY;
344
345 /**
346 * Number of cells spanned horizontally by the item.
347 */
348 @ViewDebug.ExportedProperty
349 public int cellHSpan;
350
351 /**
352 * Number of cells spanned vertically by the item.
353 */
354 @ViewDebug.ExportedProperty
355 public int cellVSpan;
356
357 /**
358 * Is this item currently being dragged
359 */
360 public boolean isDragging;
361
Winson Chung80baf5a2010-08-09 16:03:15 -0700362 // a data object that you can bind to this layout params
363 private Object mTag;
364
Winson Chung321e9ee2010-08-09 13:37:56 -0700365 // X coordinate of the view in the layout.
366 @ViewDebug.ExportedProperty
367 int x;
368 // Y coordinate of the view in the layout.
369 @ViewDebug.ExportedProperty
370 int y;
371
Winson Chung80baf5a2010-08-09 16:03:15 -0700372 public LayoutParams() {
373 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
374 cellHSpan = 1;
375 cellVSpan = 1;
376 }
377
Winson Chung321e9ee2010-08-09 13:37:56 -0700378 public LayoutParams(Context c, AttributeSet attrs) {
379 super(c, attrs);
380 cellHSpan = 1;
381 cellVSpan = 1;
382 }
383
384 public LayoutParams(ViewGroup.LayoutParams source) {
385 super(source);
386 cellHSpan = 1;
387 cellVSpan = 1;
388 }
389
390 public LayoutParams(LayoutParams source) {
391 super(source);
392 this.cellX = source.cellX;
393 this.cellY = source.cellY;
394 this.cellHSpan = source.cellHSpan;
395 this.cellVSpan = source.cellVSpan;
396 }
397
398 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
399 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
400 this.cellX = cellX;
401 this.cellY = cellY;
402 this.cellHSpan = cellHSpan;
403 this.cellVSpan = cellVSpan;
404 }
405
406 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
407 int hStartPadding, int vStartPadding) {
408
409 final int myCellHSpan = cellHSpan;
410 final int myCellVSpan = cellVSpan;
411 final int myCellX = cellX;
412 final int myCellY = cellY;
413
414 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
415 leftMargin - rightMargin;
416 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
417 topMargin - bottomMargin;
418
419 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
420 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
421 }
422
Winson Chung80baf5a2010-08-09 16:03:15 -0700423 public Object getTag() {
424 return mTag;
425 }
426
427 public void setTag(Object tag) {
428 mTag = tag;
429 }
430
Winson Chung321e9ee2010-08-09 13:37:56 -0700431 public String toString() {
Winson Chung80baf5a2010-08-09 16:03:15 -0700432 return "(" + this.cellX + ", " + this.cellY + ", " +
433 this.cellHSpan + ", " + this.cellVSpan + ")";
Winson Chung321e9ee2010-08-09 13:37:56 -0700434 }
435 }
436}