本文對Launcher2進行一個全面的瞭解,介紹Launcher2中的自定義控件java
如圖:android
launcher.xmlapp
<?xml version="1.0" encoding="utf-8"?> <com.callmewill.launcher2.DragLayer xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res/com.callmewill.launcher2" android:id="@+id/drag_layer" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/workspace_bg" > //最外層的自定義佈局繼承FrameLayout,主要功能手指拖動時生成一個懸浮的view,以及各類位值計算 <include android:id="@+id/dock_divider" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginBottom="@dimen/button_bar_height" layout="@layout/workspace_divider" /> //底部指示器的背景 <include android:id="@+id/paged_view_indicator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginBottom="@dimen/button_bar_height" layout="@layout/scroll_indicator" /> //底部指示器 <!-- The workspace contains 5 screens of cells --> <com.callmewill.launcher2.Workspace //繼承PagedView,SmoothPagedView,實現一個ViewGroup內部有多個View而且左右滑動 android:id="@+id/workspace" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/workspace_bottom_padding" android:paddingLeft="@dimen/workspace_left_padding" android:paddingRight="@dimen/workspace_right_padding" android:paddingTop="@dimen/workspace_top_padding" launcher:cellCountX="@integer/cell_count_x" launcher:cellCountY="@integer/cell_count_y" launcher:defaultScreen="2" launcher:pageSpacing="@dimen/workspace_page_spacing" launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left" launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right" > <include android:id="@+id/cell1" layout="@layout/workspace_screen" /> //內部的子View,主要類是CellLayout,CellLayout是一個網格的自定義ViewGroup,用來裝各類item <include android:id="@+id/cell2" layout="@layout/workspace_screen" /> <include android:id="@+id/cell3" layout="@layout/workspace_screen" /> <include android:id="@+id/cell4" layout="@layout/workspace_screen" /> <include android:id="@+id/cell5" layout="@layout/workspace_screen" /> </com.callmewill.launcher2.Workspace> <include //底部的Dock欄 android:id="@+id/hotseat" android:layout_width="match_parent" android:layout_height="@dimen/button_bar_height_plus_padding" android:layout_gravity="bottom" layout="@layout/hotseat" /> <include android:id="@+id/qsb_bar" //頂部的搜索和移目標控件 layout="@layout/qsb_bar" /> <com.callmewill.launcher2.DrawableStateProxyView //app抽屜,一樣繼承PagedView android:id="@+id/voice_button_proxy" android:layout_width="80dp" android:layout_height="@dimen/qsb_bar_height" android:layout_gravity="top|right" android:clickable="true" android:onClick="onClickVoiceButton" launcher:sourceViewId="@+id/voice_button" /> <include android:id="@+id/apps_customize_pane" //app抽屜,一樣繼承PagedView android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/apps_customize_pane" android:visibility="invisible" /> <include android:id="@+id/workspace_cling" //用戶引導 android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/workspace_cling" android:visibility="gone" /> <include android:id="@+id/folder_cling" //用戶引導 android:layout_width="match_parent" android:layout_height="match_parent" layout="@layout/folder_cling" android:visibility="gone" /> </com.callmewill.launcher2.DragLayer>
從上往下說吧ide
搜索條:點擊搜索條時調用的系統自己的SearchManager來完成相關邏輯,Launcher.java 中的startGlobleSearch方法:佈局
public void startGlobalSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) { final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity(); if (globalSearchActivity == null) { Log.w(TAG, "No global search activity found."); return; } Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(globalSearchActivity); // Make sure that we have a Bundle to put source in if (appSearchData == null) { appSearchData = new Bundle(); } else { appSearchData = new Bundle(appSearchData); } // Set source to package name of app that starts global search, if not // set already. if (!appSearchData.containsKey("source")) { appSearchData.putString("source", getPackageName()); } intent.putExtra(SearchManager.APP_DATA, appSearchData); if (!TextUtils.isEmpty(initialQuery)) { intent.putExtra(SearchManager.QUERY, initialQuery); } if (selectInitialQuery) { intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery); } intent.setSourceBounds(sourceBounds); try { startActivity(intent); } catch (ActivityNotFoundException ex) { Log.e(TAG, "Global search activity not found: " + globalSearchActivity); } }
DragLayer.javaspa
——FrameLayoutcode
——ViewGroupxml
——PagedView 內部View跟隨手指滑動繼承
——SmoothPagedView 處理滑動時的速度圖片
——Workspace 完成內容的展現
CellLayout.java 在Workspace中,和Hotseat中,是一個網格狀的控件,能夠設置行列,span,用來顯示app,widget,shortcut,folder等
——ViewGroup
AppCustomsizeTabHost.java
——TabHos t抽屜內部的Tab標籤
AppCustomsizePagedView
——ViewGroup
——PagedView
——DragItemPagedVIew 手提內拖拽app到Workspace
Folder.java 展現文件夾內部內容的View,內部包含一個CellLayout
——LinearLayout
FolderIcon.java 文件夾縮略圖
——LinearLayout
BubbleTextView.java 用來顯示app,shortcut
——TextView
textView.setCompoundDrawablesWithIntrinsicBounds(left,top,right,bottom)給TextView設置上下左右四個圖片
PagedViewIcon.java 抽屜內部顯示icon
——TextView
—— FrameLayout
PagedViewWidget.java 抽屜內部顯示Widget
——LinearLayout
AppWidgetResizeFrame.java 重置Widget大小時的指示框
——FrameLayout
LauncherAppWidgetHost.java 顯示加載Widget有關
——AppWidgetHost
每一個控件的代碼量都很大,看起來也費時費力。