最近在学习Android Launcher的相关知识,在github上找到可以在Android studio上编译的Launcher 3代码,地址:https://github.com/rydanliu/Launcher3
Launcher 3的界面主要由SearchDropTargetBar、Workspace、CellLayout、PageIndicator、Hotseat组成。如下图:
Launcher 3 最主要的是一个Activity,基本上所有操作都集中在这个Activity上。这个Activity文件为Launcher.java,他的布局文件为launcher.xml。
下面为竖屏的布局文件,路径为res/layout-port/launcher.xml。
1 2 3 410 11 16 17 72 7321 22 23 24 30 31 32 38 39 43 44 46 52 53 57 58 64 65 71 79
SearchDropTargetBar
屏幕最上方有个搜索框,在我们拖动图标的时候,搜索框会替换成“删除“
Workspace
就是屏幕上左右滑的好几屏幕的容器
CellLayout
Workspace里面可以滑动的单独一屏,CellLayout负责图标和小部件的显示和整齐摆放。
PageIndicator
滑动屏幕的时候看见下方的指示器
Hotseat
用来放置比较常用的应用,比如拨号,短信,相机等。
下面介绍几个类
CellLayout 类
mCountX 和 mCountY 分别表示 “x方向icon的个数” 和 “y方向icon的个数”
mOccupied 二维数组用来标记每个cell是否被占用,内部类CellInfo为静态类,其对象用于存储cell的基本信息。
DeviceProfile 类
设置各元素布局的padding 。修改workspace的padding使用getWorkspacePadding方法。
1 /** Returns the workspace padding in the specified orientation */ 2 Rect getWorkspacePadding(boolean isLayoutRtl) { 3 Rect searchBarBounds = getSearchBarBounds(isLayoutRtl); 4 Rect padding = new Rect(); 5 if (isLandscape && transposeLayoutWithOrientation) { 6 // Pad the left and right of the workspace with search/hotseat bar sizes 7 if (isLayoutRtl) { 8 padding.set(hotseatBarHeightPx, edgeMarginPx, 9 searchBarBounds.width(), edgeMarginPx); 10 } else { 11 padding.set(searchBarBounds.width(), edgeMarginPx, 12 hotseatBarHeightPx, edgeMarginPx); 13 } 14 } else { 15 if (isTablet) { 16 // Pad the left and right of the workspace to ensure consistent spacing 17 // between all icons 18 float gapScale = 1f + (dragViewScale - 1f) / 2f; 19 int width = getCurrentWidth(); 20 int height = getCurrentHeight(); 21 int paddingTop = searchBarBounds.bottom; 22 int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx; 23 int availableWidth = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) + 24 (inv.numColumns * gapScale * cellWidthPx))); 25 int availableHeight = Math.max(0, height - paddingTop - paddingBottom 26 - (int) (2 * inv.numRows * cellHeightPx)); 27 padding.set(availableWidth / 2, paddingTop + availableHeight / 2, 28 availableWidth / 2, paddingBottom + availableHeight / 2); 29 } else { 30 // Pad the top and bottom of the workspace with search/hotseat bar sizes 31 32 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 33 searchBarBounds.bottom, 34 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right, 35 hotseatBarHeightPx + pageIndicatorHeightPx); 36 37 38 } 39 } 40 return padding; 41 }
比如我要将workspace里图标顶部不留空隙,需要设置padding.set的第二个参数为0.
1 padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left, 2 0,//searchBarBounds.bottom, 3 desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right, 4 hotseatBarHeightPx + pageIndicatorHeightPx);
InvariantDeviceProfile类
一些不变的设备相关参数管理类,landscapeProfile 和 portraitProfile为横竖屏模式的DeviceProfile。
getPredefinedDeviceProfiles方法 负责加载在不同设备上不同的布局,和图标大小等。
1 ArrayListgetPredefinedDeviceProfiles() { 2 ArrayList predefinedDeviceProfiles = new ArrayList<>(); 3 // width, height, #rows, #columns, #folder rows, #folder columns, 4 // iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId. 5 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby", 6 255, 300, 2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); 7 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby", 8 255, 400, 3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4)); 9 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby", 10 275, 420, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); 11 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby", 12 255, 450, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); 13 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S", 14 296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4)); 15 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4", 16 335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); 17 18 19 20 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5", 21 359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4)); 22 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone", 23 406, 694, 5, 5, 4, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); 24 // The tablet profile is odd in that the landscape orientation 25 // also includes the nav bar on the side 26 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7", 27 575, 904, 5, 6, 4, 5, 4, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); 28 // Larger tablet profiles always have system bars on the top & bottom 29 predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10", 30 727, 1207, 5, 6, 4, 5, 4, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); 31 predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet", 32 1527, 2527, 7, 7, 6, 6, 4, 100, 20, 7, 72, R.xml.default_workspace_4x4)); 33 return predefinedDeviceProfiles; 34 }
比如我在上面代码的17行加入下列代码,将Hotseat设置成3格,图标大小为72dp
1 predefinedDeviceProfiles.add(new InvariantDeviceProfile("MX 4", 2 359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 3, 72, R.xml.default_workspace_4x4));
由于launcher是有许多自定义控件构成的,这里涉及到onMesure,onLayout,onDraw方法的复写
onMesure方法顾名思义,主要是用来重新测量自定义控件的高度和宽度,就是设置它的dimesion,一般所有自定义VIEW都需要复写这个方法。
onLayout则主要是ViewGroup需要复写这个方法,其作用给这个ViewGroup下子View布局好显示的位置。
onDraw则是需要真真正正画出内容的控件需要复写的方法,比如textview,或者其子类,其最终利用一个很重要的类Canvas的对象来实现一系列的画图,比如canvas.drawcircle,canvas.drawline.
原文地址:https://www.cnblogs.com/l2rf/p/5850341.html