Launcher3--抽屉

article/2025/11/7 3:16:24

    抽屉是用来放置安卓手机中所有需要显示到Launcher上的(当然也可以进行过滤,将不想显示的隐藏起来)应用和小部件,启动应用、添加快捷方式到桌面、卸载等。之前也提到过,有些Launcher是没有抽屉的,如MIUI的Launcher。在Launcher3中,默认是有的,当然,也提供了不显示抽屉的方法,这个后面会说到,在此先了解下抽屉。

  

一、布局
    
    抽屉的布局文件是apps_customize_pane.xml,被include在launcher.xml中,
launcher.xml
        <include layout="@layout/apps_customize_pane"android:id="@+id/apps_customize_pane"android:layout_width="match_parent"android:layout_height="match_parent"android:visibility="invisible" />
apps_customize_pane.xml
<com.android.launcher3.AppsCustomizeTabHostxmlns:android="http://schemas.android.com/apk/res/android"xmlns:launcher="http://schemas.android.com/apk/res-auto"android:clipChildren="false"><LinearLayoutandroid:id="@+id/content"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:orientation="vertical"><FrameLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:clipChildren="false"><FrameLayoutandroid:id="@+id/fake_page_container"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:clipToPadding="false"><FrameLayoutandroid:id="@+id/fake_page"android:layout_width="match_parent"android:layout_height="match_parent"android:visibility="invisible"android:clipToPadding="false" /></FrameLayout><com.android.launcher3.AppsCustomizePagedViewandroid:id="@+id/apps_customize_pane_content"android:layout_width="match_parent"android:layout_height="match_parent"launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"launcher:maxGap="@dimen/workspace_max_gap"launcher:pageIndicator="@+id/apps_customize_page_indicator" /></FrameLayout><includeandroid:id="@+id/apps_customize_page_indicator"layout="@layout/page_indicator"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center" /></LinearLayout>
</com.android.launcher3.AppsCustomizeTabHost>
    这个就是抽屉的树形结构, AppsCustomizeTabHost 是根视图, id/content 是内容区域,包含一个 FrameLayout和页面指示器indicator,这个 FrameLayout也包含两块,上面一块是用作过渡页面,下面是 AppsCustomizePagedView,就是用来显示app列表或小部件的,是最核心的部分。

