最近在學習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.DragLayer 12 android:id="@+id/drag_layer" 13 14 android:layout_width="match_parent" 15 android:layout_height="match_parent"> 16 17 <com.android.launcher3.FocusIndicatorView 18 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.Workspace 25 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 <include 33 android:id="@+id/hotseat" 34 layout="@layout/hotseat" 35 36 android:layout_width="match_parent" 37 android:layout_height="match_parent" /> 38 39 <include 40 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 when 45 we go into AllApps --> 46 <include 47 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 <include 54 android:id="@+id/search_drop_target_bar" 55 56 layout="@layout/search_drop_target_bar" /> 57 58 <include 59 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 <include 66 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 <ViewStub 74 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 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 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 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.