【源码剖析】Launcher 8.0 源码 (19) --- Launcher 启动流程 第六步之BindWorkspace 第1小步创建屏幕

Launcher8.0启动流程的第六步startLoader的第一步Loadworkspace完成,接着是第二步bindworkspace。 这一步将sBgDataModel中的图标放到桌面上。 放置的时候需要先创建屏幕而后才能将图标放到屏幕里。

 

源码如下:

private void bindWorkspace(int synchronizeBindPage) {
    final long t = SystemClock.uptimeMillis();
    Runnable r;

//启动流程Launcher调用LauncherModel的方法startLoader,而LauncherModel又会调用Launcher的方法来完成startLoader的操作。在Launcher的启动流程的第三步创建必须的对象中创建的第二个对象是LauncherModel的对象,在创建的时候传入了Launcher本身,而这个Launcher的对象在LauncherMedel类里面叫做Callbacks。随后的方法  final Callbacks oldCallbacks = mCallbacks.get();就是获取Launcher对象。


    final Callbacks oldCallbacks = mCallbacks.get();
    if (oldCallbacks == null) {
        Log.w(TAG, "LoaderTask running with no launcher");
        return;
    }

//随后将sBgDataModel里面存储的桌面布局信息放到LauncherModel的集合里面,这样方便使用这些信息。一共创建了三个信息,屏幕数,桌面图标,桌面widget。后面将按照屏幕数、桌面图标、桌面widget依次绘制。

ArrayList workspaceItems = new ArrayList<>();
    ArrayList appWidgets = new ArrayList<>();
    ArrayList orderedScreenIds = new ArrayList<>();

    synchronized (sBgDataModel) {
        workspaceItems.addAll(sBgDataModel.workspaceItems);
        appWidgets.addAll(sBgDataModel.appWidgets);
        orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
    }
//图标信息到位之后,先找到当前屏幕。获取屏幕的id,屏幕的id是0,1,2这个顺序,且严格按照这个顺序。比如Id为1,则必定是从左往右的第2个屏幕。在图标信息iteminfo里面存有每个图标的screenid信息。
    final int currentScreen;
    {
        int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
                ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
        if (currScreen >= orderedScreenIds.size()) {
            // There may be no workspace screens (just hotseat items and an empty page).
            currScreen = PagedView.INVALID_RESTORE_PAGE;
        }
        currentScreen = currScreen;
    }
    final boolean validFirstPage = currentScreen >= 0;
    final long currentScreenId =
            validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;

//将图标分为在当前屏幕和没有在当前屏幕,且由于widget和其他类型的文件有巨大差异,如内容提供方和占空间大小。所以,widget和其他分为两类。
    ArrayList currentWorkspaceItems = new ArrayList<>();
    ArrayList otherWorkspaceItems = new ArrayList<>();
    ArrayList currentAppWidgets = new ArrayList<>();
    ArrayList otherAppWidgets = new ArrayList<>();
    filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
            otherWorkspaceItems);
    filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
            otherAppWidgets);

 

//区分是否在当前屏幕的方法filterCurrentWorkspaceItems,通过比较if (info.screenId == currentScreenId) 来确定是否在当前屏幕,源码如下:

private void filterCurrentWorkspaceItems(long currentScreenId,
        ArrayList allWorkspaceItems,
        ArrayList currentScreenItems,
        ArrayList otherScreenItems) {
    Iterator iter = allWorkspaceItems.iterator();
    while (iter.hasNext()) {
        ItemInfo i = iter.next();
        if (i == null) {
            iter.remove();
        }
    }

    Set itemsOnScreen = new HashSet();
    Collections.sort(allWorkspaceItems, new Comparator() {
        @Override
        public int compare(ItemInfo lhs, ItemInfo rhs) {
            return Utilities.longCompare(lhs.container, rhs.container);
        }
    });
    for (ItemInfo info : allWorkspaceItems) {
        if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
            if (info.screenId == currentScreenId) {
                currentScreenItems.add(info);
                itemsOnScreen.add(info.id);
            } else {
                otherScreenItems.add(info);
            }
        } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
            currentScreenItems.add(info);
            itemsOnScreen.add(info.id);
        } else {
            if (itemsOnScreen.contains(info.container)) {
                currentScreenItems.add(info);
                itemsOnScreen.add(info.id);
            } else {
                otherScreenItems.add(info);
            }
        }
    }
}

 