、数据加载和显示
    
    首先需要弄清楚的是,并不是我们点击了抽屉按钮进入抽屉页才开始加载数据的,我们之前分析了<<Launcher3的加载流程>>,知道这些数据在Launcher启动过程中就加载了。这个也很好理解,Android系统中安装了很多应用,如果每次打开抽屉都要加载数据,那可想而知是多么糟糕的体验。
    这部分的数据加载就是在<<Launcher3的加载流程>>中分析的loadAndBindAllApps过程,此过程已将应用数据保存到数据库中,并且设置到AppsCustomizePagedView中,详细过程就不在介绍了。很明显,这个时候要做的就是将其显示,并将Workspace隐藏。
    进入抽屉的途径一个是点击桌面抽屉按钮图标,另一个是长按桌面选择小部件按钮,这两个操作其实进入的是同一个界面,只不过是根据操作的不同选择加载应用还是小部件,那我们就以显示应用列表来分析。
    public void onClick(View v) {.............} else if (v == mAllAppsButton) {// 抽屉按钮onClickAllAppsButton(v);} else if (tag instanceof AppInfo) {// 应用列表中的应用............}
    protected void onClickAllAppsButton(View v) {if (LOGD) Log.d(TAG, "onClickAllAppsButton");// copy dbCommonUtil.copyDBToSDcard();// endif (isAllAppsVisible()) {// 抽屉页面是否可见,实际情况在抽屉页时,不会显示按钮showWorkspace(true);} else {showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);}if (mLauncherCallbacks != null) {mLauncherCallbacks.onClickAllAppsButton(v);}}
    这里根据抽屉页是否可见来确定是显示Workspace还是抽屉,但在实际情况中抽屉中不会显示抽屉按钮,所以也就不可能执行到 showWorkspace 这个方法中。直接看 showAllApps 方法,
    void showAllApps(boolean animated, AppsCustomizePagedView.ContentType contentType,boolean resetPageToZero) {if (mState != State.WORKSPACE) return;if (resetPageToZero) {// 是否需要恢复到首页mAppsCustomizeTabHost.reset();}showAppsCustomizeHelper(animated, false, contentType);mAppsCustomizeTabHost.post(new Runnable() {@Overridepublic void run() {// We post this in-case the all apps view isn't yet constructed.mAppsCustomizeTabHost.requestFocus();// 给抽屉界面焦点}});// Change the state *after* we've called all the transition codemState = State.APPS_CUSTOMIZE;// 更新页面状态未APPS_CUSTOMIZE// Pause the auto-advance of widgets until we are out of AllAppsmUserPresent = false;updateRunning();closeFolder();// 关闭文件夹// Send an accessibility event to announce the context changegetWindow().getDecorView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);}
    这里面调用了 showAppsCustomizeHelper方法, 这是显示抽屉的的一个帮助方法,与此方法对应的是 hideAppsCustomizeHelper 方法,很显然使用隐藏抽屉时调用的,这两个方法实现很相似,我们这里只分析showAppsCustomizeHelper。
        if (mStateAnimation != null) {// 重置mStateAnimationmStateAnimation.setDuration(0);mStateAnimation.cancel();mStateAnimation = null;}
    重置 AnimatorSet,其实这个方法里面最主要就是实现各种动画效果,Workspace上的动画、抽屉上的动画。
        boolean material = Utilities.isLmpOrAbove();// sdk版本是否大于等于21final Resources res = getResources();// 定义了一些动画时长final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);final int itemsAlphaStagger = res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);//缩放大小// 从Workspace切换到AppsCustomizeTabHostfinal View fromView = mWorkspace;final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;final ArrayList<View> layerViews = new ArrayList<View>();// DragLayer上的View列表
    定义了一些变量, material来判断sdk版本,后面会根据这个布尔变量来进行不同的动画设置,在Android L及以上采用了material design,所有在较高的版本上可以有一些更好的动画效果。然后还定义动画时长,缩放比例等。
        Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;Animator workspaceAnim = mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews);// 定义切换时Workspace上的动画// 设置加载的数据类型if (!LauncherAppState.isDisableAllApps() || contentType == AppsCustomizePagedView.ContentType.Widgets) {// Set the content type for the all apps/widgets spacemAppsCustomizeTabHost.setContentTypeImmediate(contentType);}
    设置加载内容的类型,有两种类型:application和widget,这里是application类型。
        // If for some reason our views aren't initialized, don't animateboolean initialized = getAllAppsButton() != null;// 是否初始化完成
        animated && initialized
    来判断是否实现动画效果,我们直接看动画是怎么实现的。
            mStateAnimation = LauncherAnimUtils.createAnimatorSet();// 创建AnimatorSetfinal AppsCustomizePagedView content = (AppsCustomizePagedView)toView.findViewById(R.id.apps_customize_pane_content);// 抽屉内容组件final View page = content.getPageAt(content.getCurrentPage());// 抽屉当前页final View revealView = toView.findViewById(R.id.fake_page);// 一个过渡页面,用来实现动画final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;// 设置过渡页面的背景,根据类型分别设置if (isWidgetTray) {revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));} else {revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));}
    初始化抽屉页面的组件,其中 revealView  是一个过渡页,用来实现动画效果的,动画结束后将其隐藏。
            // 先隐藏真实页面,显示过渡页面// Hide the real page background, and swap in the fake onecontent.setPageBackgroundsVisible(false);revealView.setVisibility(View.VISIBLE);// We need to hide this view as the animation start will be posted.// alpha置为0revealView.setAlpha(0);int width = revealView.getMeasuredWidth();int height = revealView.getMeasuredHeight();float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);// 偏移量置为0revealView.setTranslationY(0);revealView.setTranslationX(0);// Get the y delta between the center of the page and the center of the all apps buttonint[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,getAllAppsButton(), null);float alpha = 0;float xDrift = 0;float yDrift = 0;if (material) {// sdk > 21 ?alpha = isWidgetTray ? 0.3f : 1f;yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];} else {yDrift = 2 * height / 3;xDrift = 0;}final float initAlpha = alpha;
    动画设置之前的一些初始化工作,将过渡页面的透明度、偏移量都先置0,然后设置动画时的透明度初始值和偏移量的初始值。
            revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);layerViews.add(revealView);PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);PropertyValuesHolder panelDriftY = PropertyValuesHolder.ofFloat("translationY", yDrift, 0);PropertyValuesHolder panelDriftX = PropertyValuesHolder.ofFloat("translationX", xDrift, 0);ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,panelAlpha, panelDriftY, panelDriftX);panelAlphaAndDrift.setDuration(revealDuration);panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));mStateAnimation.play(panelAlphaAndDrift);
    定义了动画的类型、时长和变化速率等。这是一个组合动画,很明显动画效果是透明度的变化和偏移量的变化。
            // 抽屉当前页的动画if (page != null) {page.setVisibility(View.VISIBLE);page.setLayerType(View.LAYER_TYPE_HARDWARE, null);layerViews.add(page);ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);page.setTranslationY(yDrift);pageDrift.setDuration(revealDuration);pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));pageDrift.setStartDelay(itemsAlphaStagger);mStateAnimation.play(pageDrift);page.setAlpha(0f);ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f);itemsAlpha.setDuration(revealDuration);itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));itemsAlpha.setStartDelay(itemsAlphaStagger);mStateAnimation.play(itemsAlpha);}
   这一段是抽屉当前页的动画效果,也是用属性动画来实现的,关于属性动画的使用可参考<<属性动画之ObjectAnimator>>。
   然后是页面指示器和sdk>21的动画,这个就不再细说了,到动画监听,
            mStateAnimation.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {dispatchOnLauncherTransitionEnd(fromView, animated, false);dispatchOnLauncherTransitionEnd(toView, animated, false);// 隐藏过渡页面revealView.setVisibility(View.INVISIBLE);revealView.setLayerType(View.LAYER_TYPE_NONE, null);if (page != null) {page.setLayerType(View.LAYER_TYPE_NONE, null);}// 显示抽屉content.setPageBackgroundsVisible(true);// Hide the search bar// 隐藏搜索栏if (mSearchDropTargetBar != null) {mSearchDropTargetBar.hideSearchBar(false);}// This can hold unnecessary references to views.mStateAnimation = null;}});
    动画结束后:隐藏过渡页面;显示抽屉内容;隐藏搜索栏。
            // Workspace动画效果if (workspaceAnim != null) {mStateAnimation.play(workspaceAnim);}
    这个是Workspace的动画,该动画定义在Workspace.java的getChangeStateAnimation方法中,该方法定义了多种情况下的动画效果,如Workspace到桌面缩略图、桌面缩略图到Workspace、Workspace到抽屉等等,进行alpha、scale等设置。
    最后定义一个runnable执行块,用于动画播放,
            final Runnable startAnimRunnable = new Runnable() {public void run() {// Check that mStateAnimation hasn't changed while// we waited for a layout/draw passif (mStateAnimation != stateAnimation)return;dispatchOnLauncherTransitionStart(fromView, animated, false);dispatchOnLauncherTransitionStart(toView, animated, false);revealView.setAlpha(initAlpha);if (Utilities.isLmpOrAbove()) {// sdk > 21 ?for (int i = 0; i < layerViews.size(); i++) {View v = layerViews.get(i);if (v != null) {if (Utilities.isViewAttachedToWindow(v)) v.buildLayer();}}}mStateAnimation.start();// 执行动画}};
    这样动画结束后,抽屉就显示出来,该隐藏的也隐藏了。如果是没有动画的情况,直接设为可见就行了,但会显得比较突兀,体验差了点。
    另外,在该方法中,多次调用了 dispatchOnLauncherTransitionXXX方法,最终调用View中实现了 LauncherTransitionable页面过渡接口的方法,在切换的不同阶段做相应的处理。
interface LauncherTransitionable {View getContent();void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);void onLauncherTransitionStep(Launcher l, float t);void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
}

三、自定义修改

1、如何更换抽屉背景?
    Launcher3中,抽屉内容的背景默认是白色的,如果想改成透明的,该怎么修改?
    一般情况下,我们首先想到的是在布局文件中找到AppsCustomizePagedView的布,然后将背景设为透明的,
            <com.android.launcher3.AppsCustomizePagedViewandroid:id="@+id/apps_customize_pane_content"android:layout_width="match_parent"android:layout_height="match_parent"launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"launcher:maxGap="@dimen/workspace_max_gap"launcher:pageIndicator="@+id/apps_customize_page_indicator" />
    这个方法显然是不能实现的,因为 AppsCustomizePagedView中还有一层 AppsCustomizeCellLayout ,一个列表页就是一个AppsCustomizeCellLayout,在<<Launcher3的加载流程>>中,有提到过对每一页的设置,直接找出这部分代码,
    launcher3\src\main\java\com\android\launcher3\AppsCustomizePagedView.java
    // 设置page的表格、背景色private void setupPage(AppsCustomizeCellLayout layout) {layout.setGridSize(mCellCountX, mCellCountY);// 设置页面表格数// Note: We force a measure here to get around the fact that when we do layout calculations// immediately after syncing, we don't have a proper width.  That said, we already know the// expected page width, so we can actually optimize by hiding all the TextView-based// children that are expensive to measure, and let that happen naturally later.setVisibilityOnChildren(layout, View.GONE);int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);layout.measure(widthSpec, heightSpec);// 设置page背景色Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);if (bg != null) {bg.setAlpha(mPageBackgroundsVisible ? 255: 0);layout.setBackground(bg);}setVisibilityOnChildren(layout, View.VISIBLE);}
    这里设置了 AppsCustomizeCellLayout 的背景色,我们将其设置透明背景,看能否达到效果。
        // 设置page背景色
//        Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);
//        if (bg != null) {
//            bg.setAlpha(mPageBackgroundsVisible ? 255: 0);
//            layout.setBackground(bg);
//        }layout.setBackgroundColor(Color.TRANSPARENT);

    这样就满足了效果,但是文字是灰色的有些不协调,我们改成白色的,这个在 syncAppsPageItems 方法中,做如下修改,
        for (int i = startIndex; i < endIndex; ++i) {// 循环添加itemsAppInfo info = mApps.get(i);BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(R.layout.apps_customize_application, layout, false);icon.applyFromApplicationInfo(info);icon.setOnClickListener(mLauncher);icon.setOnLongClickListener(this);icon.setOnTouchListener(this);icon.setOnKeyListener(this);icon.setOnFocusChangeListener(layout.mFocusHandlerView);icon.setTextColor(Color.WHITE); // modify text color.................................}


2、如何改变行和列数?
    可能已经注意到了,在布局文件中通过launcher:widgetCountX,launcher:widgetCountY来设置小部件没有显示数量,之所以可以这么设置,是因为在AppsCustomizePagedView中定义了这两个属性。
        // Save the default widget preview backgroundTypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
    但对于application而言,并没有定义类似的属性,那如何来改变行列数呢?首先得知道行和列是怎么得到的。mCellCountXmCellCountY这两个变量分别代表行数和列数,它们的值是怎么得到的呢?
    protected void onDataReady(int width, int height) {// Now that the data is ready, we can calculate the content width, the number of cells to// use for each pageLauncherAppState app = LauncherAppState.getInstance();DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();mCellCountX = (int) grid.allAppsNumCols;mCellCountY = (int) grid.allAppsNumRows;.....................................}
allAppsNumCols和allAppsNumRows相关,这两个值在DeviceProfile.java中定义的,
    private void updateIconSize(float scale, int drawablePadding, Resources resources,DisplayMetrics dm) {...................// All AppsallAppsCellWidthPx = allAppsIconSizePx;allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx;int maxLongEdgeCellCount =resources.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count);int maxShortEdgeCellCount =resources.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count);int minEdgeCellCount =resources.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count);int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) {allAppsNumRows = isLandscape ? allAppsShortEdgeCount : allAppsLongEdgeCount;allAppsNumCols = isLandscape ? allAppsLongEdgeCount : allAppsShortEdgeCount;} else {allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /(allAppsCellHeightPx + allAppsCellPaddingPx);allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));allAppsNumCols = (availableWidthPx) /(allAppsCellWidthPx + allAppsCellPaddingPx);allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));}}
    我们可以看到行列数并不是固定的,是根据配置的行列数、图标大小、表格间距等计算出来的。如果我们想增加行列数,可以把图标缩小、间距加大,反之可以减小行列数。
    Launcher3根据不同的型号的手机加载不同的配置项,launcher3\src\main\java\com\android\launcher3\DynamicGrid.java,
        deviceProfiles.add(new DeviceProfile("Nexus 4",335, 567,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4));deviceProfiles.add(new DeviceProfile("Nexus 5",359, 567,  4, 4,  DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4));
    我用的测试机是Nexus 5,但实际使用的配置却是上面那个,这个我们就不管了。一共有十个参数,分别表示:设备名、最小宽度Dps、最小高度Dps、行数、列数、图标大小、图标字体大小、固定热键数目(Hotseat)、固定热键图标大小、默认Workspace布局。
    我们先将四列改成五列,
        deviceProfiles.add(new DeviceProfile("Nexus 4",335, 567,  4, 5,  DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4));
    测试后好像没什么变化,我们把图标再改小点,默认60,改成48,
    static float DEFAULT_ICON_SIZE_DP = 48;

   我们可以看到变成5列了,但是也变成6行了,我们在把最大行数设为5,原来是6,launcher3\src\main\res\values\config.xml,
    <integer name="config_dynamic_grid_max_long_edge_cell_count">6</integer>

    这样就变成5行5列了,但是看上去不大协调,目前我的测试机还是适合5*4,这里我们只是了解下怎么修改。
    当然,除了背景、行列数可以改变外,我们也可以更改动画效果,这里就不在赘述了。


                                      结束


http://chatgpt.dhexx.cn/article/x8y6XcRW.shtml

相关文章

Android launcher3 开发初始篇

版本&#xff1a;1.0 日期&#xff1a;2014.8.26 2014.8.27 2014.11.10 版权&#xff1a;© 2014 kince 转载注明出处 好久没有写博客&#xff0c;也是因为工作比较忙的关系。当然这不是理由&#xff0c;主要是很多bug要改&#xff0c;而自己的效率又不是很高&#xff0c;…

安卓Launcher 简介

转载请注明出处&#xff1a;安卓Launcher 简介_安卓launcher是什么_Mr_Leixiansheng的博客-CSDN博客 文章概述&#xff1a; 1.什么是Launcher 2.新建一个Launcher工程 3.Apps去哪了 4.显示桌面背景 最近换了新工作(๑ㅁ)&#xff0c;又是要去接触新的知识了。闲话不多说&…

android12.0(S) Launcher3 细节修改

去除 Launcher3 底部类似 dockbar 条目 packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java availableHeightPx windowBounds.availableSize.y;mInfo info; - isTablet info.isTablet(windowBounds);//isTablet info.isTablet(windowBounds);is…

Launcher3

Android 4.4 (KK)开始Launcher默认使用Launcher3&#xff0c;Launcher3较Launcher2 UI 有部分调整&#xff0c;主要包括: &#xff08;1&#xff09; 状态栏透明&#xff0c;App List 透Wallpaper&#xff1b; &#xff08;2&#xff09; 增加overview模式&#xff0c;可以调整…

Launcher3 模块的简单设计

Launcher3 模块的简单设计 Lancher3 路劲&#xff1a; Z:\xxx\packages\apps\Launcher3 任务 1、AllApps背景透明化。 2、Allapps前3个图标变为Chrome、youtube、play商店。 3、长按桌面空白处在弹出的按钮下添加一个图标变大按钮&#xff0c;一个图标变小按钮&#xff0c;点…

android12.0(S) Launcher3 导入 AndroidStudio 调试编译

验证环境 aosp 12.0 源码&#xff0c;分支 android-12.0.0_r3 可以参考之前写的 android12.0(S) Pixel 3XL (QCOM 845) 编译刷机 AndroidStudio 版本 Android Studio Arctic Fox | 2020.3.1 Patch 4 gradle 版本 gradle-7.0.2-bin.zip gradle:7.0.4 二手 Pixel 3 XL一台可直…

launcher3的具体学习

目录结构&#xff1a; allapps 目录&#xff1a;主要存放主菜单界面相关代码。 anim目录&#xff1a;存放动画相关&#xff0c; badge目录&#xff1a;存放图标标识相关 compat目录&#xff1a;存放解决兼容性相关。 config目录&#xff1a;配置Launcher相关功能的宏开关 dragn…

Android10/11 原生Launcher3深度定制开发

一、引言 关于Android10和11系统Launcher3的定制有很多&#xff0c;根据项目的需求会进行各种定制开发&#xff0c; 于是就需要研究Launcher3的源码。本文主要从Android 11的Launcher3QuickStep着手 &#xff08;go版本或者其他版本类似&#xff09;从常用的修改进行分析&#…

Launcher3-桌面布局+主要的类+启动流程

一、launhcer3桌面布局二、launcher3主要的类LauncherModel&#xff1a;BubblTextView&#xff1a;DragController&#xff1a;LauncherAppState&#xff1a;DragView&#xff1a;DragSource&#xff0c;DropTarget&#xff1a;Folder&#xff1a;FolderIcon&#xff1a;Launch…

Launcher3--初识Launcher3

一、Launcher简介 Launcher时开机完成后第一个启动的应用&#xff0c;用来展示应用列表和快捷方式、小部件等。Launcher作为第一个(开机后第一个启动的应用)展示给用户的应用程序&#xff0c;其设计的好坏影响到用户的体验&#xff0c;甚至影响用户购机的判断。所以很多品牌厂商…

Android Launcher3分析及定制主题实现

一. Launcher3 简介 **launcher3是在Launcher2的基础上进化的版本,从Android 4.4 开始就使用Launcher3 .(kk版,kk2版)作为桌面使用,以前我们都在使用Launcher2,我们使用的是KK版本,具体区别后面再说. ** 1 Launcher3 桌面变成了动态管理,launcher2 里面默认最多加载五个worksp…

Android Launcher3简介

一.Launcher3概述 Launcher顾名思义&#xff0c;就是桌面的意思&#xff0c;也是android系统启动后第一个启动的应用程序&#xff0c;这里以android11为例&#xff0c;和其他应用并无区别&#xff0c;只是增加了对其他app和widget的管理窗口&#xff0c;且可以为用户定制化一些…

详细理解准确率、精准率、召回率,F1值等评价指标的含义

转载文章 原博客地址&#xff1a;详解准确率、精确率、召回率、F1值等评价指标的含义 机器学习问题之中&#xff0c;通常需要建立模型来解决具体问题&#xff0c;但对于模型的好坏&#xff0c;也就是模型的泛化能力&#xff0c;如何进行评估&#xff1f;我们可以定一些评价指标…

详解准确率、精确率、召回率、F1值等评价指标的含义

机器学习问题之中&#xff0c;通常需要建立模型来解决具体问题&#xff0c;但对于模型的好坏&#xff0c;也就是模型的泛化能力&#xff0c;如何进行评估呢&#xff1f; 很简单&#xff0c;我们可以定一些评价指标&#xff0c;来度量模型的优劣。比如准确率、精确率、召回率、…

分类性能评价指标——精确率,召回率,F1值详细解释

分类性能的评价指标 准确率 准确率是全部参与分类的文本中&#xff0c;与人工分类结果吻合的文本所占的比例。 即&#xff1a;预测与真实标签相同的比例 A c c u r a c y T P T N T P T N F P F N Accuracy\frac{TPTN}{TPTNFPFN} AccuracyTPTNFPFNTPTN​ 精确率 也称…

准确率、精确率、召回率、F1值

1.TP、TN、FP、FN 先粘一个官方形式的。 用新冠来举例理解。下方正方形为样本&#xff0c;其中 圆的部分认定为检测后是阳性的&#xff0c;其余部分为检测为阴性的&#xff08;但是现在的情况是检测并不完全准确&#xff0c;有可能检测时阴性&#xff0c;但实际上已经有新冠…

机器学习中的二分类问题评价指标之精确率、召回率、F1值通俗理解

引言&#xff1a;对于分类问题&#xff0c;我们在评估一个模型的好坏时&#xff0c;通常想到的是把该模型在测试集上分类结果正确的样本数量比上测试集的样本数量的比值结果&#xff0c;即准确率&#xff08;精确率&#xff09;作为评价准则。但除此之外&#xff0c;还有精确率…

【转】一些因素对F1值的影响

截自&#xff1a;https://blog.csdn.net/qq_27590277/article/details/88374695 https://blog.csdn.net/qq_27590277/article/details/88367082 一些因素对F1值的影响 如果还没了解F1值的话&#xff0c;这里有我之前写的通俗易懂的文章 详谈P(查准率)&#xff0c;R(查全率)&…

keras计算precision、recall、F1值

近期写课程作业&#xff0c;需要用Keras搭建网络层&#xff0c;跑实验时需要计算precision&#xff0c;recall和F1值&#xff0c;在前几年&#xff0c;Keras没有更新时&#xff0c;我用的代码是直接取训练期间的预测标签&#xff0c;然后和真实标签之间计算求解&#xff0c;代码…

F1值(F-Measure)、准确率(Precision)、召回率(Recall) 菜鸡版理解

前置知识&#xff1a; T&#xff08;True&#xff09;&#xff1a; 正确的 F (False) &#xff1a; 错误的 P (Positive) : 正向的/积极的 N (Negetive): 负向的/消极的 则&#xff1a; TP&#xff1a;正确的 预测了 正向的 FN&#xff1a;错误的 预测了 负向的 FP&#xff1a…