一.Launcher3概述

Launcher顾名思义,就是桌面的意思,也是android系统启动后第一个启动的应用程序,这里以android11为例,和其他应用并无区别,只是增加了对其他app和widget的管理窗口,且可以为用户定制化一些酷炫和常用的显示功能,代码上比其他app在manifest.xml中多添加一个HOME属性,eg:

<category android:name="android.intent.category.HOME" />

二.Launcher3界面显示

Launcher3的主要界面主要结构有如下几个

1.workspace工作区,主要包括SearchBar、CellLayout、PageIndicator、hotseat

2.所有应用列表

3.Widget

4.Wallpapers

三.Launcher3主要源码分析

1.代码结构

2.java源码分析

1). Launcher.java   //launcher主要的activity,是launcher第一次启动的activity,显示和启动一些初始化的view

  @Overrideprotected void onCreate(Bundle savedInstanceState) {Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,TraceHelper.FLAG_UI_EVENT);if (DEBUG_STRICT_MODE) {StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork()   // or .detectAll() for all detectable problems.penaltyLog().build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());}super.onCreate(savedInstanceState);LauncherAppState app = LauncherAppState.getInstance(this);mOldConfig = new Configuration(getResources().getConfiguration());mModel = app.getModel();mRotationHelper = new RotationHelper(this);InvariantDeviceProfile idp = app.getInvariantDeviceProfile();initDeviceProfile(idp);idp.addOnChangeListener(this);mSharedPrefs = Utilities.getPrefs(this);mIconCache = app.getIconCache();mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);mDragController = new DragController(this);mAllAppsController = new AllAppsTransitionController(this);mStateManager = new StateManager<>(this, NORMAL);mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);mAppWidgetManager = new WidgetManagerHelper(this);mAppWidgetHost = new LauncherAppWidgetHost(this,appWidgetId -> getWorkspace().removeWidget(appWidgetId));mAppWidgetHost.startListening();inflateRootView(R.layout.launcher);setupViews();mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 /*** Finds all the views we need and configure them properly.*/protected void setupViews() {mDragLayer = findViewById(R.id.drag_layer);mFocusHandler = mDragLayer.getFocusIndicatorHelper();mWorkspace = mDragLayer.findViewById(R.id.workspace);mWorkspace.initParentViews(mDragLayer);mOverviewPanel = findViewById(R.id.overview_panel);mHotseat = findViewById(R.id.hotseat);mHotseat.setWorkspace(mWorkspace);// Setup the drag layermDragLayer.setup(mDragController, mWorkspace);mWorkspace.setup(mDragController);// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the// default state, otherwise we will update to the wrong offsets in RTLmWorkspace.lockWallpaperToDefaultPage();mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);mDragController.addDragListener(mWorkspace);// Get the search/delete/uninstall barmDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);// Setup AppsmAppsView = findViewById(R.id.apps_view);// Setup ScrimmScrimView = findViewById(R.id.scrim_view);// Setup the drag controller (drop targets have to be added in reverse order in priority)mDropTargetBar.setup(mDragController);mAllAppsController.setupViews(mAppsView, mScrimView);}

2). Workspace.java //继承自PagedView,由N个cellLayout组成,从cellLayout更高一级的层面上对事件的处理

/*** Used to inflate the Workspace from XML.** @param context The application's context.* @param attrs The attributes set containing the Workspace's customization values.* @param defStyle Unused.*/public Workspace(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mLauncher = Launcher.getLauncher(context);mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);mWallpaperManager = WallpaperManager.getInstance(context);mWallpaperOffset = new WallpaperOffsetInterpolator(this);setHapticFeedbackEnabled(false);initWorkspace();// Disable multitouch across the workspace/all apps/customize traysetMotionEventSplittingEnabled(true);setOnTouchListener(new WorkspaceTouchListener(mLauncher, this));mStatsLogManager = StatsLogManager.newInstance(context);}

3).DeviceProfile.java //icon大小、各个icon间距,布局等计算实体类,可配置各个参数的全局变量

