怕以后找不到,转载过来,原博为:http://blog.csdn.net/stevenhu_223/article/details/9842115
====================================================================================================================================
前言:Launcher是Android的系统应用,在手机开机后第一个见到就是Launcher。用户通过Launcher基本上可以从整体上纵观手机中所存在的所有应用;Android源码中的Launcher分两个主要界面,一个是WorkSpace界面,就是我们俗称的桌面;另一个是AppsCustomizePagedView界面,就是我们俗称的菜单界面;Launcher可以说是Android系统中比较重要的系统应用模块,能够掌握和熟悉Launcher模块,当然是最好的,Launcher这块骨头很硬,必须要慢慢啃;个人认为,在熟悉一个系统模块之前,弄清楚它的整体布局是至关重要的,所以这篇文章主要是重点分析Launcher的相关布局,若有不当之处,还请同行们指点迷津。废话有点多了,开始吧!
一. Launcher主入口
Launcher就是一个系统应用, 如果想要知道Launcher中都有那些四大组件和主入口,那么通过它的AndroidManifest.xml文件代码就可以探知,如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.launcher">
- <original-package android:name="com.android.launcher2" />
- <!-- Launcher中自定义的权限 -->
- <permission
- android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_install_shortcut"
- android:description="@string/permdesc_install_shortcut" />
- <permission
- android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_uninstall_shortcut"
- android:description="@string/permdesc_uninstall_shortcut"/>
- <permission
- android:name="com.android.launcher.permission.READ_SETTINGS"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_read_settings"
- android:description="@string/permdesc_read_settings"/>
- <permission
- android:name="com.android.launcher.permission.WRITE_SETTINGS"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
- android:label="@string/permlab_write_settings"
- android:description="@string/permdesc_write_settings"/>
- <!-- 注册系统权限 -->
- <uses-permission android:name="android.permission.CALL_PHONE" />
- <uses-permission android:name="android.permission.SET_WALLPAPER" />
- <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.BIND_APPWIDGET" />
- <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
- <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
- <!-- hardwareAccelerated为是否使用硬件加速,largeHeap为是否使用了大的堆内存,值在config.xml文件中定义 -->
- <application
- android:name="com.android.launcher2.LauncherApplication"
- android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home"
- android:hardwareAccelerated="@bool/config_hardwareAccelerated"
- android:largeHeap="@bool/config_largeHeap">
- <activity
- android:name="com.android.launcher2.Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:windowSoftInputMode="adjustPan"
- android:screenOrientation="nosensor">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
- <activity
- android:name="com.android.launcher2.WallpaperChooser"
- style="@style/Theme.WallpaperPicker"
- android:label="@string/pick_wallpaper"
- android:icon="@drawable/ic_launcher_wallpaper"
- android:finishOnCloseSystemDialogs="true"
- android:process=":wallpaper_chooser">
- <intent-filter>
- <action android:name="android.intent.action.SET_WALLPAPER" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <meta-data android:name="android.wallpaper.preview"
- android:resource="@xml/wallpaper_picker_preview" />
- </activity>
- <activity android:name="com.android.launcher2.RocketLauncher"
- android:label="@string/dream_name"
- android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.DREAM" />
- </intent-filter>
- </activity>
- <!-- Intent received used to install shortcuts from other applications -->
- <receiver
- android:name="com.android.launcher2.InstallShortcutReceiver"
- android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
- <intent-filter>
- <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
- </intent-filter>
- </receiver>
- <!-- Intent received used to uninstall shortcuts from other applications -->
- <receiver
- android:name="com.android.launcher2.UninstallShortcutReceiver"
- android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">
- <intent-filter>
- <action android:name="com.android.launcher.action.UNINSTALL_SHORTCUT" />
- </intent-filter>
- </receiver>
- <!-- The settings provider contains Home's data, like the workspace favorites -->
- <provider
- android:name="com.android.launcher2.LauncherProvider"
- android:authorities="com.android.launcher2.settings"
- android:writePermission="com.android.launcher.permission.WRITE_SETTINGS"
- android:readPermission="com.android.launcher.permission.READ_SETTINGS" />
- </application>
- </manifest>
通过AndroidManifest.xm文件中的代码可以知道:
1). com.android.launcher2.LauncherApplication类描述整个应用,该类继承Application。某种意义上来说是Launcher的主入口类;在Launcher应用创建时,最先执行的是LauncherApplication类中的onCreate()方法;LauncherApplication类中主要实现注册一些广播接收器和监听数据库改变的ContentObserver,同时对外(主要是主Activity)提供一些获取相关对象(LauncherModel、IconCache、LauncherProvider等)和屏幕密度的接口函数。
2). com.android.launcher2.Launcher为Launcher模块的主Activity,Launcher中的灵魂类。在该主Activity的onCreate方法中执行setContentView(R.layout.launcher)加载Launcher的整个布局。
3). com.android.launcher2.WallpaperChooser为长按桌面空白处弹出的选择壁纸相关的Activity组件。
4).com.android.launcher2.InstallShortcutReceiver为安装快捷方式时负责接收相关广播进行操作的BroadcastReceiver组件。比如,要为自己开发的应用添加第一次启动时在桌面创建快捷键的功能,那么就需要注册com.android.launcher.permission.INSTALL_SHORTCUT权限,同时发送Intent的Action为com.android.launcher.action.INSTALL_SHORTCUT的广播。
5). com.android.launcher2.UninstallShortcutReceiver为卸载快捷方式时负责接收相关广播进行操作的BroadcastReceiver组件。
7). com.android.launcher2.LauncherProvider为Launcher进行数据库相关操作的ContentProvider组件。
二. Launcher总布局文件launcher.xml浅析
1. Launcher.xml文件代码(竖向布局):
- <?xml version="1.0" encoding="utf-8"?>
- <com.android.launcher2.DragLayer
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:id="@+id/drag_layer"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- Keep these behind the workspace so that they are not visible when
- we go into AllApps -->
- <!-- dock_divider为桌面分隔线,将hotseat和Workspace分隔 -->
- <include
- android:id="@+id/dock_divider"
- layout="@layout/workspace_divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/button_bar_height"
- android:layout_gravity="bottom" />
- <!-- 桌面分隔线的上指示条,Workspace翻页的时候显示 -->
- <include
- android:id="@+id/paged_view_indicator"
- layout="@layout/scroll_indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginBottom="@dimen/button_bar_height" />
- <!-- The workspace contains 5 screens of cells -->
- <!-- Workspace即手机桌面,默认系统是包含可翻转5页 -->
- <com.android.launcher2.Workspace
- android:id="@+id/workspace"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/qsb_bar_height_inset"
- android:paddingBottom="@dimen/button_bar_height"
- launcher:defaultScreen="2"
- launcher:cellCountX="4"
- launcher:cellCountY="4"
- launcher:pageSpacing="@dimen/workspace_page_spacing"
- launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
- launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
- <!-- Workspace总共可翻转5个页面,一个 workspace_screen定义一个页面布局-->
- <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
- <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.android.launcher2.Workspace>
- <!-- qsb_bar布局包含桌面上的可搜索框 以及长按桌面上图标时显示删除和应用信息的操作框-->
- <include
- android:id="@+id/qsb_bar"
- layout="@layout/qsb_bar" />
- <!-- 点击hotseat中心图标进入的界面,该界面显示所有应用和小部件 -->
- <include layout="@layout/apps_customize_pane"
- android:id="@+id/apps_customize_pane"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
- <!-- hotseat为桌面分隔线下的界面, 不随Workspace翻页操作而移动 -->
- <include layout="@layout/hotseat"
- android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="@dimen/button_bar_height_plus_padding"
- android:layout_gravity="bottom" />
- <!-- 手机刚开机,或者对launcher应用清空数据第一次进入到workspace时弹出的操作介绍界面 -->
- <include layout="@layout/workspace_cling"
- android:id="@+id/workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <!-- 手机刚开机,或者对launcher应用清空数据,第一次打开将workspace上两个以上的图标拖到一起形成 的文件夹时弹出的操作界面-->
- <include layout="@layout/folder_cling"
- android:id="@+id/folder_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- </com.android.launcher2.DragLayer>
2. 以下列出Launcher主要界面的截图:
图1 图2 图3
上面三个图比较直观的说明Launcher的主要布局:
图1:Launcher的桌面布局(从上到下为搜索框、Workspace、分割线、hotseat)。
图2:菜单界面(apps_customize_pane)之一的显示所有已安装的应用程序;
图3:菜单界面(apps_customize_pane)之一的显示所有已创建的widget(小部件)
注:菜单界面整体是一个TabHost,由两个子Tab组成;一个就是显示图2界面的子Tab,另一个就是显示图3界面的子Tab
3. 从上到下分析Launcher.xml文件中的代码:
1). 整个树状布局的最上层是com.android.launcher2.DragLayer,DragLayer类继承自FrameLayout,属于自定义实现的ViewGroup类型容器视图组件。DragLayer类中主要负责Launcher中相关的拖拽处理;
2).id为dock_divider的子视图(分割线):通过< include />元素添加布局为workspace_divider(对应的布局),我们到workspace_divider.xml文件看看该子视图的真正布局,如下:
workspace_divider.xml代码:
- <?xml version="1.0" encoding="utf-8"?>
- <ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:paddingLeft="@dimen/workspace_divider_padding_left"
- android:paddingRight="@dimen/workspace_divider_padding_right"
- android:paddingTop="@dimen/workspace_divider_padding_top"
- android:paddingBottom="@dimen/workspace_divider_padding_bottom"
- android:scaleType="fitXY"
- android:src="@drawable/hotseat_track_holo" />
由此可知id为dock_divider的子视图就是一个ImageView对象,即一条点9图的分割线;该分割线把Workspace和Hotseat分割出来;
3). id为paged_view_indicator的子视图:该子视图对应布局为scroll_indicator,scroll_indicator.xml文件的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <ImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:visibility="gone"
- android:alpha="0"
- android:scaleType="fitXY"
- android:src="@drawable/hotseat_scrubber_holo" />
4). id为workspace的子视图(Workspace界面视图):该子视图为自定义类Workspace,Workspace继承自SmoothPagedView,SmoothPagedView继承自PagedView,PagedView继承自ViewGroup;所以Workspace的终极父类也是ViewGroup;即该子视图为ViewGroup类型的自定义容器视图;在Workspace视图中又包含了5个id分别为cell1、cell2、cell3、cell4、cell5的子视图。它们对应的布局均为workspace_screen,workspace_screen.xml文件的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <com.android.launcher2.CellLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- style="@style/WorkspaceScreen"
- android:paddingLeft="@dimen/workspace_left_padding"
- android:paddingRight="@dimen/workspace_right_padding"
- android:paddingTop="@dimen/workspace_top_padding"
- android:paddingBottom="@dimen/workspace_bottom_padding"
- android:hapticFeedbackEnabled="false"
- launcher:cellWidth="@dimen/workspace_cell_width"
- launcher:cellHeight="@dimen/workspace_cell_height"
- launcher:widthGap="@dimen/workspace_width_gap"
- launcher:heightGap="@dimen/workspace_height_gap"
- launcher:maxGap="@dimen/workspace_max_gap" />
5). id为qsb_bar的子视图:该子视图对应的布局为qsb_bar,布局文件qsb_bar.xml的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <com.android.launcher2.SearchDropTargetBar
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- style="@style/QSBBar"
- android:focusable="false"
- android:background="@drawable/search_bg_panel">
- <!-- Search buttons container -->
- <!-- 桌面上可点击的搜索框 -->
- <include android:id="@+id/qsb_search_bar"
- layout="@layout/search_bar" />
- <!-- Drag specific targets container -->
- <!-- 长按菜单界面快捷图标进入到桌面时显示删除和应用信息的操作框,这时候搜索框会隐藏,操作框会显示在 搜索框的位置-->
- <LinearLayout
- style="@style/SearchDropTargetBar"
- android:id="@+id/drag_target_bar"
- android:visibility="gone">
- <!-- 显示删除的布局 -->
- <FrameLayout
- style="@style/DropTargetButtonContainer"
- android:layout_weight="1">
- <!-- Delete target -->
- <com.android.launcher2.DeleteDropTarget
- style="@style/DropTargetButton"
- android:id="@+id/delete_target_text"
- android:text="@string/delete_zone_label_workspace"
- android:drawableLeft="@drawable/delete_target_selector" />
- </FrameLayout>
- <!-- 显示应用信息的布局 -->
- <FrameLayout
- style="@style/DropTargetButtonContainer"
- android:layout_weight="1">
- <!-- Info target -->
- <com.android.launcher2.InfoDropTarget
- style="@style/DropTargetButton"
- android:id="@+id/info_target_text"
- android:text="@string/info_target_label"
- android:drawableLeft="@drawable/info_target_selector" />
- </FrameLayout>
- </LinearLayout>
- </com.android.launcher2.SearchDropTargetBar>
-->搜索框对应的是id为qsb_search_bar的视图,它对应的布局文件为search_bar.xml;
-->操作框对应的是id为drag_target_bar的视图(LinearLayout),操作框包含两部分(两个FrameLayout):一部分对应的是id为delete_target_text的布局(DeleteDropTarget),拖拽应用图标至该区域时会进入卸载应用的操作;另一部分对应的是id为info_target_text的布局(InfoDropTarget),拽应用图标至该区域时会进入显示应用程序信息的界面;如下图就是从菜单拖拽应用图标到桌面是操作框显示的示意图(屏幕上方显示操作框):
6). id为apps_customize_pane的子视图(菜单界面视图):该子视图对应的布局为apps_customize_pane,apps_customize_pane文件代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <!-- 点击hotseat中心图标显示的菜单界面布局,该界面显示所有应用和小部件 -->
- <com.android.launcher2.AppsCustomizeTabHost
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:background="#FF000000">
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- The layout_width of the tab bar gets overriden to align the content
- with the text in the tabs in AppsCustomizeTabHost. -->
- <!-- Tab标签"应用程序"、"窗口小部件"和应用商店图标的视图布局 -->
- <FrameLayout
- android:id="@+id/tabs_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/apps_customize_tab_bar_height"
- android:layout_gravity="center_horizontal">
- <com.android.launcher2.FocusOnlyTabWidget
- android:id="@android:id/tabs"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="left"
- android:background="@drawable/tab_unselected_holo"
- android:tabStripEnabled="false" />
- <include
- android:id="@+id/market_button"
- layout="@layout/market_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="right" />
- </FrameLayout>
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- 承载所有应用程序图标和widget的视图布局 -->
- <com.android.launcher2.AppsCustomizePagedView
- android:id="@+id/apps_customize_pane_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:cellCountX="@integer/apps_customize_cellCountX"
- launcher:cellCountY="@integer/apps_customize_cellCountY"
- launcher:pageLayoutWidthGap="@dimen/apps_customize_pageLayoutWidthGap"
- launcher:pageLayoutHeightGap="@dimen/apps_customize_pageLayoutHeightGap"
- launcher:pageLayoutPaddingTop="@dimen/apps_customize_pageLayoutPaddingTop"
- launcher:pageLayoutPaddingBottom="@dimen/apps_customize_pageLayoutPaddingBottom"
- launcher:pageLayoutPaddingLeft="@dimen/apps_customize_pageLayoutPaddingLeft"
- launcher:pageLayoutPaddingRight="@dimen/apps_customize_pageLayoutPaddingRight"
- launcher:widgetCellWidthGap="@dimen/apps_customize_widget_cell_width_gap"
- launcher:widgetCellHeightGap="@dimen/apps_customize_widget_cell_height_gap"
- launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"
- launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"
- launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"
- launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"
- launcher:maxGap="@dimen/workspace_max_gap" />
- <ImageView
- android:id="@+id/animation_buffer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <!-- 蓝色翻页指示线 -->
- <include
- android:id="@+id/paged_view_indicator"
- layout="@layout/scroll_indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom" />
- </FrameLayout>
- </LinearLayout>
- <!-- 第一次进入到菜单界面时弹出的操作介绍界面 -->
- <include layout="@layout/all_apps_cling"
- android:id="@+id/all_apps_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- </com.android.launcher2.AppsCustomizeTabHost>
菜单界面视图由自定义视图类AppsCustomizeTabHost描述,该类继承TabHost,在菜单界面视图中,包含了一个LinearLayout布局和一个id为all_apps_cling的视图布局(对应的布局为all_apps_cling):
-->LinearLayout布局中又包含了两个FrameLayout(对应的id分别为tabs_container、tabcontent)。
-->id为tabs_container的FrameLayout中包含了菜单界面中"应用程序"和“窗口小部件”这两个子Tab标签的视图布局(FocusOnlyTabWidget)和点击进入“应用商店“的图标(对应布局为market_button)。
-->id为tabcontent的FrameLayout中包含了用来承载应用程序图标和widget的界面视图布局(AppsCustomizePagedView)和指示翻页操作的蓝色指示线(对应布局为scroll_indicator)。
-->id为all_apps_cling的视图布局为手机恢复出厂设置(或清空Launcher数据)后第一次进入到菜单界面时弹出的操作介绍界面,如下图的界面:
7). id为hotseat的子视图:该子视图对应的布局为hotseat,hotseat.xml文件的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <com.android.launcher2.Hotseat
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:background="@drawable/hotseat_bg_panel"
- launcher:cellCountX="5"
- launcher:cellCountY="1">
- <com.android.launcher2.CellLayout
- android:id="@+id/layout"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:paddingTop="@dimen/button_bar_height_top_padding"
- android:paddingBottom="@dimen/button_bar_height_bottom_padding"
- android:paddingLeft="@dimen/button_bar_width_left_padding"
- android:paddingRight="@dimen/button_bar_width_right_padding"
- launcher:cellWidth="@dimen/hotseat_cell_width"
- launcher:cellHeight="@dimen/hotseat_cell_height"
- launcher:widthGap="@dimen/hotseat_width_gap"
- launcher:heightGap="@dimen/hotseat_height_gap"
- launcher:maxGap="@dimen/workspace_max_gap" />
- </com.android.launcher2.Hotseat>
点击中间的菜单按钮实现进入菜单界面,hotseat中的快捷图标也可以任意拖拽放置(也可以将hotseat中的快捷图标拖拽到Workspace中放置);
8). id为workspace_cling的子视图:该视图对应的布局文件为workspace_cling.xml。视图布局为手机恢复出厂设置(或清空Launcher数据)后第一次进入到workspace时弹出的操作介绍界面,如下图所示:
9). id为folder_cling的子视图:该视图对应的布局文件为folder_cling.xml;视图布局为手机恢复出厂设置(或清空Launcher数据)后第一次打开文件夹时弹出的操作介绍界面,如下图所示: