墨香带你学Launcher之(三)-绑定屏幕、图标、文件夹和Widget

上一章我们讲了Launcher的数据加载,包括:默认配置应用、文件夹以及widget的加载,所有应用的加载以及所有Widget的加载,数据加载完成后开始分批进行绘制到桌面上,包含默认配置bind,所有应用bind,所有小部件bind。下面我就从这几个方面进行分析,看看他们的加载过程。

1.默认配置图标、Widget、文件夹的绑定(bind)


上一章讲到默认配置加载的位置:

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

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

这里主要是加载默认配置,然后调用bindWorkspace进行绑定,我们先看一下流程图:

launcher01.png

整个流程看似东西很多,其实就是准备数据,然后开始绑定,下面我们看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中实现的,因此我们看流程图:

launcher02.png

代码实现就是在bindAddScreens方法中通过for循环添加CellLayout,比较简单不再贴代码。

我们接着看第二第三个函数,这两个函数是一样的,但是参数不一样,从参数名字可以看到第一个bind当前页面的图标、文件夹、widget的,第二个是bind其他屏幕图标、文件夹、widget的,因此我们只讲一个流程,剩下的是一样的。

我们先看流程图:

launcher03.png

从流程图看其实就是三个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相关,很多人一看就会了,所以不再讲解,不会的可以去看看源码。

Github地址:Launcher3_mx

首发地址:墨香博客

微信公众账号:Code-MX

注:本文原创,转载请注明出处,多谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容