DeviceProfile(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info,Point minSize, Point maxSize, int width, int height, boolean isLandscape,boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,Point windowPosition) {this.inv = inv;this.isLandscape = isLandscape;this.isMultiWindowMode = isMultiWindowMode;windowX = windowPosition.x;windowY = windowPosition.y;// Determine sizes.widthPx = width;heightPx = height;if (isLandscape) {availableWidthPx = maxSize.x;availableHeightPx = minSize.y;} else {availableWidthPx = minSize.x;availableHeightPx = maxSize.y;}mInfo = info;// Constants from resourcesfloat swDPs = Utilities.dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);isTablet = swDPs >= TABLET_MIN_DPS;isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS;isPhone = !isTablet && !isLargeTablet;aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;// Some more constantsthis.transposeLayoutWithOrientation = transposeLayoutWithOrientation;context = getContext(context, info, isVerticalBarLayout()? Configuration.ORIENTATION_LANDSCAPE: Configuration.ORIENTATION_PORTRAIT);final Resources res = context.getResources();edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;int cellLayoutPadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);if (isLandscape) {cellLayoutPaddingLeftRightPx = 0;cellLayoutBottomPaddingPx = cellLayoutPadding;} else {cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier * cellLayoutPadding;cellLayoutBottomPaddingPx = 0;}workspacePageIndicatorHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_height);mWorkspacePageIndicatorOverlapWorkspace =res.getDimensionPixelSize(R.dimen.workspace_page_indicator_overlap_workspace);iconDrawablePaddingOriginalPx =res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);workspaceSpringLoadedBottomSpace =res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);hotseatBarTopPaddingPx =res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);hotseatBarBottomPaddingPx = (isTallDevice ? 0: res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))+ res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);hotseatBarSidePaddingEndPx =res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);// Add a bit of space between nav bar and hotseat in vertical bar layout.hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics)+ (isVerticalBarLayout()? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx): (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)+ hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx));// Calculate all of the remaining variables.updateAvailableDimensions(res);// Now that we have all of the variables calculated, we can tune certain sizes.if (!isVerticalBarLayout() && isPhone && isTallDevice) {// We increase the hotseat size when there is extra space.// ie. For a display with a large aspect ratio, we can keep the icons on the workspace// in portrait mode closer together by adding more height to the hotseat.// Note: This calculation was created after noticing a pattern in the design spec.int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2- workspacePageIndicatorHeight;hotseatBarSizePx += extraSpace;hotseatBarBottomPaddingPx += extraSpace;// Recalculate the available dimensions using the new hotseat size.updateAvailableDimensions(res);}updateWorkspacePadding();// This is done last, after iconSizePx is calculated above.mDotRendererWorkSpace = new DotRenderer(iconSizePx, IconShape.getShapePath(),IconShape.DEFAULT_PATH_SIZE);mDotRendererAllApps = iconSizePx == allAppsIconSizePx ? mDotRendererWorkSpace :new DotRenderer(allAppsIconSizePx, IconShape.getShapePath(),IconShape.DEFAULT_PATH_SIZE);}

4).BubbleTextView.java //继承自TextView,Launcher所有各个icon间距文字显示的父类,包括文字的大小,文字的刷新

