CellLayoutpublic class CellLayout extends android.view.ViewGroup
Fields Summary |
---|
private boolean | mPortrait | private int | mCellWidth | private int | mCellHeight | private int | mLongAxisStartPadding | private int | mLongAxisEndPadding | private int | mShortAxisStartPadding | private int | mShortAxisEndPadding | private int | mShortAxisCells | private int | mLongAxisCells | private int | mWidthGap | private int | mHeightGap | private final android.graphics.Rect | mRect | private final CellInfo | mCellInfo | int[] | mCellXY | boolean[] | mOccupied | private android.graphics.RectF | mDragRect | private boolean | mDirtyTag |
Constructors Summary |
---|
public CellLayout(android.content.Context context)
this(context, null);
| public CellLayout(android.content.Context context, android.util.AttributeSet attrs)
this(context, attrs, 0);
| public CellLayout(android.content.Context context, android.util.AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
mLongAxisStartPadding =
a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
mLongAxisEndPadding =
a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
mShortAxisStartPadding =
a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
mShortAxisEndPadding =
a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
a.recycle();
setAlwaysDrawnWithCacheEnabled(false);
if (mOccupied == null) {
if (mPortrait) {
mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
} else {
mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
}
}
|
Methods Summary |
---|
boolean | acceptChildDrop(int x, int y, int cellHSpan, int cellVSpan, android.view.View cell)
int[] cellXY = mCellXY;
pointToCellRounded(x, y, cellXY);
int cellX = cellXY[0];
int cellY = cellXY[1];
return findCell(cellX, cellY, cellHSpan, cellVSpan, cell) == null;
| private static void | addVacantCell(android.graphics.Rect current, com.android.launcher.CellLayout$CellInfo cellInfo)
CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
cell.cellX = current.left;
cell.cellY = current.top;
cell.spanX = current.right - current.left + 1;
cell.spanY = current.bottom - current.top + 1;
if (cell.spanX > cellInfo.maxVacantSpanX) {
cellInfo.maxVacantSpanX = cell.spanX;
cellInfo.maxVacantSpanXSpanY = cell.spanY;
}
if (cell.spanY > cellInfo.maxVacantSpanY) {
cellInfo.maxVacantSpanY = cell.spanY;
cellInfo.maxVacantSpanYSpanX = cell.spanX;
}
cellInfo.vacantCells.add(cell);
| public void | addView(android.view.View child, int index, ViewGroup.LayoutParams params)
// Generate an id for each view, this assumes we have at most 256x256 cells
// per workspace screen
final LayoutParams cellParams = (LayoutParams) params;
child.setId(((getId() & 0xFF) << 16) |
(cellParams.cellX & 0xFF) << 8 | (cellParams.cellY & 0xFF));
super.addView(child, index, params);
| void | cellToPoint(int cellX, int cellY, int[] result)Given a cell coordinate, return the point that represents the upper left corner of that cell
final boolean portrait = mPortrait;
final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
| public void | cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, android.graphics.RectF dragRect)Computes a bounding rectangle for a range of cells
final boolean portrait = mPortrait;
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
final int widthGap = mWidthGap;
final int heightGap = mHeightGap;
final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
int x = hStartPadding + cellX * (cellWidth + widthGap);
int y = vStartPadding + cellY * (cellHeight + heightGap);
dragRect.set(x, y, x + width, y + height);
| protected boolean | checkLayoutParams(ViewGroup.LayoutParams p)
return p instanceof CellLayout.LayoutParams;
| com.android.launcher.CellLayout$CellInfo | findAllVacantCells(boolean[] occupiedCells)
final boolean portrait = mPortrait;
final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
boolean[][] occupied = mOccupied;
if (occupiedCells != null) {
for (int y = 0; y < yCount; y++) {
for (int x = 0; x < xCount; x++) {
occupied[x][y] = occupiedCells[y * xCount + x];
}
}
} else {
findOccupiedCells(xCount, yCount, occupied);
}
CellInfo cellInfo = new CellInfo();
cellInfo.cellX = -1;
cellInfo.cellY = -1;
cellInfo.spanY = 0;
cellInfo.spanX = 0;
cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
cellInfo.screen = mCellInfo.screen;
Rect current = cellInfo.current;
for (int x = 0; x < xCount; x++) {
for (int y = 0; y < yCount; y++) {
if (!occupied[x][y]) {
current.set(x, y, x, y);
findVacantCell(current, xCount, yCount, occupied, cellInfo);
occupied[x][y] = true;
}
}
}
cellInfo.valid = cellInfo.vacantCells.size() > 0;
// Assume the caller will perform their own cell searching, otherwise we
// risk causing an unnecessary rebuild after findCellForSpan()
return cellInfo;
| android.view.View | findCell(int cellX, int cellY, int cellHSpan, int cellVSpan, android.view.View ignoreCell)Finds the first View intersecting with the specified cell. If the cell is outside
of the layout, this is returned.
if (cellX < 0 || cellX + cellHSpan > (mPortrait ? mShortAxisCells : mLongAxisCells) ||
cellY < 0 || cellY + cellVSpan > (mPortrait ? mLongAxisCells : mShortAxisCells)) {
return this;
}
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View view = getChildAt(i);
if (view == ignoreCell) {
continue;
}
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
if (cellX < lp.cellX + lp.cellHSpan && lp.cellX < cellX + cellHSpan &&
cellY < lp.cellY + lp.cellVSpan && lp.cellY < cellY + cellVSpan) {
return view;
}
}
return null;
| private static void | findIntersectingVacantCells(com.android.launcher.CellLayout$CellInfo cellInfo, int x, int y, int xCount, int yCount, boolean[][] occupied)
cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
cellInfo.clearVacantCells();
if (occupied[x][y]) {
return;
}
cellInfo.current.set(x, y, x, y);
findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
| private void | findOccupiedCells(int xCount, int yCount, boolean[][] occupied)
for (int x = 0; x < xCount; x++) {
for (int y = 0; y < yCount; y++) {
occupied[x][y] = false;
}
}
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child instanceof Folder) {
continue;
}
LayoutParams lp = (LayoutParams) child.getLayoutParams();
for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
occupied[x][y] = true;
}
}
}
| private static void | findVacantCell(android.graphics.Rect current, int xCount, int yCount, boolean[][] occupied, com.android.launcher.CellLayout$CellInfo cellInfo)
addVacantCell(current, cellInfo);
if (current.left > 0) {
if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
current.left--;
findVacantCell(current, xCount, yCount, occupied, cellInfo);
current.left++;
}
}
if (current.right < xCount - 1) {
if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
current.right++;
findVacantCell(current, xCount, yCount, occupied, cellInfo);
current.right--;
}
}
if (current.top > 0) {
if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
current.top--;
findVacantCell(current, xCount, yCount, occupied, cellInfo);
current.top++;
}
}
if (current.bottom < yCount - 1) {
if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
current.bottom++;
findVacantCell(current, xCount, yCount, occupied, cellInfo);
current.bottom--;
}
}
| static boolean | findVacantCell(int[] vacant, int spanX, int spanY, int xCount, int yCount, boolean[][] occupied)
for (int x = 0; x < xCount; x++) {
for (int y = 0; y < yCount; y++) {
boolean available = !occupied[x][y];
out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
available = available && !occupied[i][j];
if (!available) break out;
}
}
if (available) {
vacant[0] = x;
vacant[1] = y;
return true;
}
}
}
return false;
| public ViewGroup.LayoutParams | generateLayoutParams(android.util.AttributeSet attrs)
return new CellLayout.LayoutParams(getContext(), attrs);
| protected ViewGroup.LayoutParams | generateLayoutParams(ViewGroup.LayoutParams p)
return new CellLayout.LayoutParams(p);
| int | getCountX()
return mPortrait ? mShortAxisCells : mLongAxisCells;
| int | getCountY()
return mPortrait ? mLongAxisCells : mShortAxisCells;
| boolean[] | getOccupiedCells()
final boolean portrait = mPortrait;
final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
findOccupiedCells(xCount, yCount, occupied);
final boolean[] flat = new boolean[xCount * yCount];
for (int y = 0; y < yCount; y++) {
for (int x = 0; x < xCount; x++) {
flat[y * xCount + x] = occupied[x][y];
}
}
return flat;
| public com.android.launcher.CellLayout$CellInfo | getTag()
final CellInfo info = (CellInfo) super.getTag();
if (mDirtyTag && info.valid) {
final boolean portrait = mPortrait;
final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
findOccupiedCells(xCount, yCount, occupied);
findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
mDirtyTag = false;
}
return info;
| public boolean | getVacantCell(int[] vacant, int spanX, int spanY)Find the first vacant cell, if there is one.
final boolean portrait = mPortrait;
final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
findOccupiedCells(xCount, yCount, occupied);
return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
| private static boolean | isColumnEmpty(int x, int top, int bottom, boolean[][] occupied)
for (int y = top; y <= bottom; y++) {
if (occupied[x][y]) {
return false;
}
}
return true;
| private static boolean | isRowEmpty(int y, int left, int right, boolean[][] occupied)
for (int x = left; x <= right; x++) {
if (occupied[x][y]) {
return false;
}
}
return true;
| protected void | onAttachedToWindow()
super.onAttachedToWindow();
mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
| void | onDragChild(android.view.View child)Start dragging the specified child
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.isDragging = true;
mDragRect.setEmpty();
| void | onDragOverChild(android.view.View child, int cellX, int cellY)Drag a child over the specified position
int[] cellXY = mCellXY;
pointToCellRounded(cellX, cellY, cellXY);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
invalidate();
| void | onDropAborted(android.view.View child)
if (child != null) {
((LayoutParams) child.getLayoutParams()).isDragging = false;
invalidate();
}
mDragRect.setEmpty();
| void | onDropChild(android.view.View child, int cellX, int cellY)Drop a child at the specified position
int[] cellXY = mCellXY;
pointToCellRounded(cellX, cellY, cellXY);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.cellX = cellXY[0];
lp.cellY = cellXY[1];
lp.isDragging = false;
mDragRect.setEmpty();
child.requestLayout();
invalidate();
| public boolean | onInterceptTouchEvent(android.view.MotionEvent ev)
final int action = ev.getAction();
final CellInfo cellInfo = mCellInfo;
if (action == MotionEvent.ACTION_DOWN) {
final Rect frame = mRect;
final int x = (int) ev.getX() + mScrollX;
final int y = (int) ev.getY() + mScrollY;
final int count = getChildCount();
boolean found = false;
for (int i = count - 1; i >= 0; i--) {
final View child = getChildAt(i);
if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(x, y)) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
cellInfo.cell = child;
cellInfo.cellX = lp.cellX;
cellInfo.cellY = lp.cellY;
cellInfo.spanX = lp.cellHSpan;
cellInfo.spanY = lp.cellVSpan;
cellInfo.valid = true;
found = true;
mDirtyTag = false;
break;
}
}
}
if (!found) {
int cellXY[] = mCellXY;
pointToCellExact(x, y, cellXY);
final boolean portrait = mPortrait;
final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
final boolean[][] occupied = mOccupied;
findOccupiedCells(xCount, yCount, occupied);
cellInfo.cell = null;
cellInfo.cellX = cellXY[0];
cellInfo.cellY = cellXY[1];
cellInfo.spanX = 1;
cellInfo.spanY = 1;
cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
// Instead of finding the interesting vacant cells here, wait until a
// caller invokes getTag() to retrieve the result. Finding the vacant
// cells is a bit expensive and can generate many new objects, it's
// therefore better to defer it until we know we actually need it.
mDirtyTag = true;
}
setTag(cellInfo);
} else if (action == MotionEvent.ACTION_UP) {
cellInfo.cell = null;
cellInfo.cellX = -1;
cellInfo.cellY = -1;
cellInfo.spanX = 0;
cellInfo.spanY = 0;
cellInfo.valid = false;
mDirtyTag = false;
setTag(cellInfo);
}
return false;
| protected void | onLayout(boolean changed, int l, int t, int r, int b)
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
int childLeft = lp.x;
int childTop = lp.y;
child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
}
}
| protected void | onMeasure(int widthMeasureSpec, int heightMeasureSpec)
// TODO: currently ignoring padding
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
}
final int shortAxisCells = mShortAxisCells;
final int longAxisCells = mLongAxisCells;
final int longAxisStartPadding = mLongAxisStartPadding;
final int longAxisEndPadding = mLongAxisEndPadding;
final int shortAxisStartPadding = mShortAxisStartPadding;
final int shortAxisEndPadding = mShortAxisEndPadding;
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
mPortrait = heightSpecSize > widthSpecSize;
int numShortGaps = shortAxisCells - 1;
int numLongGaps = longAxisCells - 1;
if (mPortrait) {
int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding
- (cellHeight * longAxisCells);
mHeightGap = vSpaceLeft / numLongGaps;
int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding
- (cellWidth * shortAxisCells);
if (numShortGaps > 0) {
mWidthGap = hSpaceLeft / numShortGaps;
} else {
mWidthGap = 0;
}
} else {
int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding
- (cellWidth * longAxisCells);
mWidthGap = hSpaceLeft / numLongGaps;
int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding
- (cellHeight * shortAxisCells);
if (numShortGaps > 0) {
mHeightGap = vSpaceLeft / numShortGaps;
} else {
mHeightGap = 0;
}
}
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (mPortrait) {
lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding,
longAxisStartPadding);
} else {
lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding,
shortAxisStartPadding);
}
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
int childheightMeasureSpec =
MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
setMeasuredDimension(widthSpecSize, heightSpecSize);
| void | pointToCellExact(int x, int y, int[] result)Given a point, return the cell that strictly encloses that point
final boolean portrait = mPortrait;
final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
if (result[0] < 0) result[0] = 0;
if (result[0] >= xAxis) result[0] = xAxis - 1;
if (result[1] < 0) result[1] = 0;
if (result[1] >= yAxis) result[1] = yAxis - 1;
| void | pointToCellRounded(int x, int y, int[] result)Given a point, return the cell that most closely encloses that point
pointToCellExact(x + (mCellWidth / 2), y + (mCellHeight / 2), result);
| public int[] | rectToCell(int width, int height)Computes the required horizontal and vertical cell spans to always
fit the given rectangle.
// Always assume we're working with the smallest span to make sure we
// reserve enough space in both orientations.
int actualWidth = mCellWidth + mWidthGap;
int actualHeight = mCellHeight + mHeightGap;
int smallerSize = Math.min(actualWidth, actualHeight);
// Always round up to next largest cell
int spanX = (width + smallerSize) / smallerSize;
int spanY = (height + smallerSize) / smallerSize;
return new int[] { spanX, spanY };
| public void | requestChildFocus(android.view.View child, android.view.View focused)
super.requestChildFocus(child, focused);
if (child != null) {
Rect r = new Rect();
child.getDrawingRect(r);
requestRectangleOnScreen(r);
}
| protected void | setChildrenDrawingCacheEnabled(boolean enabled)
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View view = getChildAt(i);
view.setDrawingCacheEnabled(enabled);
// Update the drawing caches
view.buildDrawingCache();
}
| protected void | setChildrenDrawnWithCacheEnabled(boolean enabled)
super.setChildrenDrawnWithCacheEnabled(enabled);
|
|