Launcher layout loading process

preface

Previously, I only knew that the Launcher was the desktop, that is, the icon of the installed application was displayed, and I didn't know anything else. When I want to learn it and modify it, I find that the Launcher involves a lot of things. This time, I will make a record of my understanding of the Launcher layout.

one

Through understanding, the Launcher is no different from an ordinary APP. It can be compiled separately, packaged into APK and installed on the mobile phone, so to learn it, I still start with the Android manifest file, and the source code is shared by a big man on Github, Launcher source code.

<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher3">
    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
    <!--
    Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
    Refer comments around specific entries on how to extend individual components.
    -->

    <!--
    Permissions required for read/write access to the workspace data. These permission name
    should not conflict with that defined in other apps, as such an app should embed its package
    name in the permissions. eq com.mypackage.permission.READ_SETTINGS
    -->
    <permission
        android:name="com.android.launcher3.permission.READ_SETTINGS"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="signatureOrSystem"
        android:label="@string/permlab_read_settings"
        android:description="@string/permdesc_read_settings"/>
    <permission
        android:name="com.android.launcher3.permission.WRITE_SETTINGS"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="signatureOrSystem"
        android:label="@string/permlab_write_settings"
        android:description="@string/permdesc_write_settings"/>

    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
    <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
    <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />

    <application
        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
        android:fullBackupOnly="true"
        android:fullBackupContent="@xml/backupscheme"
        android:hardwareAccelerated="true"
        android:icon="@drawable/ic_launcher_home"
        android:label="@string/derived_app_name"
        android:theme="@style/AppTheme"
        android:largeHeap="@bool/config_largeHeap"
        android:restoreAnyVersion="true"
        android:supportsRtl="true" >

        <!--
        Main launcher activity. When extending only change the name, and keep all the
        attributes and intent filters the same
        -->
        <activity
            android:name="com.android.launcher3.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="unspecified"
            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
            android:resizeableActivity="true"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
                <category android:name="android.intent.category.LAUNCHER_APP" />
            </intent-filter>
        </activity>

        <!--
        The settings activity. When extending keep the intent filter present
        -->
        <activity
            android:name="com.android.launcher3.SettingsActivity"
            android:label="@string/settings_button_text"
            android:theme="@android:style/Theme.DeviceDefault.Settings"
            android:autoRemoveFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <!--
        The settings provider contains Home's data, like the workspace favorites. The permissions
        should be changed to what is defined above. The authorities should also be changed to
        represent the package name.
        -->
        <provider
            android:name="com.android.launcher3.LauncherProvider"
            android:authorities="com.android.launcher3.settings"
            android:exported="true"
            android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS"
            android:readPermission="com.android.launcher3.permission.READ_SETTINGS" />

    </application>
</manifest>

Look at androidmanifest The XML file is obviously not complicated. Under the tag of Application, there are two activities and one provider: Launcher, settings activity and LauncherProvider;

  • Launcher is the desktop seen by our mobile phone;
  • SettingsActivity is the setting page of the desktop;
  • LauncherProvider is used to store page layout information.

two

I wanted to look at the source code line by line. However, too many details stick to one place, which will affect the understanding of the project; Therefore, with the help of previous summary documents, combined with their own understanding, look at the source code to learn the Launcher layout.

First, introduce some interfaces of Launcher, so that you can have an understanding rather than imagination.

This is the Launcher home page. There are shortcuts, widgets and Google search box on the desktop

This is the second page after the Launcher slides.

Slide up to access the drawer page of Launcher, including all installed applications.

This is some operations after long pressing the desktop, including wallpaper replacement, widget addition, and desktop settings.

This is the selection page for changing desktop and lock screen wallpaper.

This is the selection page for adding a desktop widget.

This is the desktop settings page.

This is to display the remove button when long pressing an icon or widget on the desktop.

This is a schematic diagram of the desktop layout. (in case of infringement, delete it immediately.)

  • DragLayer is the root layout, which is used to handle the drag layout of sub views;
  • Workspace is a control used to load all other layouts on the desktop;
  • SearchDropTargetBar is used to display the removed and unloaded controls;
  • CellLayout is used to display applications and desktop gadgets. Multiple celllayouts can be displayed in a Workspace;
  • PageIndicator is used to indicate the CellLayout of the current page;
  • Hotseat is a common application display control.

three

Next, you can see how the desktop layout is loaded;
Look at launcher Java code. In the onCreate() method, there is the following code.

      //Load the xml file as a View object
      mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
      //Set View on Activity
      setContentView(mLauncherView);

It's very simple. It's the launcher Load XML into Activity;

<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2007 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<com.android.launcher3.LauncherRootView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto"
    android:id="@+id/launcher"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <com.android.launcher3.dragndrop.DragLayer
        android:id="@+id/drag_layer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:importantForAccessibility="no">

        <!-- The workspace contains 5 screens of cells -->
        <!-- DO NOT CHANGE THE ID -->
        <com.android.launcher3.Workspace
            android:id="@+id/workspace"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:theme="@style/HomeScreenElementTheme"
            launcher:pageIndicator="@+id/page_indicator" />

        <include
            android:id="@+id/overview_panel"
            layout="@layout/overview_panel"
            android:visibility="gone" />

        <!-- Keep these behind the workspace so that they are not visible when
         we go into AllApps -->
        <com.android.launcher3.pageindicators.WorkspacePageIndicator
            android:id="@+id/page_indicator"
            android:layout_width="match_parent"
            android:layout_height="@dimen/vertical_drag_handle_size"
            android:layout_gravity="bottom|center_horizontal"
            android:theme="@style/HomeScreenElementTheme" />

        <include
            android:id="@+id/drop_target_bar"
            layout="@layout/drop_target_bar" />

        <include android:id="@+id/scrim_view"
            layout="@layout/scrim_view" />

        <include
            android:id="@+id/apps_view"
            layout="@layout/all_apps"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible" />

        <!-- DO NOT CHANGE THE ID -->
        <include
            android:id="@+id/hotseat"
            layout="@layout/hotseat"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </com.android.launcher3.dragndrop.DragLayer>

</com.android.launcher3.LauncherRootView>

View launcher XML file, you can clearly see that it contains the following views:

  • LauncherRootView
  • DragLayer
  • Workspace
  • WorkspacePageIndicator
  • overview_panel
  • drop_target_bar
  • scrim_view
  • all_apps
  • hotseat

The first four are directly introduced into the xml file through the class name, and the last five are introduced into the xml file through the form of include;
There are several mentioned earlier, such as DragLayer, Workspace, WorkspacePageIndicator and hotseat;
Several are seen for the first time, including LauncherRootView and overview_panel,drop_target_bar,scrim_view, and all_apps;
According to the Android xml layout knowledge we have learned, we can judge the desktop layout diagram and launcher XML is different.

The differences are as follows:

  • In the xml file, the root layout is LauncherRootView, which we don't see in the diagram;
  • Similarly, there are layouts in the xml file but not in the schematic diagram, as well as overview_panel,scrim_view,all_apps.
  • In the diagram, Workspace is the parent control of SearchDropTargetBar, CellLayout, PageIndicator and Hotseat. In the xml file, we can see that Workspace is a separate control;
  • In the schematic diagram, you can see the CellLayout layout, but you can't see it in the xml file;

Of course, we learn Launcher based on the source code, so if there are differences between the schematic diagram and xml, of course, xml shall prevail. Next, let's take a look at their differences one by one.

four

In the xml file, the root layout is LauncherRootView, which we don't see in the diagram;
From the source code, we can see that LauncherRootView is a subclass of InsettableFrameLayout, InsettableFrameLayout is a subclass of FrameLayout, and it implements the Insettable interface; Therefore, LauncherRootView is a FrameLayout with Insettable function. What is the function of Insettable interface? It can be seen from the data that the interface is to make the sub layout of the class implementing it not blocked by the status bar or navigation bar. The method is to set the Margin value for the sub View. See the key code below; There is only one subclass of LauncherRootView, which is DragLayer, so DragLayer will not be obscured by the status bar and navigation bar.

    @Override
    public void setInsets(Rect insets) {
        final int n = getChildCount();
        for (int i = 0; i < n; i++) {
            final View child = getChildAt(i);
            setFrameLayoutChildInsets(child, insets, mInsets);
        }
        mInsets.set(insets);
    }
    public void setFrameLayoutChildInsets(View child, Rect newInsets, Rect oldInsets) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();

        if (child instanceof Insettable) {
            ((Insettable) child).setInsets(newInsets);
        } else if (!lp.ignoreInsets) {
            lp.topMargin += (newInsets.top - oldInsets.top);
            lp.leftMargin += (newInsets.left - oldInsets.left);
            lp.rightMargin += (newInsets.right - oldInsets.right);
            lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
        }
        child.setLayoutParams(lp);
    }

five

Similarly, there are layouts in the xml file but not in the schematic diagram, as well as overview_panel,scrim_view,all_apps.

overview_panel

By viewing the overview_panel.xml file, it is found that it is a Space control. The Space control is a lightweight view subclass, which can be used to create gaps between components in the general layout. Here, its width and height are set to 0dp, which shows that it does not occupy Space;

<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2016 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<Space
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="0dp"
      android:layout_height="0dp" />

In the code, only its coordinates are confirmed in DragLayer, and there are no calls elsewhere.

mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
                    sTempRect);

scrim_view

View scrim_view.xml file, which is a ScrimView. Its function is to transition the view of the desktop background when sliding out of the drawer page

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<com.android.launcher3.views.ScrimView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/scrim_view" />

all_apps

View all_apps.xml file. Its root layout is AllAppsContainerView. Under AllAppsContainerView, four layouts are introduced through include: all_apps_rv_layout,all_apps_floating_header,search_container_all_apps and all_apps_fast_scroller;

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<!-- The top and bottom paddings are defined in this container, but since we want
     the list view to span the full width (for touch interception purposes), we
     will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.AllAppsContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/apps_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="true"
    android:clipToPadding="false"
    android:focusable="false"
    android:saveEnabled="false" >

    <include layout="@layout/all_apps_rv_layout" />

    <include layout="@layout/all_apps_floating_header" />

    <include
        android:id="@id/search_container_all_apps"
        layout="@layout/search_container_all_apps"/>

    <include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsContainerView>

all_apps_rv_layout is a control used to display all installed apps. Looking at the source code, we can see that the parent class of its parent class is our commonly used RecyclerView;

public class AllAppsRecyclerView extends BaseRecyclerView
public abstract class BaseRecyclerView extends RecyclerView

all_ apps_ floating_ The header is a hovering layout of the head. Look at the source code and test, and find that its internal controls will be hidden;

        //1. Through printing, we know that the value of mUsingTabs is false
        if (mUsingTabs) {
            mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
            mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
            onTabChanged(mViewPager.getNextPage());
        } else {
            mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
            //2.mAH[AdapterHolder.WORK].recyclerView is set to null
            mAH[AdapterHolder.WORK].recyclerView = null;
        }
        setupHeader();
    public void setupHeader() {
        mHeader.setVisibility(View.VISIBLE);
        //3. The second parameter is true
        mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null);

        int padding = mHeader.getMaxTranslation();
        for (int i = 0; i < mAH.length; i++) {
            mAH[i].padding.top = padding;
            mAH[i].applyPadding();
        }
    }
    public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
        mTabsHidden = tabsHidden;
        //4. When tabhidden is true, mTabLayout will be hidden.
        mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
        mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
        mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
        mParent = (ViewGroup) mMainRV.getParent();
        setMainActive(mMainRVActive || mWorkRV == null);
        reset(false);
    }

six

In the diagram, Workspace is the parent control of SearchDropTargetBar, CellLayout, PageIndicator and Hotseat. In the xml file, we can see that Workspace is a separate control;

Workspace

Workspace is a separate control. If it has a child View, the child View must be added in the form of code. Look at the source code. That's true. What's more, it has a child View and its child View is CellLayout.

public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
        if (mWorkspaceScreens.containsKey(screenId)) {
            throw new RuntimeException("Screen id " + screenId + " already exists!");
        }

        // Inflate the cell layout, but do not add it automatically so that we can get the newly
        // created CellLayout.
        CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                R.layout.workspace_screen, this, false /* attachToRoot */);
        newScreen.getShortcutsAndWidgets().setId(R.id.workspace_page_container);
        int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
        int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
        newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);

        mWorkspaceScreens.put(screenId, newScreen);
        mScreenOrder.add(insertIndex, screenId);
        addView(newScreen, insertIndex);
        mStateTransitionAnimation.applyChildState(
                mLauncher.getStateManager().getState(), newScreen, insertIndex);

        if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
            newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
        }

        return newScreen;
    }

After searching, in the Workspace code, only insertNewWorkspaceScreen () has the method of calling addView (). You can see that the View it adds is newScreen, and it is the View loaded through layoutinflator. Through viewing the Workspace_ screen. As you can see from XML, it is a CellLayout.

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<com.android.launcher3.CellLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:hapticFeedbackEnabled="false"
    launcher:containerType="workspace" />

Further inspection shows that insertNewWorkspaceScreen() method is called at three locations:

/**
     * Initializes and binds the first page
     * @param qsb an existing qsb to recycle or null.
     */
    public void bindAndInitFirstWorkspaceScreen(View qsb) {
        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
            return;
        }
        // Add the first page
        CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
        // Always add a QSB on the first screen.
        if (qsb == null) {
            // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
            // edges, we do not need a full width QSB.
            qsb = LayoutInflater.from(getContext())
                    .inflate(R.layout.search_container_workspace,firstPage, false);
        }

        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
        lp.canReorder = false;
        if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
            Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
        }
    }
    //Insert a new CellLayout to the back
    public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
        /**
         * Find the index to insert this view into.
         * If the empty screen exists, then insert it before that.
          */
        int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
        if (insertIndex < 0) {
            insertIndex = mScreenOrder.size();
        }
        insertNewWorkspaceScreen(screenId, insertIndex);
    }
    /**
     * When the drag application needs to add a screen or there is no screen currently,
     * Call this method to add a CellLayout in Workspace
     * @param screenId
     */
    public void insertNewWorkspaceScreen(long screenId) {
        insertNewWorkspaceScreen(screenId, getChildCount());
    }

Thus, Workspace is the parent control of one or more CellLayout.

The other three controls are also described here:

SearchDropTargetBar

SearchDropTargetBar, which corresponds to launcher Drop in XML_ target_ Bar, which is used to display the control that the remove or uninstall button will be displayed at the top of the screen when the user drags the application shortcut icon;

<?xml version="1.0" encoding="utf-8"?><!--
     Copyright (C) 2018 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<com.android.launcher3.DropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/dynamic_grid_drop_target_size"
    android:layout_gravity="center_horizontal|top"
    android:focusable="false"
    android:alpha="0"
    android:theme="@style/HomeScreenElementTheme"
    android:visibility="invisible">

    <!-- Delete target -->
    <com.android.launcher3.DeleteDropTarget
        android:id="@+id/delete_target_text"
        style="@style/DropTargetButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="@string/remove_drop_target_label" />

    <!-- Uninstall target -->
    <com.android.launcher3.SecondaryDropTarget
        android:id="@+id/uninstall_target_text"
        style="@style/DropTargetButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="@string/uninstall_drop_target_label" />

</com.android.launcher3.DropTargetBar>

PageIndicator

PageIndicator corresponds to launcher The WorkspacePageIndicator in the XML file is an indicator that is not available in CellLayout in Workspace. They are associated through the launcher:pageIndicator attribute;

        <com.android.launcher3.pageindicators.WorkspacePageIndicator
            android:id="@+id/page_indicator"
            android:layout_width="match_parent"
            android:layout_height="@dimen/vertical_drag_handle_size"
            android:layout_gravity="bottom|center_horizontal"
            android:theme="@style/HomeScreenElementTheme" />
        <com.android.launcher3.Workspace
            android:id="@+id/workspace"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:theme="@style/HomeScreenElementTheme"
            launcher:pageIndicator="@+id/page_indicator" />

Hotseat

Hotseat corresponds to launcher The hoteat in the XML file is the permanent icon bar at the bottom of the desktop. After checking it, it is also a CellLayout. Looking at the source code, we can see that it really only displays one row or column.

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<com.android.launcher3.Hotseat
    android:theme="@style/HomeScreenElementTheme"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto">

    <com.android.launcher3.CellLayout
        android:id="@+id/layout"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        launcher:containerType="hotseat"
        android:importantForAccessibility="no" />
</com.android.launcher3.Hotseat>

        if (hasVerticalHotseat) {
            mContent.setGridSize(1, idp.numHotseatIcons);
        } else {
            mContent.setGridSize(idp.numHotseatIcons, 1);
        }

seven

In the schematic diagram, you can see the CellLayout layout, but you can't see it in the xml file;
From the previous introduction, CellLayout is a sub View of Workspace or Hotseat. It is used to display shortcuts or widgets on the desktop in Workspace and resident shortcuts in Hotseat.
Looking at the source code, we can see that CellLayout divides the page into the same rectangle according to the configuration, and displays shortcuts or widgets in the rectangle. Each application shortcut occupies a rectangle. If it is a widget, you need to determine how many rectangles to occupy according to the size of the widget.

//The remaining size of the cellayout width and height minus the padding value
int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
//Average width
int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
//Average height
int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
    public static int calculateCellWidth(int width, int countX) {
        return width / countX;
    }
    public static int calculateCellHeight(int height, int countY) {
        return height / countY;
    }
      	 //Get the number of rows and columns from the configuration file
        DeviceProfile grid = mLauncher.getDeviceProfile();
        mCountX = grid.inv.numColumns;
        mCountY = grid.inv.numRows;

summary

This article is actually just for launcher The introduction of XML file has a new understanding of each control by viewing the source code; I saw the schematic diagram of the launcher layout in other places, which helped me understand the launcher layout. At the same time, it also had shortcomings, which prompted me to draw a schematic diagram of the launcher layout I understood, as follows:

This picture is for personal understanding. Don't spray it if you don't like it.

reference material

Mo Xiang takes you to learn Launcher
Android 9.0 Launcher source code analysis (III) -- Launcher layout and multi device adaptation
Analysis of Android M Launcher3 main process source code
There are still many materials that have been checked but can not be found. Thank you for your selfless sharing. Thank you.

Keywords: Android

Added by neylitalo on Thu, 13 Jan 2022 18:03:57 +0200