public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mActivity = ActivityContext.lookupContext(context);TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.BubbleTextView, defStyle, 0);mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);DeviceProfile grid = mActivity.getDeviceProfile();mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);final int defaultIconSize;if (mDisplay == DISPLAY_WORKSPACE) {//判断显示是不是在工作区setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);setCompoundDrawablePadding(grid.iconDrawablePaddingPx);defaultIconSize = grid.iconSizePx;} else if (mDisplay == DISPLAY_ALL_APPS) {//判断显示是不是在所有应用setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);defaultIconSize = grid.allAppsIconSizePx;} else if (mDisplay == DISPLAY_FOLDER) {//判断显示是不是在文件夹setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);defaultIconSize = grid.folderChildIconSizePx;} else {// widget_selection or shortcut_popupdefaultIconSize = grid.iconSizePx;}mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,defaultIconSize);a.recycle();mLongPressHelper = new CheckLongPressHelper(this);mDotParams = new DotRenderer.DrawParams();setEllipsize(TruncateAt.END);setAccessibilityDelegate(mActivity.getAccessibilityDelegate());setTextAlpha(1f);}

5).CellLayout.java  //继承自viewgroup,Launcher布局的计算类,图标的显示边距等,组成workspace的view,既是一个dragSource又是一个dropTarget,可以将它里面的item拖出去,也可以容纳拖动过来的item。在workspace_screen里面定了一些它的view参数

 public CellLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);mContainerType = a.getInteger(R.styleable.CellLayout_containerType, WORKSPACE);a.recycle();// A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show// the user where a dragged item will land when dropped.setWillNotDraw(false);setClipToPadding(false);mActivity = ActivityContext.lookupContext(context);DeviceProfile grid = mActivity.getDeviceProfile();mCellWidth = mCellHeight = -1;mFixedCellWidth = mFixedCellHeight = -1;mCountX = grid.inv.numColumns;mCountY = grid.inv.numRows;mOccupied =  new GridOccupancy(mCountX, mCountY);mTmpOccupied = new GridOccupancy(mCountX, mCountY);mPreviousReorderDirection[0] = INVALID_DIRECTION;mPreviousReorderDirection[1] = INVALID_DIRECTION;mFolderLeaveBehind.mDelegateCellX = -1;mFolderLeaveBehind.mDelegateCellY = -1;setAlwaysDrawnWithCacheEnabled(false);final Resources res = getResources();mBackground = res.getDrawable(R.drawable.bg_celllayout);mBackground.setCallback(this);mBackground.setAlpha(0);mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx);// Initialize the data structures used for the drag visualization.mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease outmDragCell[0] = mDragCell[1] = -1;for (int i = 0; i < mDragOutlines.length; i++) {mDragOutlines[i] = new Rect(-1, -1, -1, -1);}mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));// When dragging things around the home screens, we show a green outline of// where the item will land. The outlines gradually fade out, leaving a trail// behind the drag path.// Set up all the animations that are used to implement this fading.final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime);final float fromAlphaValue = 0;final float toAlphaValue = (float)res.getInteger(R.integer.config_dragOutlineMaxAlpha);Arrays.fill(mDragOutlineAlphas, fromAlphaValue);for (int i = 0; i < mDragOutlineAnims.length; i++) {final InterruptibleInOutAnimator anim =new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);anim.getAnimator().setInterpolator(mEaseOutInterpolator);final int thisIndex = i;anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {public void onAnimationUpdate(ValueAnimator animation) {final Bitmap outline = (Bitmap)anim.getTag();// If an animation is started and then stopped very quickly, we can still// get spurious updates we've cleared the tag. Guard against this.if (outline == null) {if (LOGD) {Object val = animation.getAnimatedValue();Log.d(TAG, "anim " + thisIndex + " update: " + val +", isStopped " + anim.isStopped());}// Try to prevent it from continuing to runanimation.cancel();} else {mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();CellLayout.this.invalidate(mDragOutlines[thisIndex]);}}});// The animation holds a reference to the drag outline bitmap as long is it's// running. This way the bitmap can be GCed when the animations are complete.anim.getAnimator().addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {anim.setTag(null);}}});mDragOutlineAnims[i] = anim;}mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);addView(mShortcutsAndWidgets);}

6).FolderGridOrganizer.java //展开文件夹显示的计算逻辑类,文件夹图标呈现是网格状,此类主要给文件夹各应用图标制定显示规则,eg: 3*3 、4*4

 /*** Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.*/public FolderGridOrganizer(InvariantDeviceProfile profile) {mMaxCountX = profile.numFolderColumns;mMaxCountY = profile.numFolderRows;mMaxItemsPerPage = mMaxCountX * mMaxCountY;}/*** Updates the organizer with the provided folder info*/public FolderGridOrganizer setFolderInfo(FolderInfo info) {return setContentSize(info.contents.size());}

7). LoaderTask.java //继承Runnable,加载各个模块Task的显示类,如workspace工作区icon、所有应用icon的初始化工作

public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,LoaderResults results) {mApp = app;mBgAllAppsList = bgAllAppsList;mBgDataModel = dataModel;mResults = results;mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);mUserManager = mApp.getContext().getSystemService(UserManager.class);mUserCache = UserCache.INSTANCE.get(mApp.getContext());mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());mIconCache = mApp.getIconCache();}

8).PackageUpdatedTask.java //继承BaseModelUpdateTask,实际也是Runnable,PMS安装应用后更新Launcher图标及逻辑的实现类

 * or when a user availability changes.*/