//然后将图标进行整理,将图标从上到下从左到右按顺序排好,因为图标的显示始终是一个一个依次显示,虽然速度很快,但是在手机卡顿的时候,难免第一个图标和最后一个图标还是能被人感知。如果有顺序的显示,用户体验会好很多。
    sortWorkspaceItemsSpatially(currentWorkspaceItems);
    sortWorkspaceItemsSpatially(otherWorkspaceItems);

//这里调用了Launcher的startBinding方法,google  Launcher的习惯先用一个start的方法作为一个实际操作的开始,这里的startBinding会完成resetLayout等清空数据的操作
    r = new Runnable() {
        public void run() {
            Callbacks callbacks = tryGetCallbacks(oldCallbacks);
            if (callbacks != null) {
                callbacks.clearPendingBinds();
                callbacks.startBinding();
            }
        }
    };
    runOnMainThread(r);
//而后是核心代码,首先绑定屏幕,传入的参数是orderedScreenIds,参数源于数据库。                                    

bindWorkspaceScreens(oldCallbacks, orderedScreenIds);


private void bindWorkspaceScreens(final Callbacks oldCallbacks,
        final ArrayList orderedScreens) {
    final Runnable r = new Runnable() {
        @Override
        public void run() {
            Callbacks callbacks = tryGetCallbacks(oldCallbacks);
            if (callbacks != null) {
                callbacks.bindScreens(orderedScreens);
            }
        }
    };
    runOnMainThread(r);
}

//绑定屏幕采用回调Launcher的bindScreens方法,由于第一屏会有概率出现 google搜索栏所以需要特殊定制,注意定制的google搜索栏不存于数据库中,其具备不可移动不可删除的特性,而google搜索栏在创建时是随着屏幕一同创建。

public void bindScreens(ArrayList orderedScreenIds) {
    if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
            orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
        orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
        orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
        mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
    } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
         mWorkspace.addExtraEmptyScreen();
    }
    bindAddScreens(orderedScreenIds);

    if (hasCustomContentToLeft()) {
        mWorkspace.createCustomContentContainer();
        populateCustomContentContainer();
    }

    mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
}

//对于绑定屏幕实质是创建与数据库中屏幕数一致的空屏幕。
  private void bindAddScreens(ArrayList orderedScreenIds) {
    int count = orderedScreenIds.size();
    for (int i = 0; i < count; i++) {
        long screenId = orderedScreenIds.get(i);
        if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
            // No need to bind the first screen, as its always bound.
            mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
        }
    }
}

// 创建空屏幕的方法是insertNewWorkspaceScreen,核心方法是addView方法,workspace是由celllayout组成的viewGroup,通过addview往里面添加celllayout也就是添加空屏幕。

  public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
    if (mWorkspaceScreens.containsKey(screenId)) {
        throw new RuntimeException("Screen id " + screenId + " already exists!");
    }

    CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
                    R.layout.workspace_screen, this, false /* attachToRoot */);
    newScreen.setOnLongClickListener(mLongClickListener);
    newScreen.setOnClickListener(mLauncher);
    newScreen.setSoundEffectsEnabled(false);
    mWorkspaceScreens.put(screenId, newScreen);
    mScreenOrder.add(insertIndex, screenId);
    addView(newScreen, insertIndex);

    if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
        newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
    }

    return newScreen;
}

 

以上完成了屏幕的添加,随后就需要往屏幕中添加桌面的图标。实际图标screen的子view,所以需要先有屏幕才能有图标。

你可能感兴趣的:(源码剖析)