前言
为了加深对Launcher3的整体印象,记录在工作上所遇到和解决的关于Launcher3定制开发的一些知识点,并且归纳总结成本系列文章。同时,也能起到帮助他人解决一些关于Launcher3相关定制的需求开发。
- Android Launcher3定制开发之结构
- Android Launcher3定制开发之改造横向化
- Android Launcher3定制开发之创建小部件
- Android Launcher3定制开发之预置快捷方式
- Android Launcher3定制开发之去除搜索和开机引导
- Android Launcher3定制开发之修改快捷方式图标
- Android Launcher3定制开发之添加负一屏
简介
大部分Launcher定制都是需要将某些主要的业务应用放在第一屏或者在Hotseat摆放默认应用,本篇文章将教大家如何直接实现预置应用。
找到入口
InvariantDeviceProfile类是Launcher的默认配置加载类,通过InvariantDeviceProfile方法可以看出,CellLayout显示的应用行数和列数可以通过findClosestDeviceProfiles查询XML配置来读取配参。
那么,我们先将适合自己业务的XML文件先声明。我这里定义的是一个5X4的布局(关于布局排列如何自定义后面会说),同时第一屏预置了微信 QQ、相册、设置,同时默认将电话、短信、相机、浏览器预置在Hotseat上显示。详细代码和注释见下:
default_workspace.xml
<?xml version="1.0" encoding="utf-8"?>
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
<!-- 微信 -->
<resolve
launcher:screen="1" //显示在第几屏
launcher:x="0" //显示在x轴第几个
launcher:y="4"> // 显示在y轴第几个
<favorite launcher:packageName="com.tencent.mm" launcher:className="com.tencent.mm.ui.LauncherUI" /> //通过包名和启动类名找到应用主路由地址(Tips:可配置多个,当第一个失效则默认往下筛选,多数时候为了兼容性可以多写几个)
</resolve>
<!-- QQ -->
<resolve
launcher:screen="1"
launcher:x="1"
launcher:y="4">
<favorite launcher:packageName="com.tencent.mobileqq" launcher:className="com.tencent.mobileqq.activity.SplashActivity" />
</resolve>
<!-- 相册 -->
<resolve
launcher:screen="1"
launcher:x="2"
launcher:y="4">
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
<favorite launcher:uri="#Intent;type=images/*;end" />
<favorite launcher:packageName="com.miui.gallery" launcher:className="com.miui.gallery.activity.HomePageActivity" />
<favorite launcher:packageName="com.android.gallery3d" launcher:className="com.huawei.gallery.app.GalleryMain" />
<favorite launcher:packageName="com.coloros.gallery3d" launcher:className="com.oppo.gallery3d.app.Gallery" />
<favorite launcher:packageName="com.vivo.gallery" launcher:className="com.android.gallery3d.vivo.GalleryTabActivity" />
</resolve>
<!-- 设置 -->
<resolve
launcher:screen="1"
launcher:x="3"
launcher:y="4">
<favorite launcher:uri="#Intent;action=android.settings.SETTINGS;end" />
<favorite launcher:packageName="com.android.settings" launcher:className="com.android.settings.Settings" />
<favorite launcher:packageName="com.android.settings" launcher:className="com.android.settings.MainSettings" />
<favorite launcher:packageName="com.android.settings" launcher:className="com.android.settings.HWSettings" />
<favorite launcher:packageName="com.android.settings" launcher:className="com.oppo.settings.SettingsActivity" />
</resolve>
<!-- 电话 -->
<resolve
launcher:container="-101"
launcher:screen="0"
launcher:x="0"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
<favorite launcher:uri="tel:123" />
<favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
</resolve>
<!-- 短信 -->
<resolve
launcher:container="-101"
launcher:screen="1"
launcher:x="1"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
<favorite launcher:uri="sms:" />
<favorite launcher:uri="smsto:" />
<favorite launcher:uri="mms:" />
<favorite launcher:uri="mmsto:" />
</resolve>
<!-- 相机 -->
<resolve
launcher:container="-101"
launcher:screen="2"
launcher:x="2"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
<favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
</resolve>
<!-- 浏览器 -->
<resolve
launcher:container="-101"
launcher:screen="3"
launcher:x="3"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
<favorite launcher:uri="http://www.example.com/" />
<favorite launcher:packageName="com.android.browser" launcher:className="com.android.browser.BrowserActivity" />
<favorite launcher:packageName="com.huawei.browser" launcher:className="com.huawei.browser.Main2" />
</resolve>
</favorites>
解释一下,resolve就在favorites中ViewGroup.addView。现在国产自研ROM分化严重,所以favorite兼容性上处理起来会麻烦一点,需要分不同品牌应用。
修改行数和列数
这样,我们已经把预置配置文件创建好了,接下来回到InvariantDeviceProfile.findClosestDeviceProfiles。
可以看到,findClosestDeviceProfiles在源码上根据不同的分辨率设备,分别读取了不同的机型,有的是4X4,有的是4X5,有的是5X6。这里面不符合我们现在的业务需求,我们需要将行数和列数改成5X4并且保证兼容性。所以,我对代码做了如下修改,将所有分辨率规格改为了5X4,同时不同分辨率加载不同大小的快捷图标,见下文代码和注释:
InvariantDeviceProfile(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
Point smallestSize = new Point();
Point largestSize = new Point();
display.getCurrentSizeRange(smallestSize, largestSize);
minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
//图标排列兼容处理
ArrayList<InvariantDeviceProfile> closestProfiles =
findClosestDeviceProfiles(minWidthDps, minHeightDps,
getPredefinedDeviceProfiles(R.xml.default_workspace));
InvariantDeviceProfile interpolatedDeviceProfileOut =
invDistWeightedInterpolate(minWidthDps, minHeightDps, closestProfiles);
InvariantDeviceProfile closestProfile = closestProfiles.get(0);
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
hotseatAllAppsRank = (int) (numHotseatIcons / 2);
defaultLayoutId = closestProfile.defaultLayoutId;
defaultNoAllAppsLayoutId = closestProfile.defaultNoAllAppsLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
iconSize = interpolatedDeviceProfileOut.iconSize;
iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
applyPartnerDeviceProfileOverrides(context, dm);
Point realSize = new Point();
display.getRealSize(realSize);
int smallSide = Math.min(realSize.x, realSize.y);
int largeSide = Math.max(realSize.x, realSize.y);
landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
largeSide, smallSide, true /* isLandscape */);
portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
smallSide, largeSide, false /* isLandscape */);
}
/**
* 不同分辨率统一处理5X4
*/
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(int xmlId) {
ArrayList<InvariantDeviceProfile> predefinedDeviceProfiles = new ArrayList<>();
// width, height, #rows(行数), #columns(列数), #folder rows, #folder columns,
// iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
// TODO 这里可以根据业务定义常量,规范参数。
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
255, 300, 5, 4, 4, 4, 4, SMALL_ICON_SIZE_DP, 13, 4, 48,xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
255, 400, 5, 4, 4, 4, 4, SMALL_ICON_SIZE_DP, 13, 4, 48, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
275, 420, 5, 4, 4, 4, 4, SMALL_ICON_SIZE_DP, 13, 4, 48, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
255, 450, 5, 4, 4, 4, 4, SMALL_ICON_SIZE_DP, 13, 4, 48, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
296, 491.33f, 5, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 4, 48, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
335, 567, 5, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 4, 56, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
359, 567, 5, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 4, 56, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
406, 694, 5, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 14.4f, 4, 56, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
575, 904, 5, 4, 4, 4, 4, BIG_ICON_SIZE_DP, 14.4f, 4, 60, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
727, 1207, 5, 4, 4, 4, 4, BIG_ICON_SIZE_DP, 14.4f, 4, 64, xmlId));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
1527, 2527, 5, 4, 4, 4, 4, 100, 20, 4, 72, xmlId));
return predefinedDeviceProfiles;
}
通过以上配置,就可以自定义自己想要的行数和列数以及预置应用了。
结语
如果读完本文还有不懂的地方,可以结合代码加深理解,代码通过点击这里找到。希望对大家学习和了解Launcher开发有所帮助。