public class PackageUpdatedTask extends BaseModelUpdateTask {private static final boolean DEBUG = false;private static final String TAG = "PackageUpdatedTask";public static final int OP_NONE = 0;public static final int OP_ADD = 1;public static final int OP_UPDATE = 2;public static final int OP_REMOVE = 3; // uninstalledpublic static final int OP_UNAVAILABLE = 4; // external media unmountedpublic static final int OP_SUSPEND = 5; // package suspendedpublic static final int OP_UNSUSPEND = 6; // package unsuspendedpublic static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable

9). BaseIconFactory.java //Launcher icon的工厂类,控制icon UI展示(eg:图标白边控制)

 protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize,boolean shapeDetection) {mContext = context.getApplicationContext();mShapeDetection = shapeDetection;mFillResIconDpi = fillResIconDpi;mIconBitmapSize = iconBitmapSize;mPm = mContext.getPackageManager();mColorExtractor = new ColorExtractor();mCanvas = new Canvas();mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));clear();}

10). SecondaryDropTarget.java //继承自ButtonDropTarget,实际也是TextView,长按APP icon的操作类,对icon进行移动、删除、移除、取消、卸载等操作

  public SecondaryDropTarget(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mCacheExpireAlarm = new Alarm();mStatsLogManager = StatsLogManager.newInstance(context);}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();if (mHadPendingAlarm) {mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);mCacheExpireAlarm.setOnAlarmListener(this);mHadPendingAlarm = false;}}

11).PortraitStatesTouchController.java //继承自AbstractStateChangeTouchController,纵向控制抽屉式All应用界面的触摸类,用于处理纵向UI中各种状态转换

