Android4.4-Launcher源码分析系列之CellLayout
2016-06-02 09:45
651 查看
一.CellLayout是什么
在前面的 Android4.4-Launcher源码分析系列之Launcher介绍分析了Launcher的布局,CellLayout继承自ViewGroup,
一个Workspace由多个CellLayout组成,每一个CellLayout负责里面图标(favorite)和widget的显示.说白了,我们滑动屏幕的每一页就是一个CellLayout.
二、CellLayout的布局
CellLayout的布局为workspace_screen.xml.
hapticFeedbackEnabled是触力反馈的意思,比如说按一下震动就是触力反馈.
maxGap是CellLayout中元素(图标,widget)之间的最大距离
除了这两个属性其他的属性都是在代码中定义的.
WorkSpace是在insertNewWorkspaceScreen方法中加载CellLayout的布局的.
CellLayout上有很多Cell,都有对应的坐标
![](https://oscdn.geek-share.com/Uploads/Images/Content/202007/14/dc7d4f1f836bfb36ece4aedeaba35614)
红色圆圈就是一个Cell.一个item可以占多个Cell. 那么怎么知道一个item的起始坐标,占据多少格呢,CellLayout类里有一个静态内部类CellInfo用来纪录这些信息.
之前说过CellLayout的主要属性都在代码里定义的,那我们就看下它的一些重要的属性.
现在我把item之间的宽度调到50像素,看下对比
![](https://oscdn.geek-share.com/Uploads/Images/Content/202007/14/2177c8a07e47b8093460723c8ad87024)
![](https://oscdn.geek-share.com/Uploads/Images/Content/202007/14/d7c1935c76cb0863f7a5b017774143ab)
决定水平和垂直方向的Cell个数的是以下属性
DeviceProfile类里定义了整个Launcher的很多属性
继续介绍CellLayout的属性.
这是CellLayout缩略图背景,就是当你长按屏幕空白处时CellLayout缩小时的背景图.
下面两个分别是滑动屏幕到左右边缘,继续拖动而不能滑动过去时显示的背景.
CellLayout只有一个onInterceptTouchEvent方法
![](https://oscdn.geek-share.com/Uploads/Images/Content/202007/14/047351efb5721a05f747f6d0c8ce5d0e)
默认是返回false的,如果mInterceptTouchListener不为空并且执行了onTouch方法那么返回true.mInterceptTouchListener
是在setOnInterceptTouchListener方法里初始化的.
clearTagCellInfo方法是是清除cellInfo的信息,然后设置一个Tag
在前面的 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行,想透彻的分析完不太现实,我只是把重点讲了下,欢迎各位批评指正
![](https://oscdn.geek-share.com/Uploads/Images/Content/201707/61c17ed2eb67211a84e137429ac3f7ad.gif)
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories