欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android launcher3 -- launcher3源码3

程序员文章站 2022-06-30 16:32:38
...

Android launcher3 – launcher3源码3

前面讲了简单的默认布局xml:Android launcher3 – launcher3源码1
本章主要讲解默认桌面布局存储到数据库中。

默认布局读取

手机:华为Honor 5C
默认布局:default_workspace_4x4.xml

ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() {
    ArrayList<InvariantDeviceProfile> predefinedDeviceProfiles = new ArrayList<>();
    // width, height, #rows, #columns, #folder rows, #folder columns,
    // iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
...
    predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
            335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
    predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
            359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
...
    LogHelper.i("getPredefinedDeviceProfiles()", " predefinedDeviceProfiles length is " + predefinedDeviceProfiles.size());
    return predefinedDeviceProfiles;
}

该方法返回了InvariantDeviceProfile的ArrayList数组;看下面Log返回了11个InvariantDeviceProfile;在Log中minWidthDps=336.0,minHeightDps=580.0,通过手机DisplayMetrics属性计算获取的显示的workspace最小宽高。

08-03 23:54:36.074 I/XhLauncher( 2606): [ (InvariantDeviceProfile.java:135)#<init> ]
08-03 23:54:36.074 I/XhLauncher( 2606): minWidthDps=336.0,minHeightDps=580.0
08-03 23:54:36.075 I/XhLauncher( 2606): getPredefinedDeviceProfiles(): [ (InvariantDeviceProfile.java:207)#GetPredefinedDeviceProfiles ]
08-03 23:54:36.075 I/XhLauncher( 2606):  predefinedDeviceProfiles length is 11

InvariantDeviceProfile.java中默认布局Id: defaultLayoutId = closestProfile.defaultLayoutId

ArrayList<InvariantDeviceProfile> closestProfiles =
                findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles());

findClosestDeviceProfiles返回按照“与指定宽度和高度接近”排序的设备配置文件ArrayList数组。最终取最接近的数组的第一个数据:InvariantDeviceProfile closestProfile = closestProfiles.get(0);

ArrayList<InvariantDeviceProfile> findClosestDeviceProfiles(
        final float width, final float height, ArrayList<InvariantDeviceProfile> points) {

    // Sort the profiles by their closeness to the dimensions
    ArrayList<InvariantDeviceProfile> pointsByNearness = points;
    Collections.sort(pointsByNearness, new Comparator<InvariantDeviceProfile>() {
        public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) {
            return (int) (dist(width, height, a.minWidthDps, a.minHeightDps)
                    - dist(width, height, b.minWidthDps, b.minHeightDps));
        }
    });

    return pointsByNearness;
}

最后获取default_workspace_4x4.xml布局

private DefaultLayoutParser getDefaultLayoutParser() {
    int defaultLayout = LauncherAppState.getInstance()
            .getInvariantDeviceProfile().defaultLayoutId;
    return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
            mOpenHelper, getContext().getResources(), defaultLayout);
}

默认布局解析

截图说明流程

Android launcher3 -- launcher3源码3

Android launcher3 -- launcher3源码3

调用AutoInstallsLayout.java类loadLayout方法,进入解析xml
Android launcher3 -- launcher3源码3

Android launcher3 -- launcher3源码3

Android launcher3 -- launcher3源码3

/**
 * Parses the current node and returns the number of elements added.
 */
protected int parseAndAddNode(
        XmlResourceParser parser,
        HashMap<String, TagParser> tagParserMap,
        ArrayList<Long> screenIds)
        throws XmlPullParserException, IOException {

    if (TAG_INCLUDE.equals(parser.getName())) {
        final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
        if (resId != 0) {
            // recursively load some more favorites, why not?
            return parseLayout(resId, screenIds);
        } else {
            return 0;
        }
    }
......
    TagParser tagParser = tagParserMap.get(parser.getName());
    if (tagParser == null) {
        LogHelper.d(TAG, "Ignoring unknown element tag: " + parser.getName());
        return 0;
    }
    long newElementId = tagParser.parseAndAdd(parser);
......
}

各种标签解析

在parseLayout方法中获取各种解析方式getLayoutElementsMap();
Android launcher3 -- launcher3源码3

都是继承(interface)接口TagParser,方法parseAndAdd正如描述功能是:解析标记并添加到数据库
Android launcher3 -- launcher3源码3


例如:FolderParser文件夹标签解析
布局xml:

<folder
    launcher:container="-100"
    launcher:screen="0"
    launcher:title="@string/folder_name"
    launcher:x="0"
    launcher:y="2">
    <favorite
        launcher:className="com.android.mediacenter.MainActivity"
        launcher:packageName="com.android.mediacenter" />
    <favorite
        launcher:className="com.android.settings.HWSettings"
        launcher:packageName="com.android.settings" />
</folder>

代码:

protected class FolderParser implements TagParser {
    private final HashMap<String, TagParser> mFolderElements;

    public FolderParser() {
        this(getFolderElementsMap());
    }

    public FolderParser(HashMap<String, TagParser> elements) {
        mFolderElements = elements;
    }

    @Override
    public long parseAndAdd(XmlResourceParser parser)
            throws XmlPullParserException, IOException {
        final String title;
        final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
        if (titleResId != 0) {
            title = mSourceRes.getString(titleResId);
        } else {
            title = mContext.getResources().getString(R.string.folder_name);
        }

        mValues.put(Favorites.TITLE, title);
        mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
        mValues.put(Favorites.SPANX, 1);
        mValues.put(Favorites.SPANY, 1);
        mValues.put(Favorites._ID, mCallback.generateNewItemId());
        long folderId = mCallback.insertAndCheck(mDb, mValues);
        if (folderId < 0) {
            if (LOGD) Log.e(TAG, "Unable to add folder");
            return -1;
        }

        final ContentValues myValues = new ContentValues(mValues);
        ArrayList<Long> folderItems = new ArrayList<Long>();

        int type;
        int folderDepth = parser.getDepth();
        int rank = 0;
        while ((type = parser.next()) != XmlPullParser.END_TAG ||
                parser.getDepth() > folderDepth) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            mValues.clear();
            mValues.put(Favorites.CONTAINER, folderId);
            mValues.put(Favorites.RANK, rank);

            TagParser tagParser = mFolderElements.get(parser.getName());
            if (tagParser != null) {
                final long id = tagParser.parseAndAdd(parser);
                if (id >= 0) {
                    folderItems.add(id);
                    rank++;
                }
            } else {
                throw new RuntimeException("Invalid folder item " + parser.getName());
            }
        }

        long addedId = folderId;

        // We can only have folders with >= 2 items, so we need to remove the
        // folder and clean up if less than 2 items were included, or some
        // failed to add, and less than 2 were actually added
        if (folderItems.size() < 2) {
            // Delete the folder
            Uri uri = Favorites.getContentUri(folderId);
            SqlArguments args = new SqlArguments(uri, null, null);
            mDb.delete(args.table, args.where, args.args);
            addedId = -1;

            // If we have a single item, promote it to where the folder
            // would have been.
            if (folderItems.size() == 1) {
                final ContentValues childValues = new ContentValues();
                copyInteger(myValues, childValues, Favorites.CONTAINER);
                copyInteger(myValues, childValues, Favorites.SCREEN);
                copyInteger(myValues, childValues, Favorites.CELLX);
                copyInteger(myValues, childValues, Favorites.CELLY);

                addedId = folderItems.get(0);
                mDb.update(LauncherProvider.TABLE_FAVORITES, childValues,
                        Favorites._ID + "=" + addedId, null);
            }
        }
        return addedId;
    }
}
  1. 文件名:布局标签launcher:title;代码默认文件夹名R.string.folder_name
  2. 文件夹中图标:folderItems.size(),如果图标少于2个则要删除文件夹图表形式,若刚好为1图标时,添加图标快捷方式
  3. 添加到数据库:long folderId = mCallback.insertAndCheck(mDb, mValues);如2中文件夹只有一个图标时,更新数据mDb.update(LauncherProvider.TABLE_FAVORITES, childValues, Favorites._ID + “=” + addedId, null)。

添加到数据库 的 相关代码:

@Override
public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
    return dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
}
static long dbInsertAndCheck(DatabaseHelper helper,
                             SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
    if (values == null) {
        throw new RuntimeException("Error: attempting to insert null values");
    }
    if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
        throw new RuntimeException("Error: attempting to add item without specifying an id");
    }
    helper.checkId(table, values);
    return db.insert(table, nullColumnHack, values);
}
相关标签: launcher android