launcher in android
http://blog.csdn.net/liangshengyang/article/details/5905351
Laucher的UI组成
UI组件属性在layout-port/launcher.xml中定义,主要有Workspace和SlidingDrawer两大部分组成,盛放这两者的容器就是DragLayer。
- <com.android.launcher.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="fill_parent"
- android:layout_height="fill_parent">
- <!-- The workspace contains 3 screens of cells -->
- <com.android.launcher.Workspace
- android:id="@+id/workspace"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- launcher:defaultScreen="1">
- <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" />
- </com.android.launcher.Workspace>
- <SlidingDrawer
- android:id="@+id/drawer"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:topOffset="5dip"
- android:bottomOffset="7dip"
- android:handle="@+id/all_apps"
- android:content="@+id/content">
- <com.android.launcher.HandleView
- android:id="@id/all_apps"
- android:layout_width="fill_parent"
- android:layout_height="56dip"
- android:background="@drawable/handle"
- android:focusable="true"
- android:clickable="true"
- android:scaleType="center"
- android:src="@drawable/handle_icon"
- launcher:direction="horizontal" />
- <com.android.launcher.AllAppsGridView
- android:id="@id/content"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- launcher:texture="@drawable/pattern_carbon_fiber_dark"
- android:scrollbarStyle="outsideInset"
- android:drawSelectorOnTop="false"
- android:listSelector="@drawable/grid_selector"
- android:nextFocusLeft="@id/content"
- android:nextFocusDown="@id/content"
- android:nextFocusUp="@id/all_apps"
- android:nextFocusRight="@id/content"
- android:verticalSpacing="10dip"
- android:numColumns="4" />
- </SlidingDrawer>
- <com.android.launcher.DeleteZone
- android:id="@+id/delete_zone"
- android:layout_width="wrap_content"
- android:layout_height="49dip"
- android:scaleType="center"
- android:src="@drawable/ic_delete"
- android:background="@drawable/delete_zone_selector"
- android:layout_gravity="bottom|center_horizontal"
- android:visibility="invisible"
- launcher:direction="horizontal" />
- </com.android.launcher.DragLayer>
Workspace就是启动后进入的桌面,可以左右滑动得到3个(默认值)画面,它可以有背景图片作为墙纸。
SlidingDrawer包括HandleView和AllAppsGridView两部分。在屏幕为竖直时(Portait),在layout-port/launcher.xml文件里定义的高度56dip,宽度则充满充满整个屏幕,其背景图片在文件drawable/handle.xml中指定,见上面的layout文件中的定义。文件drawable/handle.xml:
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/tray_handle_normal" />
- <item android:state_pressed="true" android:drawable="@drawable/tray_handle_pressed" />
- <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/tray_handle_selected" />
- <item android:state_enabled="true" android:drawable="@drawable/tray_handle_normal" />
- <item android:state_focused="true" android:drawable="@drawable/tray_handle_selected" />
- </selector>
对于竖直方式来说则是诸如图片res/drawable-port/tray_handle_normal.png。前景图标源则是在
drawable/handle_icon.xml(见上面的layout文件),该文件为内容为:
- <transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/ic_tray_expand" />
- <item android:drawable="@drawable/ic_tray_collapse" />
- </transition>
点击或滑动HandleView中的按钮图标,则可显示AllAppsGridView,它包含了系统中的应用程序。再点击或滑动按钮,则隐藏AllAppsGridView。
在Launcher的onCreate函数中会调用setupViews来初始化各个View类,初始化布局则是由上面的layout文件中定义。setupViews函数代码如下:
- /**
- * Finds all the views we need and configure them properly.
- */
- private void setupViews() {
- mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
- final DragLayer dragLayer = mDragLayer;
- mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
- final Workspace workspace = mWorkspace;
- mDrawer = (SlidingDrawer) dragLayer.findViewById(R.id.drawer);
- final SlidingDrawer drawer = mDrawer;
- mAllAppsGrid = (AllAppsGridView) drawer.getContent();
- final AllAppsGridView grid = mAllAppsGrid;
- final DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
- mHandleView = (HandleView) drawer.findViewById(R.id.all_apps);
- mHandleView.setLauncher(this);
- mHandleIcon = (TransitionDrawable) mHandleView.getDrawable();
- mHandleIcon.setCrossFadeEnabled(true);
- drawer.lock();
- final DrawerManager drawerManager = new DrawerManager();
- drawer.setOnDrawerOpenListener(drawerManager);
- drawer.setOnDrawerCloseListener(drawerManager);
- drawer.setOnDrawerScrollListener(drawerManager);
- grid.setTextFilterEnabled(false);
- grid.setDragger(dragLayer);
- grid.setLauncher(this);
- workspace.setOnLongClickListener(this);
- workspace.setDragger(dragLayer);
- workspace.setLauncher(this);
- loadWallpaper();
- deleteZone.setLauncher(this);
- deleteZone.setDragController(dragLayer);
- deleteZone.setHandle(mHandleView);
- dragLayer.setIgnoredDropTarget(grid);
- dragLayer.setDragScoller(workspace);
- dragLayer.setDragListener(deleteZone);
- }
其中DeleteZone用于:当长按workspace中的图标时会进入拖拽功能,可以将它们托到HandleView中,将它们从workspace中删除。
App的装载
在onCreate函数中,有如下这2行代码,用于装载应用程序信息,
- if (!mRestoring) {
- startLoaders();
- }
startLoaders会调用LauncherModel的loadApplications函数:
- private void startLoaders() {
- boolean loadApplications = sModel.loadApplications(true, this, mLocaleChanged);
- sModel.loadUserItems(!mLocaleChanged, this, mLocaleChanged, loadApplications);
- mRestoring = false;
- }
第一行装载应用程序包,第二行的函数装载workspace中用户自定义添加到ShortCut,LiveFolder,WidgetApp等。
在loadApplications中,需要注意2个地方,一是创建ApplicationsAdapter,它将应用程序数组列表ArrayList<ApplicationInfo> mApplications和View关联起来。二是继续调用函数startApplicationsLoaderLocked:
- /**
- * Loads the list of installed applications in mApplications.
- *
- * @return true if the applications loader must be started
- * (see startApplicationsLoader()), false otherwise.
- */
- synchronized boolean loadApplications(boolean isLaunching, Launcher launcher,
- boolean localeChanged) {
- if (DEBUG_LOADERS) d(LOG_TAG, "load applications");
- if (isLaunching && mApplicationsLoaded && !localeChanged) {
- mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
- if (DEBUG_LOADERS) d(LOG_TAG, " --> applications loaded, return");
- return false;
- }
- stopAndWaitForApplicationsLoader();
- if (localeChanged) {
- dropApplicationCache();
- }
- if (mApplicationsAdapter == null || isLaunching || localeChanged) {
- mApplications = new ArrayList<ApplicationInfo>(DEFAULT_APPLICATIONS_NUMBER);
- mApplicationsAdapter = new ApplicationsAdapter(launcher, mApplications);
- }
- mApplicationsLoaded = false;
- if (!isLaunching) {
- startApplicationsLoaderLocked(launcher, false);
- return false;
- }
- return true;
- }
再来看看startApplicationsLoaderLocked,它实际上启动一个线程去装载应用程序信息:
- private void startApplicationsLoaderLocked(Launcher launcher, boolean isLaunching) {
- if (DEBUG_LOADERS) d(LOG_TAG, " --> starting applications loader");
- stopAndWaitForApplicationsLoader();
- mApplicationsLoader = new ApplicationsLoader(launcher, isLaunching);
- mApplicationsLoaderThread = new Thread(mApplicationsLoader, "Applications Loader");
- mApplicationsLoaderThread.start();
- }
它创建一个线程执行体Runnable对象mApplicationsLoader,然后在调用mApplicationsLoaderThread.start()时会执行ApplicationsLoader. run():
- public void run() {
- if (DEBUG_LOADERS) d(LOG_TAG, " ----> running applications loader (" + mId + ")");
- // Elevate priority when Home launches for the first time to avoid
- // starving at boot time. Staring at a blank home is not cool.
- android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT :
- Process.THREAD_PRIORITY_BACKGROUND);
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final Launcher launcher = mLauncher.get();
- final PackageManager manager = launcher.getPackageManager();
- final List<ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
- if (apps != null && !mStopped) {
- final int count = apps.size();
- // Can be set to null on the UI thread by the unbind() method
- // Do not access without checking for null first
- final ApplicationsAdapter applicationList = mApplicationsAdapter;
- ChangeNotifier action = new ChangeNotifier(applicationList, true);
- final HashMap<ComponentName, ApplicationInfo> appInfoCache = mAppInfoCache;
- for (int i = 0; i < count && !mStopped; i++) {
- ResolveInfo info = apps.get(i);
- ApplicationInfo application =
- makeAndCacheApplicationInfo(manager, appInfoCache, info, launcher);
- if (action.add(application) && !mStopped) {
- launcher.runOnUiThread(action);
- action = new ChangeNotifier(applicationList, false);
- }
- }
- launcher.runOnUiThread(action);
- }
- if (!mStopped) {
- mApplicationsLoaded = true;
- } else {
- if (DEBUG_LOADERS) d(LOG_TAG, " ----> applications loader stopped (" + mId + ")");
- }
- mRunning = false;
- }
- }
在该线程中,它使用PackageManager获取安装的应用程序。
在装载完workspace和AllGridView中的各项后,onDesktopItemsLoaded将被调用,它会绑定桌面的Shortcut及appWidgets:
- void onDesktopItemsLoaded(ArrayList<ItemInfo> shortcuts,
- ArrayList<LauncherAppWidgetInfo> appWidgets) {
- if (mDestroyed) {
- if (LauncherModel.DEBUG_LOADERS) {
- d(LauncherModel.LOG_TAG, " ------> destroyed, ignoring desktop items");
- }
- return;
- }
- bindDesktopItems(shortcuts, appWidgets);
- }
绑定过程
绑定过程包括ApplicationAdapter(即ArrayAapter的子类)与AllGridView的绑定,实现数据到View的显示。
- private void bindDesktopItems(ArrayList<ItemInfo> shortcuts,
- ArrayList<LauncherAppWidgetInfo> appWidgets) {
- final ApplicationsAdapter drawerAdapter = sModel.getApplicationsAdapter();
- if (shortcuts == null || appWidgets == null || drawerAdapter == null) {
- if (LauncherModel.DEBUG_LOADERS) d(LauncherModel.LOG_TAG, " ------> a source is null");
- return;
- }
- mBinder = new DesktopBinder(this, shortcuts, appWidgets, drawerAdapter);
- mBinder.startBindingItems();
- }
DesktopBinder的构造函数如下,指定了Adapter,实现了数据到view的绑定:
- DesktopBinder(Launcher launcher, ArrayList<ItemInfo> shortcuts,
- ArrayList<LauncherAppWidgetInfo> appWidgets,
- ApplicationsAdapter drawerAdapter) {
- mLauncher = new
- WeakReference<Launcher>(launcher);
- mShortcuts = shortcuts;
- mDrawerAdapter = drawerAdapter;
- ...
在函数bindDrawer 中为view指定了adapter:
- private void bindDrawer(Launcher.DesktopBinder binder,
- ApplicationsAdapter drawerAdapter) {
- mAllAppsGrid.setAdapter(drawerAdapter);
- binder.startBindingAppWidgetsWhenIdle();
- }
其它
LauncherModel提供了添加/删除/更新应用程序包的功能 。
Launcher还提供了对ShortCut 、AppWidget、LiveFolder和搜索框等的管理功能,墙纸管理功能。默认的墙纸也是存放在该程序包的资源中。