一.CellLayout是什麼
在前面的 Android4.4-Launcher源碼分析系列之Launcher介紹分析了Launcher的佈局,CellLayout繼承自ViewGroup,
一個Workspace由多個CellLayout組成,每一個CellLayout負責裏面圖標(favorite)和widget的顯示.說白了,我們滑動屏幕的每一頁就是一個CellLayout.
二、CellLayout的佈局
CellLayout的佈局爲workspace_screen.xml.
<com.android.launcher3.CellLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hapticFeedbackEnabled="false"
launcher:maxGap="@dimen/workspace_max_gap" />
hapticFeedbackEnabled是觸力反饋的意思,比如說按一下震動就是觸力反饋.
maxGap是CellLayout中元素(圖標,widget)之間的最大距離
除了這兩個屬性其他的屬性都是在代碼中定義的.
WorkSpace是在insertNewWorkspaceScreen方法中加載CellLayout的佈局的.
/**
* @param screenId 屏幕Id
* @param insertIndex 插入的序號
* 插入新的屏幕
*/
public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
System.out.println(".............WorkSpace.......增加一頁");
if (mWorkspaceScreens.containsKey(screenId)) {
throw new RuntimeException("Screen id " + screenId + " already exists!");
}
//加載CellLayout的佈局
CellLayout newScreen = (CellLayout)mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
newScreen.setSoundEffectsEnabled(false);
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(insertIndex, screenId);
addView(newScreen, insertIndex);
return screenId;
}
CellLayout上有很多Cell,都有對應的座標
紅色圓圈就是一個Cell.一個item可以佔多個Cell. 那麼怎麼知道一個item的起始座標,佔據多少格呢,CellLayout類裏有一個靜態內部類CellInfo用來紀錄這些信息.
static final class CellInfo {
View cell; //當前這個item對應的View
int cellX = -1; //該item水平方向上的起始單元格
int cellY = -1; //該item垂直方向上的起始單元格
int spanX; //該item水平方向上佔據的單元格數目
int spanY; //該item垂直方向上佔據的單元格數目
long screenId; //屏幕所在的Id
long container;
@Override
public String toString() {
return "Cell[view=" + (cell == null ? "null" : cell.getClass())
+ ", x=" + cellX + ", y=" + cellY + "]";
}
}
之前說過CellLayout的主要屬性都在代碼裏定義的,那我們就看下它的一些重要的屬性.
//item之間的寬度
mWidthGap = mOriginalWidthGap =0;
//item之間的高度
mHeightGap = mOriginalHeightGap = 0;
現在我把item之間的寬度調到50像素,看下對比
決定水平和垂直方向的Cell個數的是以下屬性
//一行有多少個Cell
mCountX = (int) grid.numColumns;
//一列有多少個Cell
mCountY = (int) grid.numRows;
grid 是DeviceProfile類的對象
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
DeviceProfile類裏定義了整個Launcher的很多屬性
class DeviceProfile {
String name;
float minWidthDps;
float minHeightDps;
float numRows;
float numColumns;
float iconSize;
float iconTextSize;
float numHotseatIcons;
float hotseatIconSize;
boolean isLandscape;
boolean isTablet;
boolean isLargeTablet;
boolean transposeLayoutWithOrientation;
int desiredWorkspaceLeftRightMarginPx;
int edgeMarginPx;
Rect defaultWidgetPadding;
int widthPx;
int heightPx;
int availableWidthPx;
int availableHeightPx;
int iconSizePx;
int iconTextSizePx;
int cellWidthPx;
int cellHeightPx;
int folderBackgroundOffset;
int folderIconSizePx;
int folderCellWidthPx;
int folderCellHeightPx;
int hotseatCellWidthPx;
int hotseatCellHeightPx;
int hotseatIconSizePx;
int hotseatBarHeightPx;
int hotseatAllAppsRank;
int allAppsNumRows;
int allAppsNumCols;
int searchBarSpaceWidthPx;
int searchBarSpaceMaxWidthPx;
int searchBarSpaceHeightPx;
int searchBarHeightPx;
int pageIndicatorHeightPx;
這裏就不一一介紹了.
繼續介紹CellLayout的屬性.
這是CellLayout縮略圖背景,就是當你長按屏幕空白處時CellLayout縮小時的背景圖.
mNormalBackground = res.getDrawable(R.drawable.screenpanel);
下面兩個分別是滑動屏幕到左右邊緣,繼續拖動而不能滑動過去時顯示的背景.
mOverScrollLeft = res.getDrawable(R.drawable.overscroll_glow_left)
mOverScrollRight = res.getDrawable(R.drawable.overscroll_glow_right);
最後我們看下CellLayout的觸摸事件
CellLayout只有一個onInterceptTouchEvent方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if (action == MotionEvent.ACTION_DOWN) {
System.out.println(".................776"+"ACTION_DOWN1");
//清除cellInfo信息
clearTagCellInfo();
}
if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
return true;
}
if (action == MotionEvent.ACTION_DOWN) {
System.out.println(".................787"+"ACTION_DOWN2");
setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
}
return false;
}
當按下屏幕時會執行clearTagCellInfo方法和etTagToCellInfoForPoint方法,如下圖默認是返回false的,如果mInterceptTouchListener不爲空並且執行了onTouch方法那麼返回true.mInterceptTouchListener
是在setOnInterceptTouchListener方法裏初始化的.
public void setOnInterceptTouchListener(View.OnTouchListener listener) {
mInterceptTouchListener = listener;
}
setOnInterceptTouchListener是在WorkSpace調用的
CellLayout cl = ((CellLayout) child);
cl.setOnInterceptTouchListener(this);
現在回頭看下clearTagCellInfo方法和etTagToCellInfoForPoint方法
clearTagCellInfo方法是是清除cellInfo的信息,然後設置一個Tag
private void clearTagCellInfo() {
final CellInfo cellInfo = mCellInfo;
cellInfo.cell = null;
cellInfo.cellX = -1;
cellInfo.cellY = -1;
cellInfo.spanX = 0;
cellInfo.spanY = 0;
setTag(cellInfo);
}
setTagToCellInfoForPoint是將位置信息保存在CellLayout的tag中
public void setTagToCellInfoForPoint(int touchX, int touchY) {
final CellInfo cellInfo = mCellInfo;
Rect frame = mRect;
final int x = touchX + getScrollX();
final int y = touchY + getScrollY();
final int count = mShortcutsAndWidgets.getChildCount();
boolean found = false;
for (int i = count - 1; i >= 0; i--) {
final View child = mShortcutsAndWidgets.getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
//如果item可見並且item有動畫
if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) &&
lp.isLockedToGrid) {
//獲取item的尺寸信息,相對於CellLayout
child.getHitRect(frame);
float scale = child.getScaleX();
frame = new Rect(child.getLeft(), child.getTop(), child.getRight(),
child.getBottom());
frame.offset(getPaddingLeft(), getPaddingTop());
frame.inset((int) (frame.width() * (1f - scale) / 2),
(int) (frame.height() * (1f - scale) / 2));
//如果當前事件正好落在該child上
if (frame.contains(x, y)) {
cellInfo.cell = child;
cellInfo.cellX = lp.cellX;
cellInfo.cellY = lp.cellY;
cellInfo.spanX = lp.cellHSpan;
cellInfo.spanY = lp.cellVSpan;
found = true;
break;
}
}
}
mLastDownOnOccupiedCell = found;
//如果點擊的是空白區域
if (!found) {
final int cellXY[] = mTmpXY;
//得到當前事件所在的單元格
pointToCellExact(x, y, cellXY);
//然後保存當前位置信息
cellInfo.cell = null;
cellInfo.cellX = cellXY[0];
cellInfo.cellY = cellXY[1];
cellInfo.spanX = 1;
cellInfo.spanY = 1;
}
//將位置信息保存在CellLayout的tag中
setTag(cellInfo);
}
CellLayout代碼有3369行,想透徹的分析完不太現實,我只是把重點講了下,歡迎各位批評指正