public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {super(l, SingleAxisSwipeDetector.VERTICAL);mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);mAllowDragToOverview = allowDragToOverview;}@Overrideprotected boolean canInterceptTouch(MotionEvent ev) {if (mCurrentAnimation != null) {if (mFinishFastOnSecondTouch) {mCurrentAnimation.getAnimationPlayer().end();}AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {// If we are already animating from a previous state, we can intercept as long as// the touch is below the current all apps progress (to allow for double swipe).return true;}// Otherwise, make sure everything is settled and don't intercept so they can scroll// recents, dismiss a task, etc.if (mAtomicAnim != null) {mAtomicAnim.end();}return false;}if (mLauncher.isInState(ALL_APPS)) {// In all-apps only listen if the container cannot scroll itselfif (!mLauncher.getAppsView().shouldContainerScroll(ev)) {return false;}} else if (mLauncher.isInState(OVERVIEW)) {if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) {return false;}} else {// If we are swiping to all apps instead of overview, allow it from anywhere.boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;// For all other states, only listen if the event originated below the hotseat heightif (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {return false;}}if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {return false;}return true;}

12).OverviewToAllAppsTouchController.java //继承自PortraitStatesTouchController,横向控制抽屉式All应用界面的触摸控制器

 public OverviewToAllAppsTouchController(Launcher l) {super(l, true /* allowDragToOverview */);}@Overrideprotected boolean canInterceptTouch(MotionEvent ev) {if (mCurrentAnimation != null) {// If we are already animating from a previous state, we can intercept.return true;}if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {return false;}if (mLauncher.isInState(ALL_APPS)) {// In all-apps only listen if the container cannot scroll itselfreturn mLauncher.getAppsView().shouldContainerScroll(ev);} else if (mLauncher.isInState(NORMAL)) {return (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0;} else if (mLauncher.isInState(OVERVIEW)) {RecentsView rv = mLauncher.getOverviewPanel();return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());} else {return false;}}@Overrideprotected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {if (fromState == ALL_APPS && !isDragTowardPositive) {// Should swipe down go to OVERVIEW instead?return TouchInteractionService.isConnected() ?mLauncher.getStateManager().getLastState() : NORMAL;} else if (isDragTowardPositive) {return ALL_APPS;}return fromState;}

3.部分xml文件解析

1).device_profiles.xml //默认Launcher的网格配置,主要包括一下几点
1'. numRows/numColumns //workspace的行和列
2'.numFolderRows/numFolderColumns //文件夹中配置的行和列
3'.iconImageSize //图标大小
4'.iconTextSize// 图标名称文字大小
5'.numHotseatIcons //hotseat图标的数目
6'.defaultLayoutId //默认选择加载哪个网格xml的配置文件

<?xml version="1.0" encoding="utf-8"?>
<!--Copyright (C) 2016 The Android Open Source ProjectLicensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.
--><profiles xmlns:launcher="http://schemas.android.com/apk/res-auto" ><grid-optionlauncher:name="3_by_3"launcher:numRows="3"launcher:numColumns="3"launcher:numFolderRows="2"launcher:numFolderColumns="3"launcher:numHotseatIcons="3"launcher:dbFile="launcher_3_by_3.db"launcher:defaultLayoutId="@xml/default_workspace_3x3" ><display-optionlauncher:name="Super Short Stubby"launcher:minWidthDps="255"launcher:minHeightDps="300"launcher:iconImageSize="48"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /><display-optionlauncher:name="Shorter Stubby"launcher:minWidthDps="255"launcher:minHeightDps="400"launcher:iconImageSize="48"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /></grid-option><grid-optionlauncher:name="4_by_4"launcher:numRows="4"launcher:numColumns="4"launcher:numFolderRows="3"launcher:numFolderColumns="4"launcher:numHotseatIcons="4"launcher:dbFile="launcher_4_by_4.db"launcher:defaultLayoutId="@xml/default_workspace_4x4" ><display-optionlauncher:name="Short Stubby"launcher:minWidthDps="275"launcher:minHeightDps="420"launcher:iconImageSize="48"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /><display-optionlauncher:name="Stubby"launcher:minWidthDps="255"launcher:minHeightDps="450"launcher:iconImageSize="48"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /><display-optionlauncher:name="Nexus S"launcher:minWidthDps="296"launcher:minHeightDps="491.33"launcher:iconImageSize="48"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /><display-optionlauncher:name="Nexus 4"launcher:minWidthDps="359"launcher:minHeightDps="567"launcher:iconImageSize="54"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /><display-optionlauncher:name="Nexus 5"launcher:minWidthDps="335"launcher:minHeightDps="567"launcher:iconImageSize="54"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /></grid-option><grid-optionlauncher:name="5_by_5"launcher:numRows="5"launcher:numColumns="5"launcher:numFolderRows="4"launcher:numFolderColumns="4"launcher:numHotseatIcons="5"launcher:dbFile="launcher.db"launcher:defaultLayoutId="@xml/default_workspace_5x5" ><display-optionlauncher:name="Large Phone"launcher:minWidthDps="406"launcher:minHeightDps="694"launcher:iconImageSize="56"launcher:iconTextSize="14.4"launcher:canBeDefault="true" /><display-optionlauncher:name="Shorter Stubby"launcher:minWidthDps="255"launcher:minHeightDps="400"launcher:iconImageSize="48"launcher:iconTextSize="13.0"launcher:canBeDefault="true" /></grid-option></profiles>

2).default_workspace_xxx.xml //默认排序各个icon位置的配置文件,包括文件夹默认创建显示及位置

<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"><!-- Hotseat (We use the screen as the position of the item in the hotseat) --><!-- Messaging, [All Apps], Dialer --><resolvelauncher:container="-101"launcher:screen="0"launcher:x="0"launcher:y="0" ><favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" /><favorite launcher:uri="sms:" /><favorite launcher:uri="smsto:" /><favorite launcher:uri="mms:" /><favorite launcher:uri="mmsto:" /></resolve><!-- All Apps --><resolvelauncher:container="-101"launcher:screen="2"launcher:x="2"launcher:y="0" ><favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" /><favorite launcher:uri="tel:123" /><favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" /></resolve><!-- Bottom row --><resolvelauncher:screen="0"launcher:x="0"launcher:y="-1" ><favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" /><favorite launcher:uri="mailto:" /></resolve><resolvelauncher:screen="0"launcher:x="1"launcher:y="-1" ><favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" /><favorite launcher:uri="#Intent;type=images/*;end" /></resolve><resolvelauncher:screen="0"launcher:x="2"launcher:y="-1" ><favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" /><favorite launcher:uri="market://details?id=com.android.launcher" /></resolve></favorites>

screen  //表示第几屏,eg: 0表示第一屏

x  //表示横向的位置,eg: 0表示第一列

y  //表示纵向的位置,eg: 0表示第一行

3).folder_shapes.xml //Workspace工作区icon的圆角大小控制配置文件

<shapes xmlns:launcher="http://schemas.android.com/apk/res-auto" ><Circle launcher:folderIconRadius="1" /><!-- Default icon for AOSP --><RoundedSquare launcher:folderIconRadius="0.16" /><!-- Rounded icon from RRO --><RoundedSquare launcher:folderIconRadius="0.6" /><!-- Square icon --><RoundedSquare launcher:folderIconRadius="0" /><TearDrop launcher:folderIconRadius="0.3" /><Squircle launcher:folderIconRadius="0.2" /></shapes>

Android Launcher3简介相关推荐

  1. Android Launcher3分析——开篇

    Android Launcher3分析--开篇 简介 Launcher就是一个Activity,Launcher的源码中也是继承的Activity.直观体现就是手机的桌面,当我们打开手机的时候,手机的 ...

  2. 【译】Android系统简介—— Activity

    续上一篇,继续介绍Android系统.上一篇: [译]Android系统简介 本文主要介绍构建Android应用的一些主要概念: Activity Activity是应用程序中一个单独的有UI的页面( ...

  3. Android ViewTreeObserver简介-------------转

    Android ViewTreeObserver简介 一.结构 public final class ViewTreeObserver extends Object java.lang.Object ...

  4. android launcher3源码分析,Android Launcher3源码分析与修改

    Launcher和Setting是客户需求经常改动的地方,不过其代码量也不容小觑.今天就初略来看一下,以下内容都是本人查阅资料加上自己的理解得出,由于自己水平有限,如果误导还请指出: 先从Androi ...

  5. android radiooptions简介

    android radiooptions简介 RILD负责modem和RILJ端的通信,信息分两种:unsolicited和solicited,前者是由modem主动上报的,诸如时区更新.通话状态.网 ...

  6. Android 的简介和体系结构中每个层的功能。

    Android 的简介和体系结构中每个层的功能. 1.简介 Android是由Google公司和开放手机联盟领导并开发的一种基于Linux的自由且开放源代码的操作系统,主要使用于移动设备. Andro ...

  7. Android字体简介

    Android字体简介 Android系统默认支持三种字体,分别为:"sans","serif","monospace". android. ...

  8. Android OkHttp3简介和使用详解

    一 OKHttp简介 OKHttp是一个处理网络请求的开源项目,Android 当前最火热网络框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpC ...

  9. android 教程概要,Android精通教程-第一节Android入门简介

    前言 大家好,我是 Vic,今天给大家带来Android精通教程-第一节Android入门简介的概述,希望你们喜欢 每日一句 If life were predictable it would cea ...

最新文章

  1. [译][Tkinter 教程10] Text 控件
  2. ITK:在图像中存储非像素数据
  3. JavaScript之面向对象学习四原型对象的动态性
  4. Linux 命令之 rcp -- 远程文件复制
  5. android天气预报实训程序清单,Android天气预报项目
  6. [我研究] A TAXONOMY OF SECURITY FAULTS IN THE UNIX OPERATING SYSTEM - Master Thesis
  7. Notepad++使用技巧
  8. 九个PHP很有用的功能
  9. c语言强化训练作业整理1
  10. 【jackson】@JsonDeserialize 和 @JsonSerialize
  11. android代码无法访问data目录,解决Android7.1.1中无法打开/data目录的问题
  12. 日语学习  「そっと」 和 「こっそり」 的区別
  13. 游戏筑基之选择分支语句(C语言)
  14. nexus配置第三方库文件
  15. python如何读取二进制文件为图片_python之读取二进制文件
  16. 斯坦福课程Knowledge Graphs-What is a Knowledge Graph?
  17. APM代码调试知识点汇总
  18. 【Life】 Never Too Late, Just Do it Better!
  19. 从0到一开发微信小程序(2)——开发第一个小程序
  20. fiddler使用过滤、打断点方法

热门文章

  1. 最全动态规划题型详解
  2. h5前端开发培训,html5学习笔记
  3. 论文研读 —— 7. Very Deep Convolutional Networks for Large-Scale Image Recognition (2/3)
  4. 汇编——十进制数据输入转二进制/十六进制输出(含数字判断,不限位数)
  5. Dubbo系列之Dubbo原理简介
  6. “+智能”时代 华为如何引领中国制造走向“智”变?
  7. 操作Linux软链接引起的各种问题
  8. [预训练语言模型专题] MT-DNN(KD) : 预训练、多任务、知识蒸馏的结合
  9. CEPH(详解+配置)
  10. 解决qt5在windows系统下中文乱码的问题的简单方法