首页 > 代码库 > Android Launcher 3 简单分析
Android Launcher 3 简单分析
最近在学习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 <?xml version="1.0" encoding="utf-8"?> 2 3 <!-- Full screen view projects under the status bar and contains the background --> 4 <com.android.launcher3.LauncherRootView xmlns:android="http://schemas.android.com/apk/res/android" 5 xmlns:launcher="http://schemas.android.com/apk/res-auto" 6 android:id="@+id/launcher" 7 android:layout_width="match_parent" 8 android:layout_height="match_parent" 9 android:fitsSystemWindows="true">10 11 <com.android.launcher3.DragLayer12 android:id="@+id/drag_layer"13 14 android:layout_width="match_parent"15 android:layout_height="match_parent">16 17 <com.android.launcher3.FocusIndicatorView18 android:id="@+id/focus_indicator"19 android:layout_width="22dp"20 android:layout_height="22dp" />21 22 <!-- The workspace contains 5 screens of cells -->23 <!-- DO NOT CHANGE THE ID -->24 <com.android.launcher3.Workspace25 android:id="@+id/workspace"26 android:layout_width="match_parent"27 android:layout_height="match_parent"28 launcher:defaultScreen="@integer/config_workspaceDefaultScreen"29 launcher:pageIndicator="@+id/page_indicator"></com.android.launcher3.Workspace>30 31 <!-- DO NOT CHANGE THE ID -->32 <include33 android:id="@+id/hotseat"34 layout="@layout/hotseat"35 36 android:layout_width="match_parent"37 android:layout_height="match_parent" />38 39 <include40 android:id="@+id/overview_panel"41 layout="@layout/overview_panel"42 android:visibility="gone" />43 44 <!-- Keep these behind the workspace so that they are not visible when45 we go into AllApps -->46 <include47 android:id="@+id/page_indicator"48 layout="@layout/page_indicator"49 android:layout_width="wrap_content"50 android:layout_height="wrap_content"51 android:layout_gravity="center_horizontal" />52 53 <include54 android:id="@+id/search_drop_target_bar"55 56 layout="@layout/search_drop_target_bar" />57 58 <include59 android:id="@+id/widgets_view"60 layout="@layout/widgets_view"61 android:layout_width="match_parent"62 android:layout_height="match_parent"63 android:visibility="invisible" />64 65 <include66 android:id="@+id/apps_view"67 layout="@layout/all_apps"68 android:layout_width="match_parent"69 android:layout_height="match_parent"70 android:visibility="invisible" />71 </com.android.launcher3.DragLayer>72 73 <ViewStub74 android:id="@+id/launcher_overlay_stub"75 android:layout_width="match_parent"76 android:layout_height="match_parent"77 android:inflatedId="@+id/launcher_overlay"78 android:layout="@layout/launcher_overlay" />79 </com.android.launcher3.LauncherRootView>
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 spacing17 // between all icons18 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 - paddingBottom26 - (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 sizes31 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 ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() { 2 ArrayList<InvariantDeviceProfile> 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 orientation25 // also includes the nav bar on the side26 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 & bottom29 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.
Android Launcher 3 简单分析