1. 程式人生 > >墨香帶你學Launcher之(三)-繫結螢幕、圖示、資料夾和Widget

墨香帶你學Launcher之(三)-繫結螢幕、圖示、資料夾和Widget

上一章我們講了Launcher的資料載入,包括:預設配置應用、資料夾以及widget的載入,所有應用的載入以及所有Widget的載入,資料載入完成後開始分批進行繪製到桌面上,包含預設配置bind,所有應用bind,所有小部件bind。下面我就從這幾個方面進行分析,看看他們的載入過程。

1.預設配置圖示、Widget、資料夾的繫結(bind)

上一章講到預設配置載入的位置:

 private void loadAndBindWorkspace() {

            ...

            if (!mWorkspaceLoaded) {
                loadWorkspace();
                ...
} // Bind the workspace bindWorkspace(-1); }

這裡主要是載入預設配置,然後呼叫bindWorkspace進行繫結,我們先看一下流程圖:

![01](https://img-blog.csdn.net/20170113170233665?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzg5OTcwNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 整個流程看似東西很多,其實就是準備資料,然後開始繫結,下面我們看bindWorkspace的主要程式碼:
private void bindWorkspace(int synchronizeBindPage) {
              //準備引數
            ...
//開始繫結 ... bindWorkspaceScreens(oldCallbacks, orderedScreenIds); // Load items on the current page bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, null); ... bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, (isLoadingSynchronously ? mDeferredBindRunnables : null)); //結束繫結 ...
}

我們先分析第一個方法:bindWorkspaceScreens,我們知道桌面上的圖示、資料夾等是放置到CellLayout(實際內部還有一個容器)中的,因此我們要首先新增CellLayout整個容器,
也就是這個方法,程式碼:

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

程式碼很簡單,就是呼叫回撥函式callbacks.bindScreens,這個回撥函式是在Launcher中實現的,因此我們看流程圖:

![02](https://img-blog.csdn.net/20170113170317853?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzg5OTcwNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 程式碼實現就是在bindAddScreens方法中通過for迴圈新增CellLayout,比較簡單不再貼程式碼。 我們接著看第二第三個函式,這兩個函式是一樣的,但是引數不一樣,從引數名字可以看到第一個bind當前頁面的圖示、資料夾、widget的,第二個是bind其他螢幕圖示、資料夾、widget的,因此我們只講一個流程,剩下的是一樣的。 我們先看流程圖: ![03](https://img-blog.csdn.net/20170113170417417?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMzg5OTcwNg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 從流程圖看其實就是三個for迴圈,分別繫結圖示、資料夾、小部件,
public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end,
                          final boolean forceAnimateIcons) {
        ...

        for (int i = start; i < end; i++) {
            final ItemInfo item = shortcuts.get(i);

            // 如果是在Hotseat中並且沒有Hotseat則跳過繼續
            if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
                    mHotseat == null) {
                continue;
            }

            final View view;
            switch (item.itemType) {
                case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                    ShortcutInfo info = (ShortcutInfo) item;
                    view = createShortcut(info);
                    break;
                case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                    view = FolderIcon.fromXml(R.layout.folder_icon, this,
                            (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
                            (FolderInfo) item, mIconCache);
                    break;
                default:
                    throw new RuntimeException("Invalid Item Type");
            }

            workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
                    item.cellY, 1, 1);
}

在上面的switch語句中判斷Item的型別,根據不同型別來生成不同的View,最後通過workspace.addInScreenFromBind方法將view繫結到桌面上,我們接著看一下addInScreenFromBind這個方法,這個方法最後呼叫到Workspace中的addInScreen方法,在這個方法中有兩個引數spanX、spanY沒有講過,我來解釋一下,我們第一章講了圖示排列到桌面上是按照4x4後者4x5等形式,那麼每個單元是一個圖示位置,但是,小部件的佔用不只是一個圖示,有可能幾個圖示的位置,而spanX就是橫向佔用的單元格個數,相應的spanY就是Y方向的佔用個數。根據控制元件的起始位置,以及佔用單元格個數就可以確定他在桌面上的位置。addInScreen程式碼我就不貼了,我只是在這說一下過程,進入這個方法,首先判斷container的型別,也就是父容器的型別:CellLayout還是Hotseat,然後判斷是資料夾還是圖示,最後通過呼叫layout.addViewToCellLayout方法根據相應的引數來新增到相應的容器裡面。

其他兩個的繫結也是差不多的,只是widget的相對複雜一點,這裡不再講解,後面我會單獨寫一章來講解widget的載入新增。

2.所有應用繫結(bind)

繫結所有應用其實是繫結二級介面的所有應用圖示,程式碼開始位置是:LauncherModel中的loadAllApps方法,首先載入手機裡的所有應用資訊,然後生成對應的物件,最後通過呼叫callbacks.bindAllApplications方法將所有應用繫結到二級介面,回撥函式依然是在Launcher中實現,二級介面是AllAppsContainerView,根據程式碼流程呼叫onAppsUpdated方法,在這個方法中排序最後呼叫updateAdapterItems方法,這個介面是一個RecyclerView,準備好資料庫,重新整理介面卡即可。

3.所有Widget的繫結(bind)

繫結Widget也是從loadAllApps這個方法開始的,在這個方法的最後面有個loadAndBindWidgetsAndShortcuts,通過這個方法繫結快捷方式和widget到小部件介面,看程式碼:

public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {

        runOnWorkerThread(new Runnable() {
            @Override
            public void run() {
                updateWidgetsModel(refresh);
                final WidgetsModel model = mBgWidgetsModel.clone();

                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Callbacks cb = getCallback();
                        if (callbacks == cb && cb != null) {
                            callbacks.bindAllPackages(model);
                        }
                    }
                });
                // update the Widget entries inside DB on the worker thread.
                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
                        model.getRawList());
            }
        });
    }

首先呼叫updateWidgetsModel方法,

void updateWidgetsModel(boolean refresh) {
        PackageManager packageManager = mApp.getContext().getPackageManager();
        final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
        widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
        Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
        widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
        mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
    }

在這個方法中首先呼叫getWidgetProviders方法來載入所有的小部件資訊,然後通過packageManager.queryIntentActivities方法載入所有的快捷方式資訊,最後將所有的資訊放置到WidgetsModel中,完成後通過呼叫callbacks.bindAllPackages回撥函式開始繫結所有的小部件和快捷方式,回撥函式在Launcher中實現,然後呼叫WidgetsContainerView中的addWidgets方法傳入WidgetsModel物件,然後通過呼叫重新整理介面卡來重新整理小部件介面。

最後:這一章相對簡單,主要是UI的繪製,有一些流程我沒有講,主要是UI繪製其實和自定義view相關,很多人一看就會了,所以不再講解,不會的可以去看看原始碼。

同步釋出:墨香部落格

微信公眾賬號:Code-MX
這裡寫圖片描述

注:本文原創,轉載請註明出處,多謝。