墨香帶你學Launcher之(三)-綁定屏幕、圖標、文件夾和Widget

上一章我們講了Launcher的數(shù)據加載,包括:默認配置應用、文件夾以及widget的加載,所有應用的加載以及所有Widget的加載,數(shù)據加載完成后開始分批進行繪制到桌面上,包含默認配置bind,所有應用bind,所有小部件bind。下面我就從這幾個方面進行分析,看看他們的加載過程。

1.默認配置圖標、Widget、文件夾的綁定(bind)


上一章講到默認配置加載的位置:

 private void loadAndBindWorkspace() {
            
            ...
            
            if (!mWorkspaceLoaded) {
                loadWorkspace();
                ...
            }

            // Bind the workspace
            bindWorkspace(-1);
        }

這里主要是加載默認配置,然后調用bindWorkspace進行綁定,我們先看一下流程圖:

launcher01.png

整個流程看似東西很多,其實就是準備數(shù)據,然后開始綁定,下面我們看bindWorkspace的主要代碼:

private void bindWorkspace(int synchronizeBindPage) {
              //準備參數(shù)
            ...

            //開始綁定
            ...

            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);
        }

代碼很簡單,就是調用回調函數(shù)callbacks.bindScreens,這個回調函數(shù)是在Launcher中實現(xiàn)的,因此我們看流程圖:

launcher02.png

代碼實現(xiàn)就是在bindAddScreens方法中通過for循環(huán)添加CellLayout,比較簡單不再貼代碼。

我們接著看第二第三個函數(shù),這兩個函數(shù)是一樣的,但是參數(shù)不一樣,從參數(shù)名字可以看到第一個bind當前頁面的圖標、文件夾、widget的,第二個是bind其他屏幕圖標、文件夾、widget的,因此我們只講一個流程,剩下的是一樣的。

我們先看流程圖:

launcher03.png

從流程圖看其實就是三個for循環(huán),分別綁定圖標、文件夾、小部件,

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則跳過繼續(xù)
            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方法,在這個方法中有兩個參數(shù)spanX、spanY沒有講過,我來解釋一下,我們第一章講了圖標排列到桌面上是按照4x4后者4x5等形式,那么每個單元是一個圖標位置,但是,小部件的占用不只是一個圖標,有可能幾個圖標的位置,而spanX就是橫向占用的單元格個數(shù),相應的spanY就是Y方向的占用個數(shù)。根據控件的起始位置,以及占用單元格個數(shù)就可以確定他在桌面上的位置。addInScreen代碼我就不貼了,我只是在這說一下過程,進入這個方法,首先判斷container的類型,也就是父容器的類型:CellLayout還是Hotseat,然后判斷是文件夾還是圖標,最后通過調用layout.addViewToCellLayout方法根據相應的參數(shù)來添加到相應的容器里面。

其他兩個的綁定也是差不多的,只是widget的相對復雜一點,這里不再講解,后面我會單獨寫一章來講解widget的加載添加。

2.所有應用綁定(bind)


綁定所有應用其實是綁定二級界面的所有應用圖標,代碼開始位置是:LauncherModel中的loadAllApps方法,首先加載手機里的所有應用信息,然后生成對應的對象,最后通過調用callbacks.bindAllApplications方法將所有應用綁定到二級界面,回調函數(shù)依然是在Launcher中實現(xiàn),二級界面是AllAppsContainerView,根據代碼流程調用onAppsUpdated方法,在這個方法中排序最后調用updateAdapterItems方法,這個界面是一個RecyclerView,準備好數(shù)據庫,刷新適配器即可。

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回調函數(shù)開始綁定所有的小部件和快捷方式,回調函數(shù)在Launcher中實現(xiàn),然后調用WidgetsContainerView中的addWidgets方法傳入WidgetsModel對象,然后通過調用刷新適配器來刷新小部件界面。

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

Github地址:Launcher3_mx

首發(fā)地址:墨香博客

微信公眾賬號:Code-MX

注:本文原創(chuàng),轉載請注明出處,多謝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容