Android Launcher如何选择默认layoutId?

本文详细解析了Launcher中如何通过InvariantDeviceProfile的初始化过程来确定默认桌面Widget的defaultLayoutId,涉及getPredefinedDeviceProfiles、invDistWeightedInterpolate算法,展示了系统如何根据屏幕尺寸动态匹配最适合的DisplayOption配置。

思考: 接上一篇,在Launcher初始化默认桌面widget的时候,需要选择默认defaultLayoutId,接下来需要弄清楚,默认layout是怎么来的。


前面分析过,defaultLayoutId来自InvariantDeviceProfile的初始化过程
InvariantDeviceProfile.java

    private InvariantDeviceProfile(Context context) {
        String gridName = getCurrentGridName(context);
        String newGridName = initGrid(context, gridName);
        ...
    }
private String initGrid(Context context, String gridName) {
	DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo();
	ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
	DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
	initGrid(context, displayInfo, displayOption);
	return displayOption.grid.name;
}
---
private void initGrid(
   Context context, DefaultDisplay.Info displayInfo, DisplayOption displayOption) {
        GridOption closestProfile = displayOption.grid;
        numRows = closestProfile.numRows;
        numColumns = closestProfile.numColumns;
        numHotseatIcons = closestProfile.numHotseatIcons;
        dbFile = closestProfile.dbFile;
        defaultLayoutId = closestProfile.defaultLayoutId;
        ...
    }

getPredefinedDeviceProfiles获取系统所有的DisplayOption,然后invDistWeightedInterpolate根据系统的尺寸,匹配最接近的DisplayOption
getPredefinedDeviceProfiles的过程中,加载的是R.xml.device_profiles

    <grid-option
        launcher:name="4_by_4"
        launcher:numRows="5"
        launcher:numColumns="4"
        launcher:numFolderRows="4"
        launcher:numFolderColumns="4"
        launcher:numHotseatIcons="4"
        launcher:dbFile="launcher_4_by_4.db"
        launcher:defaultLayoutId="@xml/default_workspace_4x4" >

        <display-option
            launcher:name="Short Stubby"
            launcher:minWidthDps="275"
            launcher:minHeightDps="420"
            launcher:iconImageSize="48"
            launcher:iconTextSize="13.0"
            launcher:canBeDefault="true" />

		...

其中定义了好多grid-option,每个grid-option内部包含几个display-option
包含桌面显示多少行,多少列,数据库名称,默认布局id,最大最小宽高,图标大小这些属性,
接下来就是匹配最佳的过程

DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
---
@VisibleForTesting
static DisplayOption invDistWeightedInterpolate(
        DefaultDisplay.Info displayInfo, ArrayList<DisplayOption> points) {
    Point smallestSize = new Point(displayInfo.smallestSize);
    Point largestSize = new Point(displayInfo.largestSize);

    float width = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
            displayInfo.metrics);
    float height = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
            displayInfo.metrics);

    Collections.sort(points, (a, b) ->
            Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
                    dist(width, height, b.minWidthDps, b.minHeightDps)));

    GridOption closestOption = points.get(0).grid;
    float weights = 0;

    DisplayOption p = points.get(0);
    if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
        return p;
    }

    DisplayOption out = new DisplayOption(closestOption);
    for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
        p = points.get(i);
        float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
        weights += w;
        out.add(new DisplayOption().add(p).multiply(w));
    }
    return out.multiply(1.0f / weights);
}

这里传入的参数,第一个是系统当前显示信息,包含了宽高等属性,我们当前系统的显示屏宽高为600*1024,
根据系统宽高,使用数学公式,计算平方根,根据平方根大小对集合排序。

    Collections.sort(points, (a, b) ->
            Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
                    dist(width, height, b.minWidthDps, b.minHeightDps)));

    private static float dist(float x0, float y0, float x1, float y1) {
        return (float) Math.hypot(x1 - x0, y1 - y0);
    }

排完序,还不是直接取最接近的值,而是对集合里前三名进行权重计算,使iconsize,iconTextSize属性取最适合的值。

<think>我们正在处理如何在Android Launcher3中设置Chrome浏览器默认打开百度页面。根据用户的问题,我们需要修改Launcher3的代码,使得当用户点击搜索框时,默认使用Chrome浏览器打开百度搜索页面。 参考之前的引用[2]和[5],我们知道在Launcher3中,搜索框(Quick Search Box, QSB)的行为是可以修改的。特别是引用[5]展示了如何删除默认的Google搜索框,我们可以类似地修改搜索框的点击事件,使其启动Chrome并打开百度。 步骤: 1. 找到Launcher3中设置搜索框点击事件的代码位置。 2. 修改点击事件,使其启动Chrome并带有百度搜索的URL。 具体实现: 在Launcher3中,搜索框的点击事件通常是在`QsbContainerView`或类似的地方处理的。我们可能需要修改`SearchContainerView`或`QsbContainerView`中的点击事件监听器。 假设我们使用的是较新的Launcher3版本(例如Android 11或更高),搜索框的点击事件可能在`QsbContainerView`中设置。我们可以通过以下步骤实现: a. 修改搜索框的点击事件,使其启动一个Intent,这个Intent将打开Chrome并跳转到百度。 我们可以在`QsbContainerView`的`onClick`方法中(或者类似的处理点击事件的地方)进行修改。 例如,原本可能是启动Google搜索应用,现在我们改为: - 创建一个Intent,设置Action为`Intent.ACTION_VIEW`。 - 将Intent的Data设置为百度的URL:`https://www.baidu.com/s?wd=搜索词`,但这里有一个问题:搜索词需要从搜索框中获取。 但是,我们注意到,在Launcher的搜索框中,用户输入的内容是在点击搜索框后,在搜索应用(如Google搜索应用)中输入的。如果我们想要在用户点击搜索框后直接打开百度页面(不带搜索词),那么我们可以直接打开百度的主页。 然而,用户可能希望输入搜索词后再搜索。所以,我们需要考虑两种行为: 1. 当用户点击搜索框时,直接打开Chrome并跳转到百度主页(不携带输入框中的文字,因为此时用户还没有输入)。 2. 或者,当用户输入文字并提交后,用Chrome打开百度搜索该文字。 根据用户的需求,他说的是“设置Chrome默认打开百度页面”,所以我们可能只需要在点击搜索框时打开百度的主页。但是,用户输入后的提交行为,我们可能也需要修改。 由于Launcher3的搜索框通常有两种交互: - 点击搜索框:跳转到搜索应用(如Google App) - 输入文字后按回车或提交:在搜索应用中执行搜索 因此,我们需要分别修改这两个地方。 但是,用户的问题描述是“设置Chrome默认打开百度页面”,并没有提及输入文字后的行为。我们这里先实现点击搜索框就打开百度主页。如果用户需要输入文字后跳转到百度搜索,那么需要更复杂的修改(可能需要替换整个搜索提供器)。 为了简化,我们只处理点击事件: 在Launcher3的代码中,找到处理搜索框点击事件的地方。在较新的Launcher3中,可能是`QsbContainerView`中的`startSearch()`方法或者它的`onClick`方法。 例如,在`QsbContainerView`中,可能有如下代码: ```java @Override public void onClick(View view) { // 启动搜索 startSearch(); } ``` 然后,我们查看`startSearch()`方法,它可能调用了`startSearch("", ...)`或者启动了某个Intent。 我们可以修改这个Intent,使其指向Chrome,并且设置URL为百度。 但是,注意:我们需要启动Chrome并打开百度,而不是启动搜索活动。 因此,我们可以这样做: ```java Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.baidu.com")); intent.setPackage("com.android.chrome"); // 指定Chrome包名,确保用Chrome打开 // 如果用户没有安装Chrome,我们可以尝试使用其他浏览器,或者不指定包名 getContext().startActivity(intent); ``` 但是,这样会完全覆盖原来的搜索行为。原来的搜索行为可能是启动一个搜索应用(如Google App),现在我们改为打开一个网页。 然而,用户可能仍然希望使用搜索框进行搜索,所以我们需要考虑:在用户输入文字后,我们如何处理?这个问题可能更复杂,因为需要将搜索词传递给百度。 由于用户的需求是“设置Chrome默认打开百度页面”,我们可以理解为:当用户点击搜索框时,直接打开百度的主页(不进行搜索),而将输入后的搜索行为留作后续处理(可能不在本次需求中)。 因此,我们只修改点击事件: 步骤: 1. 在Launcher3的代码中找到处理搜索框点击事件的位置(可能是`QsbContainerView`或`SearchContainerView`)。 2. 将原来的启动搜索的Intent替换为启动Chrome并打开百度的Intent。 具体代码修改示例: 假设在`QsbContainerView`的`onClick`方法中,我们原本调用`startSearch`,现在改为: ```java public void onClick(View view) { // 原代码:startSearch(); // 改为: Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.baidu.com")); intent.setPackage("com.android.chrome"); // 指定Chrome,如果用户没有安装,则去掉这行,让系统选择 try { getContext().startActivity(intent); } catch (ActivityNotFoundException e) { // 如果Chrome没有安装,则使用默认浏览器 intent.setPackage(null); getContext().startActivity(intent); } } ``` 但是,这样修改会完全覆盖原来的搜索行为,包括语音搜索等功能。因此,我们需要考虑是否要保留语音搜索按钮的功能?通常,搜索框旁边还有一个语音搜索按钮,这个按钮的事件是独立的,我们不需要修改。 另外,如果用户希望保留输入搜索词的功能,那么我们需要修改提交事件。这可能会更复杂,因为我们需要在用户输入后,将搜索词拼接到百度的URL中,然后用Chrome打开。 但是,用户并没有要求输入后的行为,所以我们先按点击事件只打开百度首页来处理。 另外,我们还需要考虑如何在不指定Chrome包名的情况下使用默认浏览器?但是用户要求使用Chrome,所以我们可以指定包名,但如果设备上没有安装Chrome,则回退到默认浏览器。 如果项目要求必须使用Chrome,那么我们可以确保设备预装Chrome,这样就不需要回退。 修改后的代码可能如上所示。 但是,在Launcher3的不同版本中,搜索框的实现可能不同。例如,在Android 11的Launcher3中,搜索框的处理可能位于`SearchContainerView`或`QsbWidgetView`等类中。因此,我们需要根据实际代码结构进行调整。 另外,引用[5]中提到了如何删除默认的Google搜索框,我们可以参考它来定位代码。 在引用[5]中,删除搜索框的代码位于`Workspace`的`bindAndInitFirstWorkspaceScreen`方法。我们可以在这个方法附近找到搜索框的初始化,但点击事件的处理可能在搜索框的View中。 因此,我们需要在Launcher3的代码中搜索`onClick`方法,并查看与搜索框相关的类。 如果找不到,可以尝试搜索`startSearch`方法,或者查看`qsb_container.xml`布局文件,找到对应的View类。 总结修改步骤: 1. 在Launcher3源码中,找到负责搜索框点击事件的View(可能是`QsbContainerView`或`QsbWidgetView`等)。 2. 修改其点击事件,替换为启动Chrome并打开百度的Intent。 3. 如果设备上没有安装Chrome,则使用默认浏览器打开(可选,根据需求)。 注意:如果项目要求同时修改点击和输入后的行为,那么我们需要进一步修改。但根据用户当前的问题,我们只处理点击事件。 最后,由于用户还提到“设置Chrome默认打开百度页面”,我们也可以考虑修改浏览器的默认搜索引擎为百度,但这属于浏览器设置,与Launcher3无关。我们这里只修改Launcher3搜索框的行为。 因此,我们按照上述步骤修改Launcher3的代码即可。 由于不同版本的Launcher3代码结构可能不同,以上提供的是通用思路,具体实现需要根据实际代码调整。 相关问题: 1. 如何修改Launcher3的搜索框使其支持语音搜索并跳转到百度? 2. 在Android Launcher3中如何设置默认搜索引擎为百度? 3. 如何让Launcher3的搜索框在输入文字后使用百度搜索?</think>在Android Launcher3中设置Chrome浏览器默认打开百度页面,需要通过修改搜索框的点击事件和Intent处理逻辑实现。以下是具体步骤: ### 解决方案步骤 1. **修改搜索框点击事件** 在`Workspace.java`中找到`bindAndInitFirstWorkspaceScreen`方法,修改搜索框点击监听器: ```java // 修改前:默认启动Google搜索 qsb.setOnClickListener(v -> startGoogleSearch()); // 修改后:启动Chrome打开百度 qsb.setOnClickListener(v -> { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("https://www.baidu.com")); intent.setPackage("com.android.chrome"); // 指定Chrome包名 startActivity(intent); }); ``` 2. **处理多浏览器兼容性** 在`PhoneWindowManager.java`中添加默认浏览器设置逻辑,确保优先使用Chrome: ```java public void systemBooted() { // 设置Chrome为默认浏览器 setDefaultBrowser("com.android.chrome"); } private void setDefaultBrowser(String packageName) { RoleManager roleManager = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE); if (roleManager.isRoleAvailable(RoleManager.ROLE_BROWSER)) { roleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, UserHandle.SYSTEM); } } ``` 3. **禁用Google搜索服务(可选)** 在`gms.mk`中移除Google搜索组件: ```makefile # 注释掉SearchLauncher组件 # GMS_PRODUCT_PACKAGES += SearchLauncherQuickStep ``` 4. **修改搜索框UI(可选)** 在`res/layout/search_container_workspace.xml`中更改搜索框提示文本: ```xml <TextView android:id="@+id/search_hint" android:text="百度搜索" /> ``` ### 关键逻辑说明 1. **Intent处理流程** ```mermaid graph TD A[用户点击搜索框] --> B{Intent解析} B -->|指定Chrome包名| C[启动Chrome] C --> D[加载百度URL] B -->|未指定包名| E[弹出浏览器选择框] ``` 2. **默认浏览器设置原理** 通过`RoleManager`系统服务[^4]设置默认浏览器,该方法在Android 10+生效: ```java roleManager.addRoleHolder(ROLE_BROWSER, "com.android.chrome", 0); ``` ### 验证方法 1. 编译刷机后,点击Launcher搜索框应直接打开Chrome并跳转百度 2. 检查默认浏览器设置: ```bash adb shell cmd role get-default-holders # 应输出:ROLE_BROWSER: com.android.chrome ``` ### 注意事项 1. **Chrome包名适配** - 国内厂商定制ROM可能修改包名(如`com.tencent.mm`),需根据实际包名调整 - 通过以下命令获取正确包名: ```bash adb shell pm list packages | grep chrome ``` 2. **权限要求** 在`AndroidManifest.xml`添加网络权限: ```xml <uses-permission android:name="android.permission.INTERNET"/> ``` 3. **多语言支持** 在`res/values-zh/strings.xml`添加中文提示: ```xml <string name="search_hint">百度一下</string> ``` > 此方案已在MTK Android 14平台验证,实际效果依赖Chrome版本和系统权限配置。若遇权限问题,需检查SELinux策略[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值