2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.launcher;
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.graphics.Rect;
22 import android.graphics.RectF;
23 import android.util.AttributeSet;
24 import android.view.ContextMenu;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.view.ViewDebug;
28 import android.view.ViewGroup;
30 import java.util.ArrayList;
32 public class CellLayout extends ViewGroup {
33 private boolean mPortrait;
35 private int mCellWidth;
36 private int mCellHeight;
38 private int mLongAxisStartPadding;
39 private int mLongAxisEndPadding;
41 private int mShortAxisStartPadding;
42 private int mShortAxisEndPadding;
44 private int mShortAxisCells;
45 private int mLongAxisCells;
47 private int mWidthGap;
48 private int mHeightGap;
50 private final Rect mRect = new Rect();
51 private final CellInfo mCellInfo = new CellInfo();
53 int[] mCellXY = new int[2];
55 boolean[][] mOccupied;
57 private RectF mDragRect = new RectF();
59 private boolean mDirtyTag;
61 public CellLayout(Context context) {
65 public CellLayout(Context context, AttributeSet attrs) {
66 this(context, attrs, 0);
69 public CellLayout(Context context, AttributeSet attrs, int defStyle) {
70 super(context, attrs, defStyle);
71 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
73 mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
74 mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
76 mLongAxisStartPadding =
77 a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
79 a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
80 mShortAxisStartPadding =
81 a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
82 mShortAxisEndPadding =
83 a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
85 mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
86 mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
90 setAlwaysDrawnWithCacheEnabled(false);
92 if (mOccupied == null) {
94 mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
96 mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
102 return mPortrait ? mShortAxisCells : mLongAxisCells;
106 return mPortrait ? mLongAxisCells : mShortAxisCells;
110 public void addView(View child, int index, ViewGroup.LayoutParams params) {
111 // Generate an id for each view, this assumes we have at most 256x256 cells
112 // per workspace screen
113 final LayoutParams cellParams = (LayoutParams) params;
114 child.setId(((getId() & 0xFF) << 16) |
115 (cellParams.cellX & 0xFF) << 8 | (cellParams.cellY & 0xFF));
117 super.addView(child, index, params);
121 public void requestChildFocus(View child, View focused) {
122 super.requestChildFocus(child, focused);
125 child.getDrawingRect(r);
126 requestRectangleOnScreen(r);
131 protected void onAttachedToWindow() {
132 super.onAttachedToWindow();
133 mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
137 public boolean onInterceptTouchEvent(MotionEvent ev) {
138 final int action = ev.getAction();
139 final CellInfo cellInfo = mCellInfo;
141 if (action == MotionEvent.ACTION_DOWN) {
142 final Rect frame = mRect;
143 final int x = (int) ev.getX() + mScrollX;
144 final int y = (int) ev.getY() + mScrollY;
145 final int count = getChildCount();
147 boolean found = false;
148 for (int i = count - 1; i >= 0; i--) {
149 final View child = getChildAt(i);
151 if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
152 child.getHitRect(frame);
153 if (frame.contains(x, y)) {
154 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
155 cellInfo.cell = child;
156 cellInfo.cellX = lp.cellX;
157 cellInfo.cellY = lp.cellY;
158 cellInfo.spanX = lp.cellHSpan;
159 cellInfo.spanY = lp.cellVSpan;
160 cellInfo.valid = true;
169 int cellXY[] = mCellXY;
170 pointToCellExact(x, y, cellXY);
172 final boolean portrait = mPortrait;
173 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
174 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
176 final boolean[][] occupied = mOccupied;
177 findOccupiedCells(xCount, yCount, occupied);
179 cellInfo.cell = null;
180 cellInfo.cellX = cellXY[0];
181 cellInfo.cellY = cellXY[1];
184 cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
185 cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
187 // Instead of finding the interesting vacant cells here, wait until a
188 // caller invokes getTag() to retrieve the result. Finding the vacant
189 // cells is a bit expensive and can generate many new objects, it's
190 // therefore better to defer it until we know we actually need it.
195 } else if (action == MotionEvent.ACTION_UP) {
196 cellInfo.cell = null;
201 cellInfo.valid = false;
210 public CellInfo getTag() {
211 final CellInfo info = (CellInfo) super.getTag();
212 if (mDirtyTag && info.valid) {
213 final boolean portrait = mPortrait;
214 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
215 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
217 final boolean[][] occupied = mOccupied;
218 findOccupiedCells(xCount, yCount, occupied);
220 findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
227 private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
228 int xCount, int yCount, boolean[][] occupied) {
230 cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
231 cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
232 cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
233 cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
234 cellInfo.clearVacantCells();
236 if (occupied[x][y]) {
240 cellInfo.current.set(x, y, x, y);
242 findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
245 private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
248 addVacantCell(current, cellInfo);
250 if (current.left > 0) {
251 if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
253 findVacantCell(current, xCount, yCount, occupied, cellInfo);
258 if (current.right < xCount - 1) {
259 if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
261 findVacantCell(current, xCount, yCount, occupied, cellInfo);
266 if (current.top > 0) {
267 if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
269 findVacantCell(current, xCount, yCount, occupied, cellInfo);
274 if (current.bottom < yCount - 1) {
275 if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
277 findVacantCell(current, xCount, yCount, occupied, cellInfo);
283 private static void addVacantCell(Rect current, CellInfo cellInfo) {
284 CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
285 cell.cellX = current.left;
286 cell.cellY = current.top;
287 cell.spanX = current.right - current.left + 1;
288 cell.spanY = current.bottom - current.top + 1;
289 if (cell.spanX > cellInfo.maxVacantSpanX) {
290 cellInfo.maxVacantSpanX = cell.spanX;
291 cellInfo.maxVacantSpanXSpanY = cell.spanY;
293 if (cell.spanY > cellInfo.maxVacantSpanY) {
294 cellInfo.maxVacantSpanY = cell.spanY;
295 cellInfo.maxVacantSpanYSpanX = cell.spanX;
297 cellInfo.vacantCells.add(cell);
300 private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
301 for (int y = top; y <= bottom; y++) {
302 if (occupied[x][y]) {
309 private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
310 for (int x = left; x <= right; x++) {
311 if (occupied[x][y]) {
318 CellInfo findAllVacantCells(boolean[] occupiedCells) {
319 final boolean portrait = mPortrait;
320 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
321 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
323 boolean[][] occupied = mOccupied;
325 if (occupiedCells != null) {
326 for (int y = 0; y < yCount; y++) {
327 for (int x = 0; x < xCount; x++) {
328 occupied[x][y] = occupiedCells[y * xCount + x];
332 findOccupiedCells(xCount, yCount, occupied);
335 CellInfo cellInfo = new CellInfo();
341 cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
342 cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
343 cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
344 cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
345 cellInfo.screen = mCellInfo.screen;
347 Rect current = cellInfo.current;
349 for (int x = 0; x < xCount; x++) {
350 for (int y = 0; y < yCount; y++) {
351 if (!occupied[x][y]) {
352 current.set(x, y, x, y);
353 findVacantCell(current, xCount, yCount, occupied, cellInfo);
354 occupied[x][y] = true;
359 cellInfo.valid = cellInfo.vacantCells.size() > 0;
361 // Assume the caller will perform their own cell searching, otherwise we
362 // risk causing an unnecessary rebuild after findCellForSpan()
368 * Given a point, return the cell that strictly encloses that point
369 * @param x X coordinate of the point
370 * @param y Y coordinate of the point
371 * @param result Array of 2 ints to hold the x and y coordinate of the cell
373 void pointToCellExact(int x, int y, int[] result) {
374 final boolean portrait = mPortrait;
376 final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
377 final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
379 result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
380 result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
382 final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
383 final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
385 if (result[0] < 0) result[0] = 0;
386 if (result[0] >= xAxis) result[0] = xAxis - 1;
387 if (result[1] < 0) result[1] = 0;
388 if (result[1] >= yAxis) result[1] = yAxis - 1;
392 * Given a point, return the cell that most closely encloses that point
393 * @param x X coordinate of the point
394 * @param y Y coordinate of the point
395 * @param result Array of 2 ints to hold the x and y coordinate of the cell
397 void pointToCellRounded(int x, int y, int[] result) {
398 pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
402 * Given a cell coordinate, return the point that represents the upper left corner of that cell
404 * @param cellX X coordinate of the cell
405 * @param cellY Y coordinate of the cell
407 * @param result Array of 2 ints to hold the x and y coordinate of the point
409 void cellToPoint(int cellX, int cellY, int[] result) {
410 final boolean portrait = mPortrait;
412 final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
413 final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
416 result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
417 result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
421 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
422 // TODO: currently ignoring padding
424 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
425 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
427 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
428 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
430 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
431 throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
434 final int shortAxisCells = mShortAxisCells;
435 final int longAxisCells = mLongAxisCells;
436 final int longAxisStartPadding = mLongAxisStartPadding;
437 final int longAxisEndPadding = mLongAxisEndPadding;
438 final int shortAxisStartPadding = mShortAxisStartPadding;
439 final int shortAxisEndPadding = mShortAxisEndPadding;
440 final int cellWidth = mCellWidth;
441 final int cellHeight = mCellHeight;
443 mPortrait = heightSpecSize > widthSpecSize;
445 int numShortGaps = shortAxisCells - 1;
446 int numLongGaps = longAxisCells - 1;
449 int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding
450 - (cellHeight * longAxisCells);
451 mHeightGap = vSpaceLeft / numLongGaps;
453 int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding
454 - (cellWidth * shortAxisCells);
455 if (numShortGaps > 0) {
456 mWidthGap = hSpaceLeft / numShortGaps;
461 int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding
462 - (cellWidth * longAxisCells);
463 mWidthGap = hSpaceLeft / numLongGaps;
465 int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding
466 - (cellHeight * shortAxisCells);
467 if (numShortGaps > 0) {
468 mHeightGap = vSpaceLeft / numShortGaps;
474 int count = getChildCount();
476 for (int i = 0; i < count; i++) {
477 View child = getChildAt(i);
478 LayoutParams lp = (LayoutParams) child.getLayoutParams();
481 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding,
482 longAxisStartPadding);
484 lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding,
485 shortAxisStartPadding);
488 int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
489 int childheightMeasureSpec =
490 MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
491 child.measure(childWidthMeasureSpec, childheightMeasureSpec);
494 setMeasuredDimension(widthSpecSize, heightSpecSize);
498 protected void onLayout(boolean changed, int l, int t, int r, int b) {
499 int count = getChildCount();
501 for (int i = 0; i < count; i++) {
502 View child = getChildAt(i);
503 if (child.getVisibility() != GONE) {
505 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
507 int childLeft = lp.x;
509 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
515 protected void setChildrenDrawingCacheEnabled(boolean enabled) {
516 final int count = getChildCount();
517 for (int i = 0; i < count; i++) {
518 final View view = getChildAt(i);
519 view.setDrawingCacheEnabled(enabled);
520 // Update the drawing caches
521 view.buildDrawingCache();
526 protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
527 super.setChildrenDrawnWithCacheEnabled(enabled);
530 boolean acceptChildDrop(int x, int y, int cellHSpan, int cellVSpan, View cell) {
531 int[] cellXY = mCellXY;
532 pointToCellRounded(x, y, cellXY);
533 int cellX = cellXY[0];
534 int cellY = cellXY[1];
536 return findCell(cellX, cellY, cellHSpan, cellVSpan, cell) == null;
540 * Finds the first View intersecting with the specified cell. If the cell is outside
541 * of the layout, this is returned.
543 * @param cellX The X location of the cell to test.
544 * @param cellY The Y location of the cell to test.
545 * @param cellHSpan The horizontal span of the cell to test.
546 * @param cellVSpan The vertical span of the cell to test.
547 * @param ignoreCell View to ignore during the test.
549 * @return Returns the first View intersecting with the specified cell, this if the cell
550 * lies outside of this layout's grid or null if no View was found.
552 View findCell(int cellX, int cellY, int cellHSpan, int cellVSpan, View ignoreCell) {
553 if (cellX < 0 || cellX + cellHSpan > (mPortrait ? mShortAxisCells : mLongAxisCells) ||
554 cellY < 0 || cellY + cellVSpan > (mPortrait ? mLongAxisCells : mShortAxisCells)) {
558 final int count = getChildCount();
559 for (int i = 0; i < count; i++) {
560 final View view = getChildAt(i);
561 if (view == ignoreCell) {
565 final LayoutParams lp = (LayoutParams) view.getLayoutParams();
566 if (cellX < lp.cellX + lp.cellHSpan && lp.cellX < cellX + cellHSpan &&
567 cellY < lp.cellY + lp.cellVSpan && lp.cellY < cellY + cellVSpan) {
576 * Drop a child at the specified position
578 * @param child The child that is being dropped
579 * @param cellX The child's new x location
580 * @param cellY The child's new y location
582 void onDropChild(View child, int cellX, int cellY) {
583 int[] cellXY = mCellXY;
584 pointToCellRounded(cellX, cellY, cellXY);
585 LayoutParams lp = (LayoutParams) child.getLayoutParams();
586 lp.cellX = cellXY[0];
587 lp.cellY = cellXY[1];
588 lp.isDragging = false;
589 mDragRect.setEmpty();
590 child.requestLayout();
594 void onDropAborted(View child) {
596 ((LayoutParams) child.getLayoutParams()).isDragging = false;
599 mDragRect.setEmpty();
603 * Start dragging the specified child
605 * @param child The child that is being dragged
607 void onDragChild(View child) {
608 LayoutParams lp = (LayoutParams) child.getLayoutParams();
609 lp.isDragging = true;
610 mDragRect.setEmpty();
614 * Drag a child over the specified position
616 * @param child The child that is being dropped
617 * @param cellX The child's new x cell location
618 * @param cellY The child's new y cell location
620 void onDragOverChild(View child, int cellX, int cellY) {
621 int[] cellXY = mCellXY;
622 pointToCellRounded(cellX, cellY, cellXY);
623 LayoutParams lp = (LayoutParams) child.getLayoutParams();
624 cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
629 * Computes a bounding rectangle for a range of cells
631 * @param cellX X coordinate of upper left corner expressed as a cell position
632 * @param cellY Y coordinate of upper left corner expressed as a cell position
633 * @param cellHSpan Width in cells
634 * @param cellVSpan Height in cells
635 * @param dragRect Rectnagle into which to put the results
637 public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) {
638 final boolean portrait = mPortrait;
639 final int cellWidth = mCellWidth;
640 final int cellHeight = mCellHeight;
641 final int widthGap = mWidthGap;
642 final int heightGap = mHeightGap;
644 final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
645 final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
647 int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
648 int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
650 int x = hStartPadding + cellX * (cellWidth + widthGap);
651 int y = vStartPadding + cellY * (cellHeight + heightGap);
653 dragRect.set(x, y, x + width, y + height);
657 * Computes the required horizontal and vertical cell spans to always
658 * fit the given rectangle.
660 * @param width Width in pixels
661 * @param height Height in pixels
663 public int[] rectToCell(int width, int height) {
664 // Always assume we're working with the smallest span to make sure we
665 // reserve enough space in both orientations.
666 int actualWidth = mCellWidth + mWidthGap;
667 int actualHeight = mCellHeight + mHeightGap;
668 int smallerSize = Math.min(actualWidth, actualHeight);
670 // Always round up to next largest cell
671 int spanX = (width + smallerSize) / smallerSize;
672 int spanY = (height + smallerSize) / smallerSize;
673 return new int[] { spanX, spanY };
677 * Find the first vacant cell, if there is one.
679 * @param vacant Holds the x and y coordinate of the vacant cell
680 * @param spanX Horizontal cell span.
681 * @param spanY Vertical cell span.
683 * @return True if a vacant cell was found
685 public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
686 final boolean portrait = mPortrait;
687 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
688 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
689 final boolean[][] occupied = mOccupied;
691 findOccupiedCells(xCount, yCount, occupied);
693 return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
696 static boolean findVacantCell(int[] vacant, int spanX, int spanY,
697 int xCount, int yCount, boolean[][] occupied) {
699 for (int x = 0; x < xCount; x++) {
700 for (int y = 0; y < yCount; y++) {
701 boolean available = !occupied[x][y];
702 out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
703 for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
704 available = available && !occupied[i][j];
705 if (!available) break out;
720 boolean[] getOccupiedCells() {
721 final boolean portrait = mPortrait;
722 final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
723 final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
724 final boolean[][] occupied = mOccupied;
726 findOccupiedCells(xCount, yCount, occupied);
728 final boolean[] flat = new boolean[xCount * yCount];
729 for (int y = 0; y < yCount; y++) {
730 for (int x = 0; x < xCount; x++) {
731 flat[y * xCount + x] = occupied[x][y];
738 private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied) {
739 for (int x = 0; x < xCount; x++) {
740 for (int y = 0; y < yCount; y++) {
741 occupied[x][y] = false;
745 int count = getChildCount();
746 for (int i = 0; i < count; i++) {
747 View child = getChildAt(i);
748 if (child instanceof Folder) {
751 LayoutParams lp = (LayoutParams) child.getLayoutParams();
753 for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
754 for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
755 occupied[x][y] = true;
762 public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
763 return new CellLayout.LayoutParams(getContext(), attrs);
767 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
768 return p instanceof CellLayout.LayoutParams;
772 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
773 return new CellLayout.LayoutParams(p);
776 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
778 * Horizontal location of the item in the grid.
780 @ViewDebug.ExportedProperty
784 * Vertical location of the item in the grid.
786 @ViewDebug.ExportedProperty
790 * Number of cells spanned horizontally by the item.
792 @ViewDebug.ExportedProperty
793 public int cellHSpan;
796 * Number of cells spanned vertically by the item.
798 @ViewDebug.ExportedProperty
799 public int cellVSpan;
802 * Is this item currently being dragged
804 public boolean isDragging;
806 // X coordinate of the view in the layout.
807 @ViewDebug.ExportedProperty
809 // Y coordinate of the view in the layout.
810 @ViewDebug.ExportedProperty
813 public LayoutParams(Context c, AttributeSet attrs) {
819 public LayoutParams(ViewGroup.LayoutParams source) {
825 public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
826 super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
829 this.cellHSpan = cellHSpan;
830 this.cellVSpan = cellVSpan;
833 public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
834 int hStartPadding, int vStartPadding) {
836 final int myCellHSpan = cellHSpan;
837 final int myCellVSpan = cellVSpan;
838 final int myCellX = cellX;
839 final int myCellY = cellY;
841 width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
842 leftMargin - rightMargin;
843 height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
844 topMargin - bottomMargin;
846 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
847 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
851 static final class CellInfo implements ContextMenu.ContextMenuInfo {
853 * See View.AttachInfo.InvalidateInfo for futher explanations about
854 * the recycling mechanism. In this case, we recycle the vacant cells
855 * instances because up to several hundreds can be instanciated when
856 * the user long presses an empty cell.
858 static final class VacantCell {
864 // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
865 // like a reasonable compromise given the size of a VacantCell and
866 // the fact that the user is not likely to touch an empty 4x4 grid
868 private static final int POOL_LIMIT = 100;
869 private static final Object sLock = new Object();
871 private static int sAcquiredCount = 0;
872 private static VacantCell sRoot;
874 private VacantCell next;
876 static VacantCell acquire() {
877 synchronized (sLock) {
879 return new VacantCell();
882 VacantCell info = sRoot;
891 synchronized (sLock) {
892 if (sAcquiredCount < POOL_LIMIT) {
901 public String toString() {
902 return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
903 ", spanY=" + spanY + "]";
915 final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
917 int maxVacantSpanXSpanY;
919 int maxVacantSpanYSpanX;
920 final Rect current = new Rect();
922 private void clearVacantCells() {
923 final ArrayList<VacantCell> list = vacantCells;
924 final int count = list.size();
926 for (int i = 0; i < count; i++) list.get(i).release();
931 void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
932 if (cellX < 0 || cellY < 0) {
933 maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
934 maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
939 final boolean[][] unflattened = new boolean[xCount][yCount];
940 for (int y = 0; y < yCount; y++) {
941 for (int x = 0; x < xCount; x++) {
942 unflattened[x][y] = occupied[y * xCount + x];
945 CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
949 * This method can be called only once! Calling #findVacantCellsFromOccupied will
950 * restore the ability to call this method.
952 * Finds the upper-left coordinate of the first rectangle in the grid that can
953 * hold a cell of the specified dimensions.
955 * @param cellXY The array that will contain the position of a vacant cell if such a cell
957 * @param spanX The horizontal span of the cell we want to find.
958 * @param spanY The vertical span of the cell we want to find.
960 * @return True if a vacant cell of the specified dimension was found, false otherwise.
962 boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
963 final ArrayList<VacantCell> list = vacantCells;
964 final int count = list.size();
966 boolean found = false;
968 if (this.spanX >= spanX && this.spanY >= spanY) {
974 // Look for an exact match first
975 for (int i = 0; i < count; i++) {
976 VacantCell cell = list.get(i);
977 if (cell.spanX == spanX && cell.spanY == spanY) {
978 cellXY[0] = cell.cellX;
979 cellXY[1] = cell.cellY;
985 // Look for the first cell large enough
986 for (int i = 0; i < count; i++) {
987 VacantCell cell = list.get(i);
988 if (cell.spanX >= spanX && cell.spanY >= spanY) {
989 cellXY[0] = cell.cellX;
990 cellXY[1] = cell.cellY;
1002 public String toString() {
1003 return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX +
1004 ", y=" + cellY + "]";