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); }
默认布局解析
截图说明流程
调用AutoInstallsLayout.java类loadLayout方法,进入解析xml
/**
* 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();
都是继承(interface)接口TagParser,方法parseAndAdd正如描述功能是:解析标记并添加到数据库
例如: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;
}
}
- 文件名:布局标签launcher:title;代码默认文件夹名R.string.folder_name
- 文件夹中图标:folderItems.size(),如果图标少于2个则要删除文件夹图表形式,若刚好为1图标时,添加图标快捷方式
- 添加到数据库: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);
}
推荐阅读
-
android 9.0 launcher3 去掉抽屉式显示所有 app(代码详解)
-
Android开发实现Launcher3应用列表修改透明背景的方法
-
Android开发中Launcher3常见默认配置修改方法总结
-
Android实现3D推拉门式滑动菜单源码解析
-
Android 使用Gallery实现3D相册(附效果图+Demo源码)
-
比较完整的android MP3 LRC歌词滚动高亮显示(附源码)
-
android 9.0 Launcher3 去掉抽屉式,显示所有 app
-
Android8.1 Launcher3 去掉抽屉方法
-
Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码下载)
-
android Launcher3删除风格