Android 12 桌面(Launcher3)安装应用后图标默认从第2页开始显示

问题:

安装的应用,应用图标会在主页未满的情况下直接跳转到第二页显示

Launcher3 是一个应用app ,主要作用是 管理手机桌面的图标小部件的显示等相关管理的功能

所以本文问题的关键就在于 处理图标显示的处理逻辑上

  • launcher.java
    launcher.java 是Launcher3应用的主窗口(即一般app的MainActivity)
    这里先看下,初始化onCreate()里的代码处理
    src/com/android/launcher3/Launcher.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	***省略N行代码***
    	
    	// 初始化里做了很多界面以及管理器的初始化,这里三行才是本文问题的关键
    	// 主要是LauncherAppState 和 mModel(LauncherModel)
    	// 这两个文件是和应用管理相关的
    	LauncherAppState app = LauncherAppState.getInstance(this);
        mOldConfig = new Configuration(getResources().getConfiguration());
        mModel = app.getModel();
        
    	***省略N行代码***
    }

然后我们可以简单看下

  • LauncherAppState.java
    // 可以看到LauncherAppState是个单例,MainThreadInitializedObject是个壳
    // 目的是限定单例对象定义在主线程上
    public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
            new MainThreadInitializedObject<>(LauncherAppState::new);
            
    public static LauncherAppState getInstanceNoCreate() {
        return INSTANCE.getNoCreate();
    }
    
    ***省略N行代码***
    
    public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
        mContext = context;

        mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
        mIconProvider =  new IconProvider(context, Themes.isThemedIconEnabled(context));
        mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
                iconCacheFileName, mIconProvider);
        mWidgetCache = new DatabaseWidgetPreviewLoader(mContext, mIconCache);
        // 这里可以看到,在LauncherAppState构造里,初始化了LauncherModel实例
        // 将LauncherModel实例绑定到了LauncherAppState单例上,suoyi
        mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
        mOnTerminateCallback.add(mIconCache::close);
    }
    

接下来,看看

  • LauncherModel.java
    它不是单例,但是通过前面我们发现,它是依附于LauncherAppState这个单利上的,所以也具备了一定的单例性,那就是具备一定意义上通用性,复用性。
    然后再看这个类的官方注释:

/**

  • Maintains in-memory state of the Launcher. It is expected that there should be only one
  • LauncherModel object held in a static. Also provide APIs for updating the database state
  • for the Launcher.
    */
    翻译一下:保持启动器的内存状态。预计静态中应该只有一个LauncherModel对象。还提供用于更新启动器数据库状态的API。

其实这就是相当于Launcher3 的一个数据管理器

看代码有如下相关代码:

    /**
     * Adds the provided items to the workspace.
     */
    public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
        addAndBindAddedWorkspaceItems(itemList, true, false);
    }

    /**
     * Adds the provided items to the workspace.
     */
    public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList,
                                              boolean animated, boolean ignoreLoaded) {
        for (Callbacks cb : getCallbacks()) {
            cb.preAddApps();
        }
        // 看注释已经说的很直白了,添加项目到工作空间(翻译一下:添加桌面元素到桌面上)
        // 这里注意初始化了AddWorkspaceItemsTask这个类,后面我们进去看下
        AddWorkspaceItemsTask awit = new AddWorkspaceItemsTask(itemList);
        awit.setEnableAnimated(animated).setIgnoreLoaded(ignoreLoaded);
        enqueueModelUpdateTask(awit);
    }

接着看下AddWorkspaceItemsTask.java

  • AddWorkspaceItemsTask.java
    @Override
    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
        if (mItemList.isEmpty()) {
            return;
        }
        ***省略N行代码***
        
	InstallSessionHelper packageInstaller =
    InstallSessionHelper.INSTANCE.get(app.getContext());
            LauncherApps launcherApps = app.getContext().getSystemService(LauncherApps.class);

            for (ItemInfo item : filteredItems) {
                // Find appropriate space for the item.
                // 这里核心的是 findSpaceForItem 这个方法
                // 这个方法就是给要显示存放的桌面元素,寻找可以放置的screen(屏幕/页面)
                int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
                        addedWorkspaceScreensFinal, item.spanX, item.spanY);
                int screenId = coords[0];
                
			    ***这里省略的逻辑主要是桌面元素的差分处理以及后面与获取的屏幕/页面数据绑定***
			
			    // log bitmap and label
                FileLog.d(LOG, "Adding item info to workspace: " + itemInfo);
            }
        
        ***省略N行代码***
	}   
    /**
     * Find a position on the screen for the given size or adds a new screen.
     * @return screenId and the coordinates for the item in an int array of size 3.
     */
    protected int[] findSpaceForItem( LauncherAppState app, BgDataModel dataModel,
            IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY) {
		***省略N行代码***
		        int screenId = 0;
        int[] cordinates = new int[2];
        boolean found = false;

        int screenCount = workspaceScreens.size();
        // First check the preferred screen.
        int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
        if (preferredScreenIndex < screenCount) {
            screenId = workspaceScreens.get(preferredScreenIndex);
            found = findNextAvailableIconSpaceInScreen(
                    app, screenItems.get(screenId), cordinates, spanX, spanY);
        }

        if (!found) {
            // Search on any of the screens starting from the first screen.
            // 注意看:这里就是这个问题的核心,for循环里 screen 直接定义从1开始,那么不言而喻
            for (int screen = 1; screen < screenCount; screen++) {
                screenId = workspaceScreens.get(screen);
                if (findNextAvailableIconSpaceInScreen(
                        app, screenItems.get(screenId), cordinates, spanX, spanY)) {
                    // We found a space for it
                    found = true;
                    break;
                }
            }
        }

    	***省略N行代码***
    }

所以要解决本文的问题:解决方案就是让for循环里screen从0开始寻找,问题即可解决

for (int screen = 1; screen < screenCount; screen++) {
修改为
for (int screen = 0; screen < screenCount; screen++) {

你可能感兴趣的:(AOSP,android,java)