赞
踩
当我们手指触摸屏幕上指定App图标Logo的时候,App就由Launcher开始启动了。
Launcher到底是一个概念呢?
Launcher本质上也是一个应用程序,和我们的App一样,也是继承自Activity
packages/apps/Launcher2/src/com/android/launcher2/Launcher.java
- public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
- View.OnTouchListener {
- }
Launcher类中实现了点击OnClickListener、长按OnLongClickListener等回调接口,来接收用户的输入操作。
比如,我们点击一个图标,是怎么打来应用呢?
通过捕捉图标对应的点击事件操作,然后调用startActivity()发送对应的Intent请求,Launcher就是这么做的。
那么到底是处理的哪个对象的点击事件呢?既然Launcher是App,并且有界面,那么肯定有布局文件,是的,我找到了布局文件launcher.xml
- <FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:id="@+id/launcher">
-
- <com.android.launcher2.DragLayer
- android:id="@+id/drag_layer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <!-- Keep these behind the workspace so that they are not visible when
- we go into AllApps -->
- //底部操作栏和上面图标布局的分割线
- <include
- android:id="@+id/dock_divider"
- layout="@layout/workspace_divider"
- android:layout_marginBottom="@dimen/button_bar_height"
- android:layout_gravity="bottom" />
-
- //页面指示器
- <include
- android:id="@+id/paged_view_indicator"
- layout="@layout/scroll_indicator"
- android:layout_gravity="bottom"
- android:layout_marginBottom="@dimen/button_bar_height" />
-
- <!-- The workspace contains 5 screens of cells -->
- //包含了5个屏幕的单元格。类似于手机首页 底部打电话 短信 浏览器 通讯录等底部按钮
- <com.android.launcher2.Workspace
- android:id="@+id/workspace"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="@dimen/workspace_left_padding"
- android:paddingEnd="@dimen/workspace_right_padding"
- android:paddingTop="@dimen/workspace_top_padding"
- android:paddingBottom="@dimen/workspace_bottom_padding"
- launcher:defaultScreen="2"
- launcher:cellCountX="@integer/cell_count_x"
- launcher:cellCountY="@integer/cell_count_y"
- 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" />
- <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>
-
- ...ignore some code...
-
- </com.android.launcher2.DragLayer>
- </FrameLayout>
从上面这些我们应该可以看出一些东西来:Launcher大量使用include标签来实现界面的复用,而且定义了很多的自定义控件实现界面效果,dock_divider从布局的参数声明上可以猜出,是底部操作栏和上面图标布局的分割线,而paged_view_indicator则是页面指示器,和App首次进入的引导页下面的界面引导是一样的道理。当然,我们最关心的是Workspace这个布局,因为注释里面说在这里面包含了5个屏幕的单元格,想必你也猜到了,这个就是在首页存放我们图标的那五个界面(不同的ROM会做不同的DIY,数量不固定)。
workspace_screen布局:
- <com.android.launcher2.CellLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/cell_layout_left_padding"
- android:paddingEnd="@dimen/cell_layout_right_padding"
- android:paddingTop="@dimen/cell_layout_top_padding"
- android:paddingBottom="@dimen/cell_layout_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" />
系统界面布局分析:
1.底部快捷入口View:
就一个CellLayout自定义布局,那么我们就可以猜到了,既然可以存放图标,那么这个自定义的布局很有可能是继承自ViewGroup或者是其子类,实际上,CellLayout确实是继承自ViewGroup。在CellLayout里面,只放了一个子View,那就是ShortcutAndWidgetContainer。从名字也可以看出来,ShortcutAndWidgetContainer这个类就是用来存放快捷图标和Widget小部件的,那么里面放的是什么对象呢?
在桌面上的图标,使用的是BubbleTextView对象,这个对象在TextView的基础之上,添加了一些特效,比如你长按移动图标的时候,图标位置会出现一个背景(不同版本的效果不同),所以我们找到BubbleTextView对象的点击事件,就可以找到Launcher如何开启一个App了。
2.程序列表中的点击:
除了桌面上图标之外,在程序列表中点击图标,也可以开启对应的程序。这里的图标使用的不是BubbleTextView对象,而是PagedViewIcon对象,我们如果找到它的点击事件,就也可以找到Launcher如何开启一个App。
底部快捷的BubbleTextView对象的点击事件在哪里呢?我来告诉你:在Launcher.onClick(View v)里面。
- /**
- * Launches the intent referred by the clicked shortcut
- */
- public void onClick(View v) {
-
- ...ignore some code...
-
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- // Open shortcut
- final Intent intent = ((ShortcutInfo) tag).intent;
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- intent.setSourceBounds(new Rect(pos[0], pos[1],
- pos[0] + v.getWidth(), pos[1] + v.getHeight()));
- //开始开启Activity咯~
- boolean success = startActivitySafely(v, intent, tag);
-
- if (success && v instanceof BubbleTextView) {
- mWaitingForResume = (BubbleTextView) v;
- mWaitingForResume.setStayPressed(true);
- }
- } else if (tag instanceof FolderInfo) {
- //如果点击的是图标文件夹,就打开文件夹
- if (v instanceof FolderIcon) {
- FolderIcon fi = (FolderIcon) v;
- handleFolderClick(fi);
- }
- } else if (v == mAllAppsButton) {
- ...ignore some code...
- }
- }
程序列表中的点击:
程序列表界面使用的是AppsCustomizePagedView对象,所以我在这个类里面找到了onClick(View v)。
com.android.launcher2.AppsCustomizePagedView.java
- /**
- * The Apps/Customize page that displays all the applications, widgets, and shortcuts.
- */
- public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
- View.OnClickListener, View.OnKeyListener, DragSource,
- PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
- LauncherTransitionable {
-
- @Override
- public void onClick(View v) {
-
- ...ignore some code...
-
- if (v instanceof PagedViewIcon) {
- mLauncher.updateWallpaperVisibility(true);
- mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
- } else if (v instanceof PagedViewWidget) {
- ...ignore some code..
- }
- }
- }
两种方式,殊途同归!
不管从哪里点击图标,调用的都是Launcher.startActivitySafely()方法。
下面一起来看一下Launcher.startActivitySafely()的流程
- boolean startActivitySafely(View v, Intent intent, Object tag) {
- boolean success = false;
- try {
- success = startActivity(v, intent, tag);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
- }
- return success;
- }
-
-
- boolean startActivity(View v, Intent intent, Object tag) {
-
- //Activity会添加到一个新的Task栈
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- boolean useLaunchAnimation = (v != null) &&
- !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
-
- if (useLaunchAnimation) {
- if (user == null || user.equals(android.os.Process.myUserHandle())) {
- startActivity(intent, opts.toBundle());
- } else {
- launcherApps.startMainActivity(intent.getComponent(), user,
- intent.getSourceBounds(),
- opts.toBundle());
- }
- } else {
- if (user == null || user.equals(android.os.Process.myUserHandle())) {
- startActivity(intent);
- } else {
- launcherApps.startMainActivity(intent.getComponent(), user,
- intent.getSourceBounds(), null);
- }
- }
- return true;
- } catch (SecurityException e) {
- ...
- }
- return false;
- }
-
- //实际调用的是 startActivityForResult方法
-
- @Override
- public void startActivity(Intent intent, @Nullable Bundle options) {
- if (options != null) {
- startActivityForResult(intent, -1, options);
- } else {
- // Note we want to go through this call for compatibility with
- // applications that may have overridden the method.
- startActivityForResult(intent, -1);
- }
- }
所以,Launcher开启一个App,其实和我们在Activity中直接startActivity()基本一样,都是调用了Activity.startActivityForResult